@lzwme/m3u8-dl 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/m3u8dl.js CHANGED
File without changes
package/cjs/cli.js CHANGED
@@ -29,7 +29,7 @@ commander_1.program
29
29
  .option('-p, --play', `是否边下边看`)
30
30
  .option('-C, --cache-dir <dirpath>', `临时文件保存目录。默认为 cache`)
31
31
  .option('-S, --save-dir <dirpath>', `下载文件保存的路径。默认为当前目录`)
32
- .option('--no-del-cache', `下载成功后是否删除临时文件。默认为 true。保存临时文件可以在重复下载时识别缓存`, true)
32
+ .option('--no-del-cache', `下载成功后是否删除临时文件。默认为 true。保存临时文件可以在重复下载时识别缓存`)
33
33
  .action(async (urls) => {
34
34
  const options = getOptions();
35
35
  utils_js_1.logger.debug(urls, options);
@@ -48,7 +48,7 @@ commander_1.program
48
48
  .option('-R,--remote-config-url <url>', '自定义远程配置加载地址。默认从主仓库配置读取')
49
49
  .description('m3u8视频在线搜索与下载')
50
50
  .action(async (keyword, options) => {
51
- (0, video_search_js_1.VideoSerachAndDL)(keyword, options, getOptions());
51
+ await (0, video_search_js_1.VideoSerachAndDL)(keyword, options, getOptions());
52
52
  });
53
53
  commander_1.program.parse(process.argv);
54
54
  function getOptions() {
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { M3u8DLOptions, TsItemInfo } from '../types/m3u8';
3
2
  /**
4
3
  * 边下边看
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toLocalM3u8 = exports.localPlay = void 0;
3
+ exports.localPlay = localPlay;
4
+ exports.toLocalM3u8 = toLocalM3u8;
4
5
  const fe_utils_1 = require("@lzwme/fe-utils");
5
6
  const console_log_colors_1 = require("console-log-colors");
6
7
  const node_fs_1 = require("node:fs");
@@ -14,15 +15,15 @@ async function localPlay(m3u8Info, options) {
14
15
  if (!m3u8Info?.length)
15
16
  return null;
16
17
  const cacheDir = (0, node_path_1.dirname)(m3u8Info[0].tsOut);
17
- const info = await createLocalServer(cacheDir);
18
+ const cacheDirname = (0, node_path_1.basename)(cacheDir);
19
+ const info = await createLocalServer((0, node_path_1.dirname)(cacheDir));
18
20
  const filename = (0, node_path_1.basename)(options.filename).slice(0, options.filename.lastIndexOf('.')) + `.m3u8`;
19
- await toLocalM3u8(m3u8Info, (0, node_path_1.resolve)(cacheDir, filename), info.origin);
20
- const playUrl = `https://lzw.me/x/m3u8-player?url=${encodeURIComponent(`${info.origin}/${filename}`)}`;
21
+ await toLocalM3u8(m3u8Info, (0, node_path_1.resolve)(cacheDir, filename), `/${cacheDirname}`);
22
+ const playUrl = `https://lzw.me/x/m3u8-player?url=${encodeURIComponent(`${info.origin}/${cacheDirname}/${filename}`)}`;
21
23
  const cmd = `${process.platform === 'win32' ? 'start' : 'open'} ${playUrl}`;
22
24
  (0, fe_utils_1.execSync)(cmd);
23
25
  return info;
24
26
  }
25
- exports.localPlay = localPlay;
26
27
  async function toLocalM3u8(m3u8Info, filepath, host = '') {
27
28
  const m3u8ContentList = [
28
29
  `#EXTM3U`,
@@ -40,8 +41,8 @@ async function toLocalM3u8(m3u8Info, filepath, host = '') {
40
41
  const m3u8Content = m3u8ContentList.join('\n');
41
42
  await node_fs_1.promises.writeFile(filepath, m3u8Content, 'utf8');
42
43
  }
43
- exports.toLocalM3u8 = toLocalM3u8;
44
44
  async function createLocalServer(baseDir) {
45
+ baseDir = (0, node_path_1.resolve)(baseDir);
45
46
  const port = await (0, fe_utils_1.findFreePort)();
46
47
  const origin = `http://localhost:${port}`;
47
48
  const server = (0, node_http_1.createServer)((req, res) => {
@@ -64,6 +65,15 @@ async function createLocalServer(baseDir) {
64
65
  (0, node_fs_1.createReadStream)(filename).pipe(res);
65
66
  return;
66
67
  }
68
+ else if (stats.isDirectory()) {
69
+ const html = (0, node_fs_1.readdirSync)(filename).map(filepath => {
70
+ const rpath = (0, node_path_1.resolve)(filename, filepath).replace(baseDir, '');
71
+ return `<li><a href="${rpath}">${rpath}</a></li>`;
72
+ });
73
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
74
+ res.end(`<ol>${html.join('')}</ol>`);
75
+ return;
76
+ }
67
77
  }
68
78
  res.writeHead(404, { 'Content-Type': 'text/plain; charset=utf-8' });
69
79
  res.end('Not found');
@@ -1,12 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.m3u8Convert = void 0;
3
+ exports.m3u8Convert = m3u8Convert;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const fe_utils_1 = require("@lzwme/fe-utils");
7
7
  const console_log_colors_1 = require("console-log-colors");
8
8
  const utils_1 = require("./utils");
9
- const helper_1 = require("@lzwme/fe-utils/cjs/common/helper");
10
9
  async function m3u8Convert(options, data) {
11
10
  let ffmpegSupport = (0, utils_1.isSupportFfmpeg)();
12
11
  let filepath = (0, node_path_1.resolve)(options.saveDir, options.filename);
@@ -21,7 +20,13 @@ async function m3u8Convert(options, data) {
21
20
  if (process.platform === 'win32')
22
21
  filesAllArr = filesAllArr.map(d => d.replaceAll('\\', '/'));
23
22
  await node_fs_1.promises.writeFile(inputFilePath, 'ffconcat version 1.0\nfile ' + filesAllArr.join('\nfile '));
24
- const cmd = `ffmpeg -y -f concat -safe 0 -i ${inputFilePath} -acodec copy -vcodec copy -absf aac_adtstoasc "${filepath}"`;
23
+ let headersString = '';
24
+ if (options.headers) {
25
+ for (const [key, value] of Object.entries(options.headers)) {
26
+ headersString += `-headers "${key}: ${String(value)}" `;
27
+ }
28
+ }
29
+ const cmd = `ffmpeg -y -f concat -safe 0 -i ${inputFilePath} -acodec copy -vcodec copy -bsf:a aac_adtstoasc ${headersString} "${filepath}"`;
25
30
  utils_1.logger.debug('[convert to mp4]cmd:', (0, console_log_colors_1.cyan)(cmd));
26
31
  const r = (0, fe_utils_1.execSync)(cmd);
27
32
  ffmpegSupport = !r.error;
@@ -34,7 +39,6 @@ async function m3u8Convert(options, data) {
34
39
  }
35
40
  if (!(0, node_fs_1.existsSync)(filepath))
36
41
  return '';
37
- utils_1.logger.info(`File saved[${(0, console_log_colors_1.magentaBright)((0, helper_1.formatByteSize)((0, node_fs_1.statSync)(filepath).size))}]:`, (0, console_log_colors_1.greenBright)(filepath));
42
+ utils_1.logger.info(`File saved[${(0, console_log_colors_1.magentaBright)((0, fe_utils_1.formatByteSize)((0, node_fs_1.statSync)(filepath).size))}]:`, (0, console_log_colors_1.greenBright)(filepath));
38
43
  return filepath;
39
44
  }
40
- exports.m3u8Convert = m3u8Convert;
@@ -1,4 +1,5 @@
1
1
  import { WorkerPool } from './worker_pool';
2
+ import { parseM3U8 } from './parseM3u8';
2
3
  import type { M3u8DLOptions, TsItemInfo, WorkerTaskInfo } from '../types/m3u8';
3
4
  export declare const workPoll: WorkerPool<WorkerTaskInfo, {
4
5
  success: boolean;
@@ -7,12 +8,6 @@ export declare const workPoll: WorkerPool<WorkerTaskInfo, {
7
8
  export declare function preDownLoad(url: string, options: M3u8DLOptions): Promise<void>;
8
9
  export declare function m3u8Download(url: string, options?: M3u8DLOptions): Promise<{
9
10
  options: M3u8DLOptions;
10
- m3u8Info: {
11
- manifest: any;
12
- tsCount: number;
13
- durationSecond: number;
14
- data: TsItemInfo[];
15
- crypto: import("../types/m3u8").M3u8Crypto;
16
- };
11
+ m3u8Info: Awaited<ReturnType<typeof parseM3U8>> | null;
17
12
  filepath: string;
18
13
  }>;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.m3u8Download = exports.preDownLoad = exports.workPoll = void 0;
3
+ exports.workPoll = void 0;
4
+ exports.preDownLoad = preDownLoad;
5
+ exports.m3u8Download = m3u8Download;
4
6
  const node_path_1 = require("node:path");
5
7
  const node_fs_1 = require("node:fs");
6
8
  const node_os_1 = require("node:os");
@@ -18,7 +20,7 @@ const cache = {
18
20
  };
19
21
  const tsDlFile = (0, node_path_1.resolve)(__dirname, './ts-download.js');
20
22
  exports.workPoll = new worker_pool_1.WorkerPool(tsDlFile);
21
- async function formatOptions(url, opts) {
23
+ function formatOptions(url, opts) {
22
24
  const options = {
23
25
  delCache: !opts.debug,
24
26
  saveDir: process.cwd(),
@@ -54,7 +56,7 @@ async function formatOptions(url, opts) {
54
56
  return [url, options];
55
57
  }
56
58
  async function m3u8InfoParse(url, options = {}) {
57
- [url, options] = await formatOptions(url, options);
59
+ [url, options] = formatOptions(url, options);
58
60
  const ext = (0, utils_1.isSupportFfmpeg)() ? '.mp4' : '.ts';
59
61
  let filepath = (0, node_path_1.resolve)(options.saveDir, options.filename);
60
62
  if (!filepath.endsWith(ext))
@@ -86,7 +88,6 @@ async function preDownLoad(url, options) {
86
88
  }
87
89
  }
88
90
  }
89
- exports.preDownLoad = preDownLoad;
90
91
  async function m3u8Download(url, options = {}) {
91
92
  utils_1.logger.info('Starting download for', (0, console_log_colors_1.cyanBright)(url));
92
93
  const result = await m3u8InfoParse(url, options);
@@ -181,4 +182,3 @@ async function m3u8Download(url, options = {}) {
181
182
  utils_1.logger.debug('Done!', url, result.m3u8Info);
182
183
  return result;
183
184
  }
184
- exports.m3u8Download = m3u8Download;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseM3U8 = void 0;
3
+ exports.parseM3U8 = parseM3U8;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const m3u8_parser_1 = require("m3u8-parser");
@@ -54,8 +54,9 @@ async function parseM3U8(content, url = process.cwd(), cacheDir = './cache') {
54
54
  if (tsKeyInfo?.uri) {
55
55
  if (tsKeyInfo.method)
56
56
  result.crypto.method = tsKeyInfo.method.toUpperCase();
57
- if (tsKeyInfo.iv)
58
- result.crypto.iv = new Uint8Array(Buffer.from(tsKeyInfo.iv));
57
+ if (tsKeyInfo.iv) {
58
+ result.crypto.iv = typeof tsKeyInfo.iv === 'string' ? new Uint8Array(Buffer.from(tsKeyInfo.iv)) : tsKeyInfo.iv;
59
+ }
59
60
  result.crypto.uri = tsKeyInfo.uri.includes('://') ? tsKeyInfo.uri : new URL(tsKeyInfo.uri, url).toString();
60
61
  }
61
62
  if (result.crypto.uri !== '') {
@@ -77,5 +78,4 @@ async function parseM3U8(content, url = process.cwd(), cacheDir = './cache') {
77
78
  result.durationSecond = +Number(result.durationSecond).toFixed(2);
78
79
  return result;
79
80
  }
80
- exports.parseM3U8 = parseM3U8;
81
81
  // parseM3U8('', 't.m3u8').then(d => console.log(d));
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.tsDownload = void 0;
3
+ exports.tsDownload = tsDownload;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const node_fs_1 = require("node:fs");
6
6
  const node_path_1 = require("node:path");
@@ -27,10 +27,15 @@ async function tsDownload(info, cryptoInfo) {
27
27
  }
28
28
  return false;
29
29
  }
30
- exports.tsDownload = tsDownload;
31
30
  function aesDecrypt(data, cryptoInfo) {
32
- const decipher = (0, node_crypto_1.createDecipheriv)((cryptoInfo.method + '-cbc').toLocaleLowerCase(), cryptoInfo.key, cryptoInfo.iv);
33
- return Buffer.concat([decipher.update(Buffer.isBuffer(data) ? data : Buffer.from(data)), decipher.final()]);
31
+ try {
32
+ const decipher = (0, node_crypto_1.createDecipheriv)((cryptoInfo.method + '-cbc').toLocaleLowerCase(), cryptoInfo.key, cryptoInfo.iv);
33
+ return Buffer.concat([decipher.update(Buffer.isBuffer(data) ? data : Buffer.from(data)), decipher.final()]);
34
+ }
35
+ catch (err) {
36
+ console.log('aesDecrypt err:', err);
37
+ throw err;
38
+ }
34
39
  }
35
40
  if (!node_worker_threads_1.isMainThread && node_worker_threads_1.parentPort) {
36
41
  node_worker_threads_1.parentPort.on('message', (data) => {
@@ -1,5 +1,3 @@
1
- /// <reference types="node" />
2
- /// <reference types="node" />
3
1
  import { NLogger, Request } from '@lzwme/fe-utils';
4
2
  export declare const request: Request;
5
3
  export declare const getRetry: <T = string>(url: string, retries?: number) => Promise<{
package/cjs/lib/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isSupportFfmpeg = exports.logger = exports.getRetry = exports.request = void 0;
3
+ exports.logger = exports.getRetry = exports.request = void 0;
4
+ exports.isSupportFfmpeg = isSupportFfmpeg;
4
5
  const fe_utils_1 = require("@lzwme/fe-utils");
5
6
  exports.request = new fe_utils_1.Request('', {
6
7
  'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
@@ -22,4 +23,3 @@ function isSupportFfmpeg() {
22
23
  _isSupportFfmpeg = (0, fe_utils_1.execSync)('ffmpeg -version').stderr === '';
23
24
  return _isSupportFfmpeg;
24
25
  }
25
- exports.isSupportFfmpeg = isSupportFfmpeg;
@@ -25,13 +25,13 @@ export declare class VideoSearch {
25
25
  apiMap: Map<string, {
26
26
  url: string;
27
27
  desc?: string;
28
- enable?: boolean | 0 | 1; /** 播放地址缓存 */
28
+ enable?: 0 | 1 | boolean;
29
29
  remote?: boolean;
30
30
  }>;
31
31
  get api(): {
32
32
  url: string;
33
33
  desc?: string;
34
- enable?: boolean | 0 | 1; /** 播放地址缓存 */
34
+ enable?: 0 | 1 | boolean;
35
35
  remote?: boolean;
36
36
  }[];
37
37
  constructor(options?: VSOptions);
@@ -39,13 +39,13 @@ export declare class VideoSearch {
39
39
  search(wd: string, api?: {
40
40
  url: string;
41
41
  desc?: string;
42
- enable?: boolean | 0 | 1; /** 播放地址缓存 */
42
+ enable?: 0 | 1 | boolean;
43
43
  remote?: boolean;
44
44
  }): Promise<VideoSearchResult>;
45
45
  getVideoList(ids: number | string | (number | string)[], api?: {
46
46
  url: string;
47
47
  desc?: string;
48
- enable?: boolean | 0 | 1; /** 播放地址缓存 */
48
+ enable?: 0 | 1 | boolean;
49
49
  remote?: boolean;
50
50
  }): Promise<VideoListResult>;
51
51
  private formatUrl;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.VideoSerachAndDL = exports.VideoSearch = void 0;
3
+ exports.VideoSearch = void 0;
4
+ exports.VideoSerachAndDL = VideoSerachAndDL;
4
5
  const fe_utils_1 = require("@lzwme/fe-utils");
5
6
  const storage_js_1 = require("./storage.js");
6
7
  const utils_js_1 = require("./utils.js");
@@ -41,7 +42,7 @@ class VideoSearch {
41
42
  async updateOptions(options) {
42
43
  const cache = storage_js_1.stor.get();
43
44
  const apis = [...(cache.api || []), ...options.api];
44
- await this.formatUrl(apis);
45
+ this.formatUrl(apis);
45
46
  if (options.api?.length)
46
47
  storage_js_1.stor.set({ api: apis });
47
48
  (cache.api || []).forEach(url => {
@@ -258,4 +259,3 @@ async function VideoSerachAndDL(keyword, options, baseOpts) {
258
259
  return VideoSerachAndDL(keyword, options, baseOpts);
259
260
  }
260
261
  }
261
- exports.VideoSerachAndDL = VideoSerachAndDL;
@@ -1,4 +1,3 @@
1
- /// <reference types="node" />
2
1
  import { EventEmitter } from 'node:events';
3
2
  export declare class WorkerPool<T = unknown, R = unknown> extends EventEmitter {
4
3
  private processorFile;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.m3u8BatchDownload = void 0;
3
+ exports.m3u8BatchDownload = m3u8BatchDownload;
4
4
  const node_fs_1 = require("node:fs");
5
5
  const node_path_1 = require("node:path");
6
6
  const m3u8_download_1 = require("./lib/m3u8-download");
@@ -38,7 +38,7 @@ async function m3u8BatchDownload(urls, options) {
38
38
  const tasks = await formatUrls(urls, options);
39
39
  return new Promise(rs => {
40
40
  let preDLing = false;
41
- const run = async () => {
41
+ const run = () => {
42
42
  const [key, keyNext] = [...tasks.keys()];
43
43
  if (key) {
44
44
  const o = { ...tasks.get(key) };
@@ -63,4 +63,3 @@ async function m3u8BatchDownload(urls, options) {
63
63
  return d;
64
64
  });
65
65
  }
66
- exports.m3u8BatchDownload = m3u8BatchDownload;
@@ -1,6 +1,3 @@
1
- /// <reference path="../../global.d.ts" />
2
- /// <reference types="node" />
3
- /// <reference types="node" />
4
1
  import type { IncomingHttpHeaders } from 'node:http';
5
2
  export interface TsItemInfo {
6
3
  /** ts 文件次序 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lzwme/m3u8-dl",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
4
4
  "description": "Batch download of m3u8 files and convert to mp4",
5
5
  "main": "cjs/index.js",
6
6
  "types": "cjs/index.d.ts",
@@ -12,7 +12,7 @@
12
12
  "url": "https://lzw.me"
13
13
  },
14
14
  "scripts": {
15
- "prepare": "husky install",
15
+ "prepare": "husky || true",
16
16
  "dev": "npm run watch",
17
17
  "watch": "npm run build -- -- -w",
18
18
  "lint": "flh --eslint --tscheck --prettier",
@@ -35,7 +35,6 @@
35
35
  "download",
36
36
  "ffmpeg"
37
37
  ],
38
- "packageManager": "pnpm@8.0.0",
39
38
  "engines": {
40
39
  "node": ">=14.18"
41
40
  },
@@ -44,22 +43,24 @@
44
43
  "registry": "https://registry.npmjs.com"
45
44
  },
46
45
  "devDependencies": {
47
- "@lzwme/fed-lint-helper": "^2.5.2",
48
- "@types/node": "^20.11.20",
49
- "@typescript-eslint/eslint-plugin": "^7.0.2",
50
- "@typescript-eslint/parser": "^7.0.2",
51
- "eslint": "^8.56.0",
46
+ "@eslint/js": "^9.8.0",
47
+ "@lzwme/fed-lint-helper": "^2.6.2",
48
+ "@types/node": "^22.0.0",
49
+ "@typescript-eslint/eslint-plugin": "^7.18.0",
50
+ "@typescript-eslint/parser": "^7.18.0",
51
+ "eslint": "^9.8.0",
52
52
  "eslint-config-prettier": "^9.1.0",
53
- "eslint-plugin-prettier": "^5.1.3",
54
- "husky": "^9.0.11",
55
- "prettier": "^3.2.5",
53
+ "eslint-plugin-prettier": "^5.2.1",
54
+ "husky": "^9.1.4",
55
+ "prettier": "^3.3.3",
56
56
  "standard-version": "^9.5.0",
57
- "typescript": "^5.3.3"
57
+ "typescript": "^5.5.4",
58
+ "typescript-eslint": "^7.18.0"
58
59
  },
59
60
  "dependencies": {
60
- "@lzwme/fe-utils": "^1.6.0",
61
- "commander": "^12.0.0",
62
- "console-log-colors": "^0.4.0",
61
+ "@lzwme/fe-utils": "^1.7.5",
62
+ "commander": "^12.1.0",
63
+ "console-log-colors": "^0.5.0",
63
64
  "enquirer": "^2.4.1",
64
65
  "m3u8-parser": "^7.1.0"
65
66
  },