@lzwme/m3u8-dl 1.6.0-0 → 1.7.0
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/README.MD +293 -212
- package/README.zh-CN.md +580 -0
- package/cjs/i18n/locales/en.d.ts +9 -0
- package/cjs/i18n/locales/en.js +10 -1
- package/cjs/i18n/locales/{zh.d.ts → zh-CN.d.ts} +9 -0
- package/cjs/i18n/locales/{zh.js → zh-CN.js} +10 -1
- package/cjs/lib/file-download.js +1 -1
- package/cjs/lib/format-options.js +27 -4
- package/cjs/lib/i18n.d.ts +1 -1
- package/cjs/lib/i18n.js +29 -10
- package/cjs/lib/local-play.js +1 -1
- package/cjs/lib/m3u8-download.js +1 -1
- package/cjs/lib/utils.d.ts +2 -0
- package/cjs/lib/utils.js +14 -0
- package/cjs/server/download-server.d.ts +11 -4
- package/cjs/server/download-server.js +170 -73
- package/cjs/types/m3u8.d.ts +2 -2
- package/client/assets/main-BSWj1VKy.js +29 -0
- package/client/assets/main-T6xR17Gh.css +1 -0
- package/client/index.html +3 -3
- package/client/m3u8-capture.user.js +114 -0
- package/client/play.html +38 -21
- package/package.json +27 -13
- package/client/assets/main-DYJAIw1q.css +0 -1
- package/client/assets/main-XL0wiaDU.js +0 -25
|
@@ -7,22 +7,45 @@ const fe_utils_1 = require("@lzwme/fe-utils");
|
|
|
7
7
|
const video_parser_1 = require("../video-parser");
|
|
8
8
|
const utils_1 = require("./utils");
|
|
9
9
|
const fileSupportExtList = [
|
|
10
|
+
// video
|
|
10
11
|
'.mp4',
|
|
11
12
|
'.mkv',
|
|
12
13
|
'.avi',
|
|
13
14
|
'.mov',
|
|
14
15
|
'.wmv',
|
|
15
16
|
'.ts',
|
|
16
|
-
|
|
17
|
-
'.
|
|
18
|
-
'.
|
|
19
|
-
'.
|
|
17
|
+
// audio
|
|
18
|
+
'.mp3',
|
|
19
|
+
'.wav',
|
|
20
|
+
'.ogg',
|
|
21
|
+
'.m4a',
|
|
22
|
+
'.aac',
|
|
23
|
+
'.flac',
|
|
24
|
+
'.ape',
|
|
25
|
+
// document
|
|
20
26
|
'.doc',
|
|
21
27
|
'.docx',
|
|
22
28
|
'.xls',
|
|
23
29
|
'.xlsx',
|
|
24
30
|
'.ppt',
|
|
25
31
|
'.pptx',
|
|
32
|
+
'.pdf',
|
|
33
|
+
'.mobi',
|
|
34
|
+
'.epub',
|
|
35
|
+
// others
|
|
36
|
+
'.exe',
|
|
37
|
+
'.msi',
|
|
38
|
+
'.zip',
|
|
39
|
+
'.rar',
|
|
40
|
+
'.7z',
|
|
41
|
+
'.tar.gz',
|
|
42
|
+
'.tar.bz2',
|
|
43
|
+
'.tar.xz',
|
|
44
|
+
'.iso',
|
|
45
|
+
'.dmg',
|
|
46
|
+
'.pkg',
|
|
47
|
+
'.deb',
|
|
48
|
+
'.rpm',
|
|
26
49
|
];
|
|
27
50
|
function formatOptions(url, opts) {
|
|
28
51
|
const options = {
|
package/cjs/lib/i18n.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared i18n utility for backend (CLI, SDK, Server)
|
|
3
3
|
*/
|
|
4
4
|
import type { AnyObject } from '@lzwme/fe-utils';
|
|
5
|
-
type Locale = 'zh' | 'en';
|
|
5
|
+
type Locale = 'zh-CN' | 'en';
|
|
6
6
|
export declare const LANG_CODES: Set<string>;
|
|
7
7
|
/**
|
|
8
8
|
* Detect language from OS or environment
|
package/cjs/lib/i18n.js
CHANGED
|
@@ -9,7 +9,7 @@ exports.setLanguage = setLanguage;
|
|
|
9
9
|
exports.getLanguage = getLanguage;
|
|
10
10
|
exports.getLang = getLang;
|
|
11
11
|
exports.t = t;
|
|
12
|
-
exports.LANG_CODES = new Set(['zh', 'en']);
|
|
12
|
+
exports.LANG_CODES = new Set(['zh-CN', 'en']);
|
|
13
13
|
let globalLang = null;
|
|
14
14
|
/**
|
|
15
15
|
* Detect language from OS or environment
|
|
@@ -18,17 +18,27 @@ function detectLanguage() {
|
|
|
18
18
|
// Check environment variable first
|
|
19
19
|
const envLang = process.env.M3U8DL_LANG || process.env.LANG;
|
|
20
20
|
if (envLang) {
|
|
21
|
-
const langCode = envLang.toLowerCase()
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const langCode = envLang.toLowerCase();
|
|
22
|
+
// 支持 zh-CN, zh-TW, zh 等变体映射到 zh-CN
|
|
23
|
+
if (langCode.startsWith('zh')) {
|
|
24
|
+
return 'zh-CN';
|
|
25
|
+
}
|
|
26
|
+
const baseLangCode = langCode.split('-')[0].split('_')[0];
|
|
27
|
+
if (exports.LANG_CODES.has(baseLangCode)) {
|
|
28
|
+
return baseLangCode;
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
31
|
// Try to detect from OS locale
|
|
27
32
|
try {
|
|
28
33
|
const osLocale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
29
|
-
const langCode = osLocale.toLowerCase()
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
const langCode = osLocale.toLowerCase();
|
|
35
|
+
// 支持 zh-CN, zh-TW 等变体映射到 zh-CN
|
|
36
|
+
if (langCode.startsWith('zh')) {
|
|
37
|
+
return 'zh-CN';
|
|
38
|
+
}
|
|
39
|
+
const baseLangCode = langCode.split('-')[0];
|
|
40
|
+
if (exports.LANG_CODES.has(baseLangCode)) {
|
|
41
|
+
return baseLangCode;
|
|
32
42
|
}
|
|
33
43
|
}
|
|
34
44
|
catch {
|
|
@@ -53,8 +63,15 @@ function getLanguage() {
|
|
|
53
63
|
* Get language from various sources
|
|
54
64
|
*/
|
|
55
65
|
function getLang(lang) {
|
|
56
|
-
if (lang
|
|
57
|
-
|
|
66
|
+
if (lang) {
|
|
67
|
+
const normalizedLang = lang.toLowerCase();
|
|
68
|
+
// 支持向后兼容:将 zh 映射到 zh-CN
|
|
69
|
+
if (normalizedLang === 'zh' || normalizedLang.startsWith('zh')) {
|
|
70
|
+
return 'zh-CN';
|
|
71
|
+
}
|
|
72
|
+
if (exports.LANG_CODES.has(normalizedLang)) {
|
|
73
|
+
return normalizedLang;
|
|
74
|
+
}
|
|
58
75
|
}
|
|
59
76
|
if (globalLang) {
|
|
60
77
|
return globalLang;
|
|
@@ -66,7 +83,9 @@ function getLang(lang) {
|
|
|
66
83
|
*/
|
|
67
84
|
function t(key, lang, params) {
|
|
68
85
|
const targetLang = getLang(lang);
|
|
69
|
-
|
|
86
|
+
// 将 zh-CN 映射到文件名 zh-CN.ts
|
|
87
|
+
const langFile = targetLang === 'zh-CN' ? 'zh-CN' : targetLang;
|
|
88
|
+
const translations = require(`../i18n/locales/${langFile}.js`);
|
|
70
89
|
// Navigate through nested keys (e.g., 'cli.command.download.description')
|
|
71
90
|
const keys = key.split('.');
|
|
72
91
|
let value = translations.default || translations;
|
package/cjs/lib/local-play.js
CHANGED
|
@@ -19,7 +19,7 @@ async function localPlay(m3u8Info) {
|
|
|
19
19
|
const cacheFilepath = toLocalM3u8(m3u8Info);
|
|
20
20
|
const filename = (0, node_path_1.basename)(cacheFilepath);
|
|
21
21
|
const info = await createLocalServer((0, node_path_1.dirname)(cacheDir));
|
|
22
|
-
const playUrl = `https://lzw.me
|
|
22
|
+
const playUrl = `https://m3u8-player.lzw.me?from=sdk&url=${encodeURIComponent(`${info.origin}/${cacheDirname}/${filename}`)}`;
|
|
23
23
|
const cmd = `${process.platform === 'win32' ? 'start' : 'open'} ${playUrl}`;
|
|
24
24
|
(0, fe_utils_1.execSync)(cmd);
|
|
25
25
|
return info;
|
package/cjs/lib/m3u8-download.js
CHANGED
|
@@ -9,8 +9,8 @@ const node_path_1 = require("node:path");
|
|
|
9
9
|
const fe_utils_1 = require("@lzwme/fe-utils");
|
|
10
10
|
const helper_1 = require("@lzwme/fe-utils/cjs/common/helper");
|
|
11
11
|
const console_log_colors_1 = require("console-log-colors");
|
|
12
|
-
const i18n_js_1 = require("./i18n.js");
|
|
13
12
|
const format_options_js_1 = require("./format-options.js");
|
|
13
|
+
const i18n_js_1 = require("./i18n.js");
|
|
14
14
|
const local_play_js_1 = require("./local-play.js");
|
|
15
15
|
const m3u8_convert_js_1 = require("./m3u8-convert.js");
|
|
16
16
|
const parseM3u8_js_1 = require("./parseM3u8.js");
|
package/cjs/lib/utils.d.ts
CHANGED
|
@@ -18,3 +18,5 @@ export declare function getLocation(url: string, method?: string): Promise<strin
|
|
|
18
18
|
* 如果 headers 是字符串,会先将其解析为对象;如果 headers 为空,则返回空对象。
|
|
19
19
|
*/
|
|
20
20
|
export declare function formatHeaders(headers: string | OutgoingHttpHeaders): Record<string, string>;
|
|
21
|
+
/** 异步检查文件是否存在 */
|
|
22
|
+
export declare function checkFileExists(filepath: string): Promise<boolean>;
|
package/cjs/lib/utils.js
CHANGED
|
@@ -5,7 +5,9 @@ exports.isSupportFfmpeg = isSupportFfmpeg;
|
|
|
5
5
|
exports.findFiles = findFiles;
|
|
6
6
|
exports.getLocation = getLocation;
|
|
7
7
|
exports.formatHeaders = formatHeaders;
|
|
8
|
+
exports.checkFileExists = checkFileExists;
|
|
8
9
|
const node_fs_1 = require("node:fs");
|
|
10
|
+
const promises_1 = require("node:fs/promises");
|
|
9
11
|
const node_path_1 = require("node:path");
|
|
10
12
|
const fe_utils_1 = require("@lzwme/fe-utils");
|
|
11
13
|
exports.request = new fe_utils_1.Request({
|
|
@@ -65,3 +67,15 @@ function formatHeaders(headers) {
|
|
|
65
67
|
headers = Object.fromEntries(headers.split('\n').map(line => line.split(':').map(d => d.trim())));
|
|
66
68
|
return (0, fe_utils_1.toLowcaseKeyObject)(headers);
|
|
67
69
|
}
|
|
70
|
+
/** 异步检查文件是否存在 */
|
|
71
|
+
async function checkFileExists(filepath) {
|
|
72
|
+
try {
|
|
73
|
+
if (!filepath)
|
|
74
|
+
return false;
|
|
75
|
+
await (0, promises_1.access)(filepath, promises_1.constants.F_OK);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Express } from 'express';
|
|
2
|
-
import type {
|
|
2
|
+
import type { WebSocketServer } from 'ws';
|
|
3
3
|
import type { M3u8DLOptions, M3u8DLProgressStats, M3u8WorkerPool, TsItemInfo } from '../types/m3u8.js';
|
|
4
4
|
interface DLServerOptions {
|
|
5
5
|
port?: number;
|
|
@@ -18,12 +18,18 @@ interface CacheItem extends Partial<M3u8DLProgressStats> {
|
|
|
18
18
|
/** 格式化后实际下载使用的参数 */
|
|
19
19
|
dlOptions?: M3u8DLOptions;
|
|
20
20
|
status: 'pause' | 'resume' | 'done' | 'pending' | 'error';
|
|
21
|
+
/**
|
|
22
|
+
* 当前任务的 ts 信息
|
|
23
|
+
* @deprecated 下一版本将移除
|
|
24
|
+
*/
|
|
21
25
|
current?: TsItemInfo;
|
|
26
|
+
/** 当前任务的 ts 缓存目录 */
|
|
27
|
+
cacheDir?: string;
|
|
22
28
|
workPoll?: M3u8WorkerPool;
|
|
23
29
|
}
|
|
24
30
|
export declare class DLServer {
|
|
25
31
|
app: Express;
|
|
26
|
-
wss:
|
|
32
|
+
wss: WebSocketServer | null;
|
|
27
33
|
/** DS 参数 */
|
|
28
34
|
options: DLServerOptions;
|
|
29
35
|
private serverInfo;
|
|
@@ -38,11 +44,12 @@ export declare class DLServer {
|
|
|
38
44
|
private checkDLFileLaest;
|
|
39
45
|
private checkDLFileTimer;
|
|
40
46
|
private checkDLFileIsExists;
|
|
47
|
+
private checkItemStatus;
|
|
41
48
|
dlCacheClone(): [string, CacheItem][];
|
|
42
49
|
private cacheSaveTimer;
|
|
43
50
|
saveCache(): void;
|
|
44
|
-
private
|
|
45
|
-
saveConfig(config: M3u8DLOptions, configPath?: string): void
|
|
51
|
+
private loadConfig;
|
|
52
|
+
saveConfig(config: M3u8DLOptions, configPath?: string): Promise<void>;
|
|
46
53
|
private createApp;
|
|
47
54
|
private startDownload;
|
|
48
55
|
startNextPending(): void;
|