@lzwme/m3u8-dl 1.4.3 → 1.6.0-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 +361 -68
- package/cjs/cli.js +37 -23
- package/cjs/i18n/locales/en.d.ts +108 -0
- package/cjs/i18n/locales/en.js +109 -0
- package/cjs/i18n/locales/zh.d.ts +108 -0
- package/cjs/i18n/locales/zh.js +109 -0
- package/cjs/index.d.ts +1 -1
- package/cjs/index.js +1 -1
- package/cjs/lib/file-download.js +8 -5
- package/cjs/lib/getM3u8Urls.d.ts +6 -0
- package/cjs/lib/getM3u8Urls.js +45 -27
- package/cjs/lib/i18n.d.ts +27 -0
- package/cjs/lib/i18n.js +89 -0
- package/cjs/lib/m3u8-convert.js +5 -4
- package/cjs/lib/m3u8-download.js +37 -11
- package/cjs/lib/utils.d.ts +1 -1
- package/cjs/lib/utils.js +5 -5
- package/cjs/server/download-server.d.ts +1 -0
- package/cjs/server/download-server.js +111 -39
- package/cjs/types/index.d.ts +1 -1
- package/cjs/types/index.js +1 -1
- package/cjs/types/m3u8.d.ts +4 -0
- package/client/assets/main-DYJAIw1q.css +1 -0
- package/client/assets/main-XL0wiaDU.js +25 -0
- package/client/index.html +4 -1144
- package/client/play.html +221 -14
- package/package.json +20 -16
- package/client/style.css +0 -137
package/README.MD
CHANGED
|
@@ -12,23 +12,65 @@
|
|
|
12
12
|
[![GitHub forks][forks-badge]][forks-url]
|
|
13
13
|
[![GitHub stars][stars-badge]][stars-url]
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
一个免费开源功能强大的 m3u8 视频批量下载工具,支持多线程下载、边下边播、WebUI 管理、视频解析等多种功能。支持 CLI命令行、浏览器、PC客户端、Docker 部署以及 Node.js API 调用等多种使用方式。
|
|
16
16
|
|
|
17
17
|

