diff --git a/dist/hls2mp4.cjs b/dist/hls2mp4.cjs index 106c66f..b832913 100644 --- a/dist/hls2mp4.cjs +++ b/dist/hls2mp4.cjs @@ -914,7 +914,7 @@ function parseM3u8File(url, customFetch) { case 1: playList = _a.sent(); return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, ffmpeg.fetchFile(url).then(function (data) { return new Blob([data.buffer]).text(); })]; + case 2: return [4 /*yield*/, ffmpeg.fetchFile(url).then(function (data) { return aesjs.utils.utf8.fromBytes(data); })]; case 3: playList = _a.sent(); _a.label = 4; @@ -957,7 +957,11 @@ var Hls2Mp4 = /** @class */ (function () { return buffer.slice(bufferOffset); }; Hls2Mp4.prototype.hexToUint8Array = function (hex) { - return new Uint8Array(hex.replace(/^0x/, '').match(/[\da-f]{2}/gi).map(function (hx) { return parseInt(hx, 16); })); + var matchedChars = hex.replace(/^0x/, '').match(/[\da-f]{2}/gi); + if (matchedChars) { + return new Uint8Array(matchedChars.map(function (hx) { return parseInt(hx, 16); })); + } + return new Uint8Array(0); }; Hls2Mp4.prototype.aesDecrypt = function (buffer, keyBuffer, iv) { var ivData; @@ -982,7 +986,7 @@ var Hls2Mp4 = /** @class */ (function () { (_b = this.onProgress) === null || _b === void 0 ? void 0 : _b.call(this, exports.TaskType.parseM3u8, 1); return [2 /*return*/, data]; } - return [2 /*return*/]; + throw new Error('m3u8 load failed'); } }); }); @@ -1037,13 +1041,14 @@ var Hls2Mp4 = /** @class */ (function () { }; Hls2Mp4.prototype.downloadM3u8 = function (url) { return __awaiter(this, void 0, void 0, function () { - var _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; + var m3u8Parsed, _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; var e_1, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, this.parseM3u8(url)]; case 1: - _a = _c.sent(), content = _a.content, parsedUrl = _a.url; + m3u8Parsed = _c.sent(); + _a = m3u8Parsed, content = _a.content, parsedUrl = _a.url; keyMatchRegExp = createFileUrlRegExp('key', 'gi'); keyTagMatchRegExp = new RegExp('#EXT-X-KEY:METHOD=(AES-128|NONE)(,URI="' + keyMatchRegExp.source + '"(,IV=\\w+)?)?', 'gi'); matchReg = new RegExp(keyTagMatchRegExp.source + '|' + createFileUrlRegExp('ts', 'gi').source, 'g'); @@ -1189,7 +1194,7 @@ var Hls2Mp4 = /** @class */ (function () { } return [2 /*return*/, { done: false, - data: null + data: undefined }]; case 3: return [2 /*return*/]; } @@ -1251,7 +1256,7 @@ var Hls2Mp4 = /** @class */ (function () { anchor.click(); setTimeout(function () { return URL.revokeObjectURL(objectUrl); }, 100); }; - Hls2Mp4.version = '1.1.5'; + Hls2Mp4.version = '1.1.7'; return Hls2Mp4; }()); diff --git a/dist/hls2mp4.js b/dist/hls2mp4.js index f6b9bbe..12add18 100644 --- a/dist/hls2mp4.js +++ b/dist/hls2mp4.js @@ -911,7 +911,7 @@ case 1: playList = _a.sent(); return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, ffmpeg.fetchFile(url).then(function (data) { return new Blob([data.buffer]).text(); })]; + case 2: return [4 /*yield*/, ffmpeg.fetchFile(url).then(function (data) { return aesjs.utils.utf8.fromBytes(data); })]; case 3: playList = _a.sent(); _a.label = 4; @@ -954,7 +954,11 @@ return buffer.slice(bufferOffset); }; Hls2Mp4.prototype.hexToUint8Array = function (hex) { - return new Uint8Array(hex.replace(/^0x/, '').match(/[\da-f]{2}/gi).map(function (hx) { return parseInt(hx, 16); })); + var matchedChars = hex.replace(/^0x/, '').match(/[\da-f]{2}/gi); + if (matchedChars) { + return new Uint8Array(matchedChars.map(function (hx) { return parseInt(hx, 16); })); + } + return new Uint8Array(0); }; Hls2Mp4.prototype.aesDecrypt = function (buffer, keyBuffer, iv) { var ivData; @@ -979,7 +983,7 @@ (_b = this.onProgress) === null || _b === void 0 ? void 0 : _b.call(this, exports.TaskType.parseM3u8, 1); return [2 /*return*/, data]; } - return [2 /*return*/]; + throw new Error('m3u8 load failed'); } }); }); @@ -1034,13 +1038,14 @@ }; Hls2Mp4.prototype.downloadM3u8 = function (url) { return __awaiter(this, void 0, void 0, function () { - var _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; + var m3u8Parsed, _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; var e_1, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, this.parseM3u8(url)]; case 1: - _a = _c.sent(), content = _a.content, parsedUrl = _a.url; + m3u8Parsed = _c.sent(); + _a = m3u8Parsed, content = _a.content, parsedUrl = _a.url; keyMatchRegExp = createFileUrlRegExp('key', 'gi'); keyTagMatchRegExp = new RegExp('#EXT-X-KEY:METHOD=(AES-128|NONE)(,URI="' + keyMatchRegExp.source + '"(,IV=\\w+)?)?', 'gi'); matchReg = new RegExp(keyTagMatchRegExp.source + '|' + createFileUrlRegExp('ts', 'gi').source, 'g'); @@ -1186,7 +1191,7 @@ } return [2 /*return*/, { done: false, - data: null + data: undefined }]; case 3: return [2 /*return*/]; } @@ -1248,7 +1253,7 @@ anchor.click(); setTimeout(function () { return URL.revokeObjectURL(objectUrl); }, 100); }; - Hls2Mp4.version = '1.1.5'; + Hls2Mp4.version = '1.1.7'; return Hls2Mp4; }()); diff --git a/dist/hls2mp4.umd.js b/dist/hls2mp4.umd.js index 710a721..ae71d23 100644 --- a/dist/hls2mp4.umd.js +++ b/dist/hls2mp4.umd.js @@ -914,7 +914,7 @@ case 1: playList = _a.sent(); return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, ffmpeg.fetchFile(url).then(function (data) { return new Blob([data.buffer]).text(); })]; + case 2: return [4 /*yield*/, ffmpeg.fetchFile(url).then(function (data) { return aesjs.utils.utf8.fromBytes(data); })]; case 3: playList = _a.sent(); _a.label = 4; @@ -957,7 +957,11 @@ return buffer.slice(bufferOffset); }; Hls2Mp4.prototype.hexToUint8Array = function (hex) { - return new Uint8Array(hex.replace(/^0x/, '').match(/[\da-f]{2}/gi).map(function (hx) { return parseInt(hx, 16); })); + var matchedChars = hex.replace(/^0x/, '').match(/[\da-f]{2}/gi); + if (matchedChars) { + return new Uint8Array(matchedChars.map(function (hx) { return parseInt(hx, 16); })); + } + return new Uint8Array(0); }; Hls2Mp4.prototype.aesDecrypt = function (buffer, keyBuffer, iv) { var ivData; @@ -982,7 +986,7 @@ (_b = this.onProgress) === null || _b === void 0 ? void 0 : _b.call(this, exports.TaskType.parseM3u8, 1); return [2 /*return*/, data]; } - return [2 /*return*/]; + throw new Error('m3u8 load failed'); } }); }); @@ -1037,13 +1041,14 @@ }; Hls2Mp4.prototype.downloadM3u8 = function (url) { return __awaiter(this, void 0, void 0, function () { - var _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; + var m3u8Parsed, _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; var e_1, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, this.parseM3u8(url)]; case 1: - _a = _c.sent(), content = _a.content, parsedUrl = _a.url; + m3u8Parsed = _c.sent(); + _a = m3u8Parsed, content = _a.content, parsedUrl = _a.url; keyMatchRegExp = createFileUrlRegExp('key', 'gi'); keyTagMatchRegExp = new RegExp('#EXT-X-KEY:METHOD=(AES-128|NONE)(,URI="' + keyMatchRegExp.source + '"(,IV=\\w+)?)?', 'gi'); matchReg = new RegExp(keyTagMatchRegExp.source + '|' + createFileUrlRegExp('ts', 'gi').source, 'g'); @@ -1189,7 +1194,7 @@ } return [2 /*return*/, { done: false, - data: null + data: undefined }]; case 3: return [2 /*return*/]; } @@ -1251,7 +1256,7 @@ anchor.click(); setTimeout(function () { return URL.revokeObjectURL(objectUrl); }, 100); }; - Hls2Mp4.version = '1.1.5'; + Hls2Mp4.version = '1.1.7'; return Hls2Mp4; }()); diff --git a/dist/index.js b/dist/index.js index d15d380..0ff2135 100644 --- a/dist/index.js +++ b/dist/index.js @@ -910,7 +910,7 @@ function parseM3u8File(url, customFetch) { case 1: playList = _a.sent(); return [3 /*break*/, 4]; - case 2: return [4 /*yield*/, fetchFile(url).then(function (data) { return new Blob([data.buffer]).text(); })]; + case 2: return [4 /*yield*/, fetchFile(url).then(function (data) { return aesjs.utils.utf8.fromBytes(data); })]; case 3: playList = _a.sent(); _a.label = 4; @@ -953,7 +953,11 @@ var Hls2Mp4 = /** @class */ (function () { return buffer.slice(bufferOffset); }; Hls2Mp4.prototype.hexToUint8Array = function (hex) { - return new Uint8Array(hex.replace(/^0x/, '').match(/[\da-f]{2}/gi).map(function (hx) { return parseInt(hx, 16); })); + var matchedChars = hex.replace(/^0x/, '').match(/[\da-f]{2}/gi); + if (matchedChars) { + return new Uint8Array(matchedChars.map(function (hx) { return parseInt(hx, 16); })); + } + return new Uint8Array(0); }; Hls2Mp4.prototype.aesDecrypt = function (buffer, keyBuffer, iv) { var ivData; @@ -978,7 +982,7 @@ var Hls2Mp4 = /** @class */ (function () { (_b = this.onProgress) === null || _b === void 0 ? void 0 : _b.call(this, TaskType.parseM3u8, 1); return [2 /*return*/, data]; } - return [2 /*return*/]; + throw new Error('m3u8 load failed'); } }); }); @@ -1033,13 +1037,14 @@ var Hls2Mp4 = /** @class */ (function () { }; Hls2Mp4.prototype.downloadM3u8 = function (url) { return __awaiter(this, void 0, void 0, function () { - var _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; + var m3u8Parsed, _a, content, parsedUrl, keyMatchRegExp, keyTagMatchRegExp, matchReg, matches, segments, i, matched, matchedKey, matchedIV, batch, treatedSegments, segments_1, segments_1_1, group, total, keyBuffer, keyUrl, _loop_1, this_1, i, e_1_1, m3u8; var e_1, _b; return __generator(this, function (_c) { switch (_c.label) { case 0: return [4 /*yield*/, this.parseM3u8(url)]; case 1: - _a = _c.sent(), content = _a.content, parsedUrl = _a.url; + m3u8Parsed = _c.sent(); + _a = m3u8Parsed, content = _a.content, parsedUrl = _a.url; keyMatchRegExp = createFileUrlRegExp('key', 'gi'); keyTagMatchRegExp = new RegExp('#EXT-X-KEY:METHOD=(AES-128|NONE)(,URI="' + keyMatchRegExp.source + '"(,IV=\\w+)?)?', 'gi'); matchReg = new RegExp(keyTagMatchRegExp.source + '|' + createFileUrlRegExp('ts', 'gi').source, 'g'); @@ -1185,7 +1190,7 @@ var Hls2Mp4 = /** @class */ (function () { } return [2 /*return*/, { done: false, - data: null + data: undefined }]; case 3: return [2 /*return*/]; } @@ -1247,7 +1252,7 @@ var Hls2Mp4 = /** @class */ (function () { anchor.click(); setTimeout(function () { return URL.revokeObjectURL(objectUrl); }, 100); }; - Hls2Mp4.version = '1.1.5'; + Hls2Mp4.version = '1.1.7'; return Hls2Mp4; }()); diff --git a/package.json b/package.json index 0afb0f1..ac6b591 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@rollup/plugin-node-resolve": "^15.0.2", "@rollup/plugin-commonjs": "^25.0.0", "@rollup/plugin-typescript": "^11.1.0", + "@types/aes-js": "^3.1.1", "rollup": "^3.21.4", "tslib": "^2.0.0", "typescript": "^4.7.4" diff --git a/src/index.ts b/src/index.ts index f26cf74..fdf9e13 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import { createFFmpeg, fetchFile, CreateFFmpegOptions, FFmpeg } from '@ffmpeg/ffmpeg'; -import aesjs from 'aes-js'; +import aesjs, { type ByteSource } from 'aes-js'; export enum TaskType { loadFFmeg = 0, @@ -14,7 +14,7 @@ interface ProgressCallback { type LoadResult = { done: boolean; - data: T; + data?: T; msg?: string; } @@ -64,7 +64,7 @@ export async function parseM3u8File(url: string, customFetch?: (url: string) => } else { playList = await fetchFile(url).then( - data => new Blob([data.buffer]).text() + data => aesjs.utils.utf8.fromBytes(data) ) } const matchedM3u8 = playList.match( @@ -89,7 +89,7 @@ export default class Hls2Mp4 { private tsDownloadConcurrency: number; private totalSegments = 0; private savedSegments = 0; - public static version = '1.1.5' + public static version = '1.1.7' constructor({ maxRetry = 3, tsDownloadConcurrency = 10, ...options }: CreateFFmpegOptions & Hls2Mp4Options, onProgress?: ProgressCallback) { this.instance = createFFmpeg(options); @@ -114,18 +114,23 @@ export default class Hls2Mp4 { } private hexToUint8Array(hex: string) { - return new Uint8Array( - hex.replace(/^0x/, '').match( - /[\da-f]{2}/gi - ).map(hx => parseInt(hx, 16))) + const matchedChars = hex.replace(/^0x/, '').match( + /[\da-f]{2}/gi + ); + if (matchedChars) { + return new Uint8Array( + matchedChars.map(hx => parseInt(hx, 16)) + ) + } + return new Uint8Array(0); } private aesDecrypt(buffer: Uint8Array, keyBuffer: Uint8Array, iv?: string) { - let ivData: Uint8Array; + let ivData: ByteSource; if (iv) { ivData = iv.startsWith('0x') ? this.hexToUint8Array(iv) : aesjs.utils.utf8.toBytes(iv) } - const aesCbc = new aesjs.ModeOfOperation.cbc(keyBuffer, ivData); + const aesCbc = new aesjs.ModeOfOperation.cbc(keyBuffer, ivData!); return aesCbc.decrypt(buffer); } @@ -138,7 +143,7 @@ export default class Hls2Mp4 { this.onProgress?.(TaskType.parseM3u8, 1) return data; } - new Error('m3u8 load failed') + throw new Error('m3u8 load failed') } private async downloadFile(url: string) { @@ -156,7 +161,7 @@ export default class Hls2Mp4 { return Promise.all( segs.map(async ({ name, url, source }) => { const tsData = await this.downloadFile(url) - const buffer = key ? this.aesDecrypt(tsData, key, iv) : this.transformBuffer(tsData) + const buffer = key ? this.aesDecrypt(tsData!, key, iv) : this.transformBuffer(tsData!) this.instance.FS('writeFile', name, buffer) this.savedSegments += 1 this.onProgress?.(TaskType.downloadTs, this.savedSegments / this.totalSegments) @@ -170,7 +175,8 @@ export default class Hls2Mp4 { } private async downloadM3u8(url: string) { - let { content, url: parsedUrl } = await this.parseM3u8(url) + const m3u8Parsed = await this.parseM3u8(url) + let { content, url: parsedUrl } = m3u8Parsed!; const keyMatchRegExp = createFileUrlRegExp('key', 'gi'); const keyTagMatchRegExp = new RegExp( '#EXT-X-KEY:METHOD=(AES-128|NONE)(,URI="' + keyMatchRegExp.source + '"(,IV=\\w+)?)?', @@ -215,7 +221,7 @@ export default class Hls2Mp4 { for (const group of segments) { const total = group.segments.length; - let keyBuffer: Uint8Array; + let keyBuffer: Uint8Array | undefined; if (group.key) { const keyUrl = parseUrl(parsedUrl, group.key) @@ -270,7 +276,7 @@ export default class Hls2Mp4 { } return { done: false, - data: null + data: undefined } } } diff --git a/tsconfig.json b/tsconfig.json index 0d56441..0056f02 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,7 @@ "outDir": "dist", "allowJs": true, "skipLibCheck": true, - "strict": false, + "strict": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, diff --git a/yarn.lock b/yarn.lock index 499b68b..15a8832 100644 --- a/yarn.lock +++ b/yarn.lock @@ -63,6 +63,11 @@ estree-walker "^2.0.2" picomatch "^2.3.1" +"@types/aes-js@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/aes-js/-/aes-js-3.1.1.tgz#34b3978122310c135de4b377270d1d65676fae28" + integrity sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw== + "@types/estree@*", "@types/estree@^1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194"