|
|
18
18
|
|
|
19
|
-
## 功能特性
|
|
19
|
+
## ✨ 功能特性
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
- `边下边播模式`。支持使用已下载的 ts 缓存文件在线播放。
|
|
23
|
-
- 支持指定多个 m3u8 地址批量下载。
|
|
24
|
-
- 支持缓存续传。下载失败会保留缓存,重试时只下载失败的片段。
|
|
25
|
-
- 支持常见的 AES 加密视频流解密。
|
|
26
|
-
- 自动转换为 mp4。**需全局安装 [ffmpeg](https://ffmpeg.org/download.html)**
|
|
27
|
-
- 支持指定采集站标准 API,以命令行交互的方式搜索和下载。
|
|
28
|
-
- `[NEW!]` 新增下载中心,支持启动为 webui 服务方式进行下载管理。
|
|
29
|
-
- `[NEW!]` 新增支持抖音、微博视频分享地址解析及无水印下载。
|
|
21
|
+
### 🚀 核心下载功能
|
|
30
22
|
|
|
31
|
-
|
|
23
|
+
- **多线程下载**:采用线程池模式,支持自定义线程数,大幅提升下载速度
|
|
24
|
+
- **边下边播模式**:支持使用已下载的 ts 缓存文件在线播放,无需等待完整下载
|
|
25
|
+
- **批量下载**:支持指定多个 m3u8 地址批量下载,支持文本文件批量导入
|
|
26
|
+
- **缓存续传**:下载失败会保留缓存,重试时只下载失败的片段,节省带宽和时间
|
|
27
|
+
- **AES 加密支持**:自动识别并解密常见的 AES-128 加密视频流
|
|
28
|
+
- **格式转换**:自动将下载的 ts 片段合并转换为 mp4 格式(需安装 [ffmpeg](https://ffmpeg.org/download.html))
|
|
29
|
+
- **多格式支持**:支持下载 mp4、mkv 等格式的视频文件
|
|
30
|
+
- **片段过滤**:支持忽略指定时间段的视频片段(如跳过片头片尾)
|
|
31
|
+
|
|
32
|
+
### 🌐 WebUI 下载管理
|
|
33
|
+
|
|
34
|
+
- **现代化界面**:基于 Vue 3 + TypeScript 构建的现代化 Web 界面
|
|
35
|
+
- **实时进度**:通过 WebSocket 实时显示下载进度和状态
|
|
36
|
+
- **任务管理**:支持暂停、恢复、删除下载任务,支持批量操作
|
|
37
|
+
- **下载中心**:集中管理所有下载任务,支持搜索和筛选
|
|
38
|
+
- **配置管理**:可视化配置下载参数(线程数、保存目录等)
|
|
39
|
+
- **访问控制**:支持设置访问密码(token)保护服务
|
|
40
|
+
|
|
41
|
+
### 🎬 视频解析功能
|
|
42
|
+
|
|
43
|
+
- **多平台支持**:支持抖音、微博、皮皮虾等平台的视频分享链接解析
|
|
44
|
+
- **无水印下载**:自动提取无水印视频地址并下载
|
|
45
|
+
- **智能识别**:自动识别视频平台并选择合适的解析器
|
|
46
|
+
|
|
47
|
+
### 🔍 智能提取功能
|
|
48
|
+
|
|
49
|
+
- **网页提取**:支持从视频播放页面自动提取 m3u8 地址
|
|
50
|
+
- **深度搜索**:支持多层级页面搜索,自动发现视频链接
|
|
51
|
+
- **批量提取**:一次提取多个视频链接,支持批量下载
|
|
52
|
+
|
|
53
|
+
### 📺 视频搜索功能
|
|
54
|
+
|
|
55
|
+
- **采集站支持**:支持标准采集站 API,通过命令行交互搜索和下载
|
|
56
|
+
- **缓存机制**:自动缓存搜索历史,支持继续未完成的下载
|
|
57
|
+
|
|
58
|
+
### 💻 多种使用方式
|
|
59
|
+
|
|
60
|
+
- **命令行工具**:提供完整的 CLI 命令,支持各种参数配置
|
|
61
|
+
- **Node.js API**:提供编程接口,方便集成到其他项目
|
|
62
|
+
- **Web 服务**:支持启动为 Web 服务,通过浏览器管理下载
|
|
63
|
+
- **Docker 部署**:提供 Docker 镜像,一键部署到服务器
|
|
64
|
+
- **Electron 桌面应用**:支持打包为桌面应用,包含内置浏览器功能
|
|
65
|
+
|
|
66
|
+
### 🌍 国际化支持
|
|
67
|
+
|
|
68
|
+
- 支持中文和英文多语言
|
|
69
|
+
- 命令行和 WebUI 均支持语言切换
|
|
70
|
+
|
|
71
|
+
## 📦 安装
|
|
72
|
+
|
|
73
|
+
**方式一:Node.js 核心库安装**
|
|
32
74
|
|
|
33
75
|
```bash
|
|
34
76
|
npm i -g @lzwme/m3u8-dl
|
|
@@ -41,108 +83,266 @@ m3u8dl -h
|
|
|
41
83
|
npx @lzwme/m3u8-dl -h
|
|
42
84
|
```
|
|
43
85
|
|
|
44
|
-
|
|
86
|
+
**方式二:桌面应用下载**
|
|
87
|
+
|
|
88
|
+
可访问如下地址之一下载最新版本:
|
|
89
|
+
|
|
90
|
+
- [https://m3u8-player.lzw.me/download.html](https://m3u8-player.lzw.me/download.html)
|
|
91
|
+
- [https://github.com/lzwme/m3u8-dl/releases](https://github.com/lzwme/m3u8-dl/releases)
|
|
92
|
+
|
|
93
|
+
## 📖 使用指南
|
|
94
|
+
|
|
95
|
+
> **提示**:如需要下载并转换为 `mp4` 视频格式,您需全局安装 [ffmpeg](https://ffmpeg.org/download.html)。
|
|
96
|
+
> 或者使用 `--ffmpeg-path` 参数指定 ffmpeg 的路径。
|
|
45
97
|
|
|
46
|
-
|
|
98
|
+
### 命令行使用
|
|
47
99
|
|
|
48
|
-
|
|
100
|
+
查看所有可用命令和选项:
|
|
49
101
|
|
|
50
102
|
```bash
|
|
51
103
|
m3u8dl --help
|
|
52
104
|
```
|
|
53
105
|
|
|
54
|
-
####
|
|
106
|
+
#### 基础下载
|
|
55
107
|
|
|
56
108
|
```bash
|
|
57
|
-
|
|
109
|
+
# 下载单个 m3u8 文件
|
|
110
|
+
m3u8dl https://example.com/video.m3u8
|
|
111
|
+
|
|
112
|
+
# 指定文件名和保存目录
|
|
113
|
+
m3u8dl https://example.com/video.m3u8 --filename "我的视频" --save-dir "./downloads"
|
|
114
|
+
|
|
115
|
+
# 启用边下边播模式
|
|
116
|
+
m3u8dl https://example.com/video.m3u8 --play
|
|
117
|
+
|
|
118
|
+
# 设置线程数(默认 4)
|
|
119
|
+
m3u8dl https://example.com/video.m3u8 --thread-num 8
|
|
120
|
+
|
|
121
|
+
# 不转换为 mp4(仅下载 ts 片段)
|
|
122
|
+
m3u8dl https://example.com/video.m3u8 --no-convert
|
|
123
|
+
|
|
124
|
+
# 忽略指定时间片段(如跳过前 30 秒和最后 60 秒)
|
|
125
|
+
m3u8dl https://example.com/video.m3u8 --ignore-segments "0-30,END-60"
|
|
58
126
|
```
|
|
59
127
|
|
|
60
|
-
####
|
|
128
|
+
#### 批量下载
|
|
129
|
+
|
|
130
|
+
**方式一:命令行参数**
|
|
61
131
|
|
|
62
132
|
```bash
|
|
63
|
-
#
|
|
64
|
-
m3u8dl "第1
|
|
133
|
+
# 下载多个文件,使用 | 分隔文件名和 URL
|
|
134
|
+
m3u8dl "第1集|https://example.com/ep1.m3u8" "第2集|https://example.com/ep2.m3u8" --filename "剧集名称"
|
|
65
135
|
```
|
|
66
136
|
|
|
67
|
-
|
|
137
|
+
**方式二:文本文件批量导入**
|
|
68
138
|
|
|
69
|
-
|
|
139
|
+
创建 `剧集列表.txt` 文件,格式如下(使用 `$` 分隔文件名和 URL):
|
|
70
140
|
|
|
71
141
|
```txt
|
|
72
|
-
第1集$https://
|
|
73
|
-
第2集$https://
|
|
74
|
-
第3集$https://
|
|
142
|
+
第1集$https://example.com/ep1.m3u8
|
|
143
|
+
第2集$https://example.com/ep2.m3u8
|
|
144
|
+
第3集$https://example.com/ep3.m3u8
|
|
75
145
|
```
|
|
76
146
|
|
|
77
|
-
|
|
147
|
+
然后执行:
|
|
78
148
|
|
|
79
149
|
```bash
|
|
80
|
-
m3u8dl
|
|
150
|
+
m3u8dl 剧集列表.txt --filename "剧集名称"
|
|
81
151
|
```
|
|
82
152
|
|
|
83
|
-
|
|
153
|
+
#### 视频解析下载
|
|
84
154
|
|
|
85
|
-
|
|
155
|
+
支持抖音、微博等平台的分享链接:
|
|
86
156
|
|
|
87
157
|
```bash
|
|
88
|
-
|
|
158
|
+
# 抖音视频分享链接
|
|
159
|
+
m3u8dl "https://v.douyin.com/xxxxx/" --type parser
|
|
89
160
|
|
|
90
|
-
#
|
|
91
|
-
m3u8dl
|
|
161
|
+
# 微博视频分享链接
|
|
162
|
+
m3u8dl "https://weibo.com/xxxxx" --type parser
|
|
92
163
|
```
|
|
93
164
|
|
|
94
|
-
|
|
165
|
+
#### 从网页提取 m3u8 地址
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
# 从视频播放页面自动提取 m3u8 地址并下载
|
|
169
|
+
m3u8dl "https://example.com/play/12345" --type web
|
|
170
|
+
```
|
|
95
171
|
|
|
96
|
-
|
|
172
|
+
#### 视频搜索下载
|
|
97
173
|
|
|
98
174
|
```bash
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
175
|
+
# 查看搜索命令帮助
|
|
176
|
+
m3u8dl search --help
|
|
177
|
+
|
|
178
|
+
# 指定采集站 API 并搜索下载(会缓存 API 地址)
|
|
179
|
+
m3u8dl search -u https://api.example.com/provide/vod/
|
|
180
|
+
|
|
181
|
+
# 直接搜索关键词
|
|
182
|
+
m3u8dl search "关键词" -u https://api.example.com/provide/vod/
|
|
103
183
|
```
|
|
104
184
|
|
|
105
|
-
|
|
185
|
+
> **声明**:以上仅作示例,请自行搜索查找可用的采集站 API。本工具仅用作技术研究学习,不提供任何具体资源类信息。
|
|
106
186
|
|
|
107
|
-
|
|
187
|
+
#### 常用命令行选项
|
|
108
188
|
|
|
109
|
-
|
|
189
|
+
| 选项 | 说明 |
|
|
190
|
+
|------|------|
|
|
191
|
+
| `-f, --filename <name>` | 指定文件名 |
|
|
192
|
+
| `-n, --thread-num <number>` | 设置下载线程数(默认 4) |
|
|
193
|
+
| `-p, --play` | 启用边下边播模式 |
|
|
194
|
+
| `-C, --cache-dir <dirpath>` | 指定缓存目录 |
|
|
195
|
+
| `-S, --save-dir <dirpath>` | 指定保存目录 |
|
|
196
|
+
| `--no-convert` | 不转换为 mp4 |
|
|
197
|
+
| `--no-del-cache` | 下载完成后不删除缓存 |
|
|
198
|
+
| `--ffmpeg-path <path>` | 指定 ffmpeg 路径 |
|
|
199
|
+
| `-H, --headers <headers>` | 设置请求头(JSON 格式) |
|
|
200
|
+
| `-I, --ignore-segments <time>` | 忽略指定时间片段 |
|
|
201
|
+
| `--debug` | 启用调试模式 |
|
|
202
|
+
| `--lang <lang>` | 设置语言(zh/en) |
|
|
110
203
|
|
|
111
|
-
|
|
112
|
-
|
|
204
|
+
### WebUI 下载管理
|
|
205
|
+
|
|
206
|
+
启动 Web 服务,通过浏览器管理下载任务:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# 启动服务(默认端口 6600)
|
|
210
|
+
m3u8dl server
|
|
211
|
+
|
|
212
|
+
# 指定端口和访问密码
|
|
213
|
+
m3u8dl server -p 8080 -t "your-secret-token"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
启动后,在浏览器中访问 `http://localhost:6600` 即可使用 WebUI。
|
|
217
|
+
|
|
218
|
+

|
|
219
|
+
|
|
220
|
+
**WebUI 主要功能:**
|
|
221
|
+
|
|
222
|
+
- 📥 创建下载任务(支持 m3u8 链接、视频分享链接、网页提取)
|
|
223
|
+
- 📊 实时查看下载进度和速度
|
|
224
|
+
- ⏸️ 暂停/恢复下载任务
|
|
225
|
+
- 🗑️ 删除任务和已下载文件
|
|
226
|
+
- ⚙️ 配置下载参数(线程数、保存目录等)
|
|
227
|
+
- 🔍 搜索和筛选任务
|
|
228
|
+
- 📁 查看已完成的下载
|
|
229
|
+
|
|
230
|
+
**环境变量配置:**
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
# 设置端口
|
|
234
|
+
export DS_PORT=6600
|
|
235
|
+
|
|
236
|
+
# 设置访问密码
|
|
237
|
+
export DS_SECRET=your-secret-token
|
|
238
|
+
|
|
239
|
+
# 设置保存目录
|
|
240
|
+
export DS_SAVE_DIR=./downloads
|
|
241
|
+
|
|
242
|
+
# 设置缓存目录
|
|
243
|
+
export DS_CACHE_DIR=./cache
|
|
113
244
|
|
|
114
|
-
|
|
115
|
-
|
|
245
|
+
# 设置 ffmpeg 路径
|
|
246
|
+
export DS_FFMPEG_PATH=/usr/local/bin/ffmpeg
|
|
116
247
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
248
|
+
# 启用调试模式
|
|
249
|
+
export DS_DEBUG=1
|
|
250
|
+
|
|
251
|
+
# 限制文件访问(仅允许访问下载和缓存目录)
|
|
252
|
+
export DS_LIMTE_FILE_ACCESS=1
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Node.js API 调用
|
|
256
|
+
|
|
257
|
+
在您的项目中使用编程接口:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { m3u8Download, m3u8BatchDownload, VideoParser, getM3u8Urls } from '@lzwme/m3u8-dl';
|
|
261
|
+
|
|
262
|
+
// 示例 1:单文件下载
|
|
263
|
+
const result = await m3u8Download('https://example.com/video.m3u8', {
|
|
264
|
+
filename: '我的视频',
|
|
265
|
+
saveDir: './downloads',
|
|
266
|
+
threadNum: 8,
|
|
267
|
+
debug: true,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (result.errmsg) {
|
|
271
|
+
console.error('下载失败:', result.errmsg);
|
|
272
|
+
} else {
|
|
273
|
+
console.log('下载成功:', result.filepath);
|
|
122
274
|
}
|
|
275
|
+
|
|
276
|
+
// 示例 2:批量下载
|
|
277
|
+
const fileList = [
|
|
278
|
+
'第1集$https://example.com/ep1.m3u8',
|
|
279
|
+
'第2集$https://example.com/ep2.m3u8',
|
|
280
|
+
];
|
|
281
|
+
await m3u8BatchDownload(fileList, {
|
|
282
|
+
filename: '剧集名称',
|
|
283
|
+
threadNum: 4,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// 示例 3:视频解析下载(抖音、微博等)
|
|
287
|
+
const parser = new VideoParser();
|
|
288
|
+
const parseResult = await parser.parse('https://v.douyin.com/xxxxx/');
|
|
289
|
+
if (parseResult.data) {
|
|
290
|
+
console.log('视频标题:', parseResult.data.title);
|
|
291
|
+
console.log('视频地址:', parseResult.data.url);
|
|
292
|
+
|
|
293
|
+
// 下载视频
|
|
294
|
+
await parser.download('https://v.douyin.com/xxxxx/', {
|
|
295
|
+
filename: parseResult.data.title,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 示例 4:从网页提取 m3u8 地址
|
|
300
|
+
const urls = await getM3u8Urls({
|
|
301
|
+
url: 'https://example.com/play/12345',
|
|
302
|
+
headers: {
|
|
303
|
+
'User-Agent': 'Mozilla/5.0...',
|
|
304
|
+
},
|
|
305
|
+
deep: 2, // 搜索深度
|
|
306
|
+
});
|
|
307
|
+
console.log('提取到的地址:', Array.from(urls.keys()));
|
|
308
|
+
|
|
309
|
+
// 示例 5:指定 ffmpeg 路径
|
|
310
|
+
import ffmpegStatic from 'ffmpeg-static';
|
|
311
|
+
m3u8Download('https://example.com/video.m3u8', {
|
|
312
|
+
filename: '测试视频',
|
|
313
|
+
ffmpegPath: ffmpegStatic, // 使用 ffmpeg-static 包
|
|
314
|
+
// 或指定系统路径
|
|
315
|
+
// ffmpegPath: '/usr/local/bin/ffmpeg',
|
|
316
|
+
});
|
|
123
317
|
```
|
|
124
318
|
|
|
125
|
-
|
|
319
|
+
### Docker 部署
|
|
126
320
|
|
|
127
|
-
|
|
321
|
+
#### 使用 Docker 命令
|
|
128
322
|
|
|
129
323
|
```bash
|
|
130
|
-
#
|
|
324
|
+
# 拉取镜像
|
|
131
325
|
docker pull renxia/m3u8dl-dl:latest
|
|
132
326
|
|
|
327
|
+
# 运行容器
|
|
133
328
|
docker run --rm -it \
|
|
134
329
|
-v ./cache:/app/cache \
|
|
135
330
|
-v ./downloads:/app/downloads \
|
|
136
331
|
-p 6600:6600 \
|
|
332
|
+
-e DS_PORT=6600 \
|
|
333
|
+
-e DS_SECRET=your-secret-token \
|
|
137
334
|
renxia/m3u8dl-dl:latest
|
|
138
335
|
```
|
|
139
336
|
|
|
140
|
-
|
|
337
|
+
#### 使用 Docker Compose
|
|
338
|
+
|
|
339
|
+
创建 `docker-compose.yml` 文件:
|
|
141
340
|
|
|
142
341
|
```yml
|
|
143
342
|
services:
|
|
144
|
-
|
|
145
|
-
image: renxia/
|
|
343
|
+
m3u8-dl:
|
|
344
|
+
image: renxia/m3u8dl-dl:latest
|
|
345
|
+
container_name: m3u8-dl
|
|
146
346
|
volumes:
|
|
147
347
|
- ./downloads:/app/downloads
|
|
148
348
|
- ./cache:/app/cache
|
|
@@ -154,33 +354,126 @@ services:
|
|
|
154
354
|
DS_CACHE_DIR: '/app/cache'
|
|
155
355
|
DS_SECRET: '' # 设置访问密码
|
|
156
356
|
DS_DEBUG: ''
|
|
157
|
-
|
|
158
|
-
|
|
357
|
+
DS_FFMPEG_PATH: '' # 留空则使用系统 PATH 中的 ffmpeg
|
|
358
|
+
DS_LIMTE_FILE_ACCESS: '1' # 限制文件访问
|
|
159
359
|
restart: unless-stopped
|
|
160
360
|
```
|
|
161
361
|
|
|
162
|
-
|
|
362
|
+
启动服务:
|
|
163
363
|
|
|
164
|
-
|
|
364
|
+
```bash
|
|
365
|
+
docker-compose up -d
|
|
366
|
+
```
|
|
165
367
|
|
|
166
|
-
|
|
368
|
+
部署成功后,浏览器访问 `http://your-server-ip:6600` 即可使用。
|
|
167
369
|
|
|
168
|
-
|
|
370
|
+
> **提示**:Docker 镜像已包含 ffmpeg,无需额外安装。镜像同时包含了 [AriaNg](https://github.com/mayswind/AriaNg) 静态资源。
|
|
371
|
+
|
|
372
|
+
### Electron 桌面应用
|
|
373
|
+
|
|
374
|
+
项目支持打包为 Electron 桌面应用,提供更丰富的功能:
|
|
375
|
+
|
|
376
|
+
- 🖥️ 原生桌面体验
|
|
377
|
+
- 🌐 内置浏览器,支持从网页提取视频链接
|
|
378
|
+
- 📱 系统托盘支持
|
|
379
|
+
- 🔄 自动更新功能
|
|
380
|
+
|
|
381
|
+
构建桌面应用:
|
|
169
382
|
|
|
170
383
|
```bash
|
|
171
|
-
|
|
384
|
+
# 进入应用目录
|
|
385
|
+
cd packages/m3u8dl-app
|
|
386
|
+
|
|
387
|
+
# 安装依赖
|
|
172
388
|
pnpm install
|
|
389
|
+
|
|
390
|
+
# 开发模式运行
|
|
173
391
|
pnpm dev
|
|
174
|
-
|
|
392
|
+
|
|
393
|
+
# 构建应用
|
|
394
|
+
pnpm build
|
|
175
395
|
```
|
|
176
396
|
|
|
177
|
-
|
|
397
|
+
下载已构建的应用:
|
|
398
|
+
|
|
399
|
+
- https://m3u8-player.lzw.me/download.html
|
|
400
|
+
- https://github.com/lzwme/m3u8-dl/releases
|
|
401
|
+
|
|
402
|
+
## 🛠️ 技术栈
|
|
403
|
+
|
|
404
|
+
- **后端**:Node.js + TypeScript + Express + WebSocket
|
|
405
|
+
- **前端**:Vue 3 + TypeScript + Vite + Pinia + TailwindCSS
|
|
406
|
+
- **桌面应用**:Electron
|
|
407
|
+
- **代码质量**:Biome (Linter & Formatter)
|
|
408
|
+
- **构建工具**:TypeScript Compiler
|
|
409
|
+
|
|
410
|
+
## 💻 开发指南
|
|
411
|
+
|
|
412
|
+
### 本地开发
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
# 克隆项目
|
|
416
|
+
git clone https://github.com/lzwme/m3u8-dl.git
|
|
417
|
+
cd m3u8-dl
|
|
418
|
+
|
|
419
|
+
# 安装依赖
|
|
420
|
+
pnpm install
|
|
421
|
+
|
|
422
|
+
# 开发模式(监听文件变化自动编译)
|
|
423
|
+
pnpm dev
|
|
424
|
+
|
|
425
|
+
# 构建项目
|
|
426
|
+
pnpm build
|
|
427
|
+
|
|
428
|
+
# 代码检查
|
|
429
|
+
pnpm lint
|
|
430
|
+
|
|
431
|
+
# 代码格式化
|
|
432
|
+
pnpm format
|
|
433
|
+
|
|
434
|
+
# 修复代码问题
|
|
435
|
+
pnpm fix
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
### 项目结构
|
|
439
|
+
|
|
440
|
+
```
|
|
441
|
+
m3u8-dl/
|
|
442
|
+
├── src/ # 源代码(TypeScript)
|
|
443
|
+
│ ├── cli.ts # 命令行入口
|
|
444
|
+
│ ├── lib/ # 核心库
|
|
445
|
+
│ ├── server/ # Web 服务
|
|
446
|
+
│ ├── video-parser/ # 视频解析器
|
|
447
|
+
│ └── types/ # 类型定义
|
|
448
|
+
├── packages/
|
|
449
|
+
│ ├── frontend/ # Vue 3 前端项目
|
|
450
|
+
│ └── m3u8dl-app/ # Electron 桌面应用
|
|
451
|
+
├── cjs/ # 编译后的 CommonJS 代码
|
|
452
|
+
└── client/ # 前端构建输出
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### 贡献代码
|
|
456
|
+
|
|
457
|
+
欢迎提交 Issue 和 Pull Request!
|
|
458
|
+
|
|
459
|
+
1. [Fork](https://github.com/lzwme/m3u8-dl/fork) 本项目
|
|
460
|
+
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
|
461
|
+
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
|
|
462
|
+
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
|
463
|
+
5. 开启 Pull Request
|
|
464
|
+
|
|
465
|
+
**欢迎贡献想法与代码!** 🎉
|
|
466
|
+
|
|
467
|
+
## 📚 相关资源
|
|
468
|
+
|
|
469
|
+
- [ffmpeg 下载](https://ffmpeg.org/download.html) - 视频处理工具
|
|
470
|
+
- [m3u8 格式说明](https://en.wikipedia.org/wiki/M3U) - M3U8 播放列表格式
|
|
471
|
+
- [项目更新日志](./CHANGELOG.md) - 查看版本更新历史
|
|
178
472
|
|
|
179
|
-
|
|
473
|
+
## 🙏 致谢
|
|
180
474
|
|
|
181
|
-
|
|
475
|
+
感谢以下项目的启发和参考:
|
|
182
476
|
|
|
183
|
-
- [ffmpeg download](https://ffmpeg.org/download.html)
|
|
184
477
|
- [m3u8-multi-thread-downloader](https://github.com/sahadev/m3u8Downloader)
|
|
185
478
|
- [m3u8Utils](https://github.com/liupishui/m3u8Utils)
|
|
186
479
|
|
package/cjs/cli.js
CHANGED
|
@@ -37,37 +37,45 @@ const node_path_1 = require("node:path");
|
|
|
37
37
|
const fe_utils_1 = require("@lzwme/fe-utils");
|
|
38
38
|
const commander_1 = require("commander");
|
|
39
39
|
const console_log_colors_1 = require("console-log-colors");
|
|
40
|
+
const i18n_js_1 = require("./lib/i18n.js");
|
|
40
41
|
const utils_js_1 = require("./lib/utils.js");
|
|
41
42
|
const video_search_js_1 = require("./lib/video-search.js");
|
|
42
43
|
const m3u8_batch_download_1 = require("./m3u8-batch-download");
|
|
43
44
|
const pkg = (0, fe_utils_1.readJsonFileSync)((0, node_path_1.resolve)(__dirname, '../package.json'));
|
|
44
45
|
process.on('unhandledRejection', r => {
|
|
45
46
|
console.error(r);
|
|
46
|
-
|
|
47
|
+
const lang = (0, i18n_js_1.getLang)();
|
|
48
|
+
utils_js_1.logger.info(`[${(0, i18n_js_1.t)('common.exit', lang)}][unhandledRejection]`, r.message);
|
|
47
49
|
process.exit();
|
|
48
50
|
});
|
|
49
51
|
process.on('SIGINT', signal => {
|
|
50
|
-
|
|
52
|
+
const lang = (0, i18n_js_1.getLang)();
|
|
53
|
+
utils_js_1.logger.info(`[SIGINT]${(0, i18n_js_1.t)('common.exit', lang)}`, signal);
|
|
51
54
|
process.exit();
|
|
52
55
|
});
|
|
56
|
+
// Initialize language before using t()
|
|
57
|
+
const initialLang = (0, i18n_js_1.getLang)();
|
|
58
|
+
(0, i18n_js_1.setLanguage)(initialLang);
|
|
53
59
|
commander_1.program
|
|
54
60
|
.version(pkg.version, '-v, --version')
|
|
55
61
|
.description((0, console_log_colors_1.cyanBright)(pkg.description))
|
|
56
|
-
.argument('<m3u8Urls...>', '
|
|
57
|
-
.option('--silent', '
|
|
58
|
-
.option('--debug', '
|
|
59
|
-
.option('-f, --filename <name>', '
|
|
60
|
-
.option('-n, --thread-num <number>', '
|
|
61
|
-
.option('-F, --force', '
|
|
62
|
-
.option('--no-progress', '
|
|
63
|
-
.option('-p, --play', '
|
|
64
|
-
.option('-C, --cache-dir <dirpath>', '
|
|
65
|
-
.option('-S, --save-dir <dirpath>', '
|
|
66
|
-
.option('--no-del-cache', '
|
|
67
|
-
.option('--no-convert', '
|
|
68
|
-
.option('-
|
|
69
|
-
.option('-
|
|
70
|
-
.option('-
|
|
62
|
+
.argument('<m3u8Urls...>', (0, i18n_js_1.t)('cli.command.download.description', initialLang))
|
|
63
|
+
.option('--silent', (0, i18n_js_1.t)('cli.option.silent', initialLang))
|
|
64
|
+
.option('--debug', (0, i18n_js_1.t)('cli.option.debug', initialLang))
|
|
65
|
+
.option('-f, --filename <name>', (0, i18n_js_1.t)('cli.option.filename', initialLang))
|
|
66
|
+
.option('-n, --thread-num <number>', (0, i18n_js_1.t)('cli.option.threadNum', initialLang))
|
|
67
|
+
.option('-F, --force', (0, i18n_js_1.t)('cli.option.force', initialLang))
|
|
68
|
+
.option('--no-progress', (0, i18n_js_1.t)('cli.option.noProgress', initialLang))
|
|
69
|
+
.option('-p, --play', (0, i18n_js_1.t)('cli.option.play', initialLang))
|
|
70
|
+
.option('-C, --cache-dir <dirpath>', (0, i18n_js_1.t)('cli.option.cacheDir', initialLang))
|
|
71
|
+
.option('-S, --save-dir <dirpath>', (0, i18n_js_1.t)('cli.option.saveDir', initialLang))
|
|
72
|
+
.option('--no-del-cache', (0, i18n_js_1.t)('cli.option.noDelCache', initialLang))
|
|
73
|
+
.option('--no-convert', (0, i18n_js_1.t)('cli.option.noConvert', initialLang))
|
|
74
|
+
.option('--ffmpeg-path <path>', (0, i18n_js_1.t)('cli.option.ffmpegPath', initialLang))
|
|
75
|
+
.option('-H, --headers <headers>', (0, i18n_js_1.t)('cli.option.headers', initialLang))
|
|
76
|
+
.option('-T, --type <type>', (0, i18n_js_1.t)('cli.option.type', initialLang))
|
|
77
|
+
.option('-I, --ignore-segments <time-segments>', (0, i18n_js_1.t)('cli.option.ignoreSegments', initialLang))
|
|
78
|
+
.option('--lang <lang>', (0, i18n_js_1.t)('cli.option.lang', initialLang))
|
|
71
79
|
.action(async (urls) => {
|
|
72
80
|
const options = getOptions();
|
|
73
81
|
utils_js_1.logger.debug(urls, options);
|
|
@@ -82,9 +90,10 @@ commander_1.program
|
|
|
82
90
|
});
|
|
83
91
|
commander_1.program
|
|
84
92
|
.command('server')
|
|
85
|
-
.description('
|
|
86
|
-
.option('-P, --port <port>', '
|
|
87
|
-
.option('-t, --token <token>', '
|
|
93
|
+
.description((0, i18n_js_1.t)('cli.command.server.description', initialLang))
|
|
94
|
+
.option('-P, --port <port>', (0, i18n_js_1.t)('cli.option.port', initialLang))
|
|
95
|
+
.option('-t, --token <token>', (0, i18n_js_1.t)('cli.option.token', initialLang))
|
|
96
|
+
.option('--lang <lang>', (0, i18n_js_1.t)('cli.option.lang', initialLang))
|
|
88
97
|
.action((options) => {
|
|
89
98
|
const opts = getOptions();
|
|
90
99
|
if (opts.debug)
|
|
@@ -99,16 +108,21 @@ commander_1.program
|
|
|
99
108
|
commander_1.program
|
|
100
109
|
.command('search [keyword]')
|
|
101
110
|
.alias('s')
|
|
102
|
-
.option('-u,--url <api...>',
|
|
103
|
-
.option('-d, --apidir <dirpath>', '
|
|
111
|
+
.option('-u,--url <api...>', (0, i18n_js_1.t)('cli.option.url', initialLang))
|
|
112
|
+
.option('-d, --apidir <dirpath>', (0, i18n_js_1.t)('cli.option.apidir', initialLang))
|
|
113
|
+
.option('--lang <lang>', (0, i18n_js_1.t)('cli.option.lang', initialLang))
|
|
104
114
|
// .option('-R,--remote-config-url <url>', '自定义远程配置加载地址。默认从主仓库配置读取')
|
|
105
|
-
.description('
|
|
115
|
+
.description((0, i18n_js_1.t)('cli.command.search.description', initialLang))
|
|
106
116
|
.action(async (keyword, options) => {
|
|
107
117
|
await (0, video_search_js_1.VideoSerachAndDL)(keyword, options, getOptions());
|
|
108
118
|
});
|
|
109
119
|
commander_1.program.parse(process.argv);
|
|
110
120
|
function getOptions() {
|
|
111
121
|
const options = commander_1.program.opts();
|
|
122
|
+
// Set global language if specified
|
|
123
|
+
if (options.lang) {
|
|
124
|
+
(0, i18n_js_1.setLanguage)((0, i18n_js_1.getLang)(options.lang));
|
|
125
|
+
}
|
|
112
126
|
if (options.debug) {
|
|
113
127
|
utils_js_1.logger.updateOptions({ levelType: 'debug' });
|
|
114
128
|
}
|