1
mirror of https://github.com/nilaoda/N_m3u8DL-CLI synced 2025-09-07 02:45:59 +02:00

Compare commits

...

248 Commits
2.2.4 ... 2.9.8

Author SHA1 Message Date
nilaoda
faf67cd527 2.9.8 2021-11-20 10:14:31 +08:00
nilaoda
38d1a1a2dc 修正init url缺失baseurl问题 2021-11-12 23:28:35 +08:00
nilaoda
12eb68d592 修复日志冲突问题 2021-10-19 11:14:41 +08:00
nilaoda
0804e295e5 修复流选择的显示bug 2021-10-19 10:53:03 +08:00
nilaoda
847c4683cb update wetv js 2021-10-17 00:51:46 +08:00
nilaoda
8c72947860 package exe only 2021-10-17 00:24:34 +08:00
nilaoda
f0b240a6ee 优化AppleTV处理 2021-09-05 19:21:08 +08:00
nilaoda
793cf53042 解决同一个period且同id导致被重复添加分片 2021-09-05 18:17:05 +08:00
nilaoda
612fc29197 修复padding负值问题 2021-09-02 22:41:16 +08:00
nilaoda
307e2389de Update README.md 2021-09-02 10:46:11 +08:00
nilaoda
1c932abdc3 Update AssemblyInfo.cs 2021-08-23 11:38:19 +08:00
nilaoda
314f0065c7 fix node select bug and type show bug #496 2021-08-23 11:35:14 +08:00
nilaoda
59060bb74d Merge pull request #495 from ncnnnnn/patch-2
Update README.md
2021-08-21 22:30:13 +08:00
ncnnnnn
cab882c3a3 Update README.md 2021-08-21 20:35:02 +08:00
nilaoda
9955532ce5 强校验MAP下载成功 2021-08-15 20:18:06 +08:00
nilaoda
7e127be8c2 初步修改显示输出样式 2021-08-15 15:32:01 +08:00
nilaoda
b46571a57f Merge pull request #470 from CW-B-W/master
Fix typos in README_ENG.md
2021-07-18 02:28:52 +08:00
Chiao-Wei Wang
da5861d907 Update README_ENG.md 2021-07-18 01:57:16 +08:00
nilaoda
92bc91a1fb drm-json to m3u8 2021-07-04 00:40:05 +08:00
nilaoda
439f50103e 1.支持iq-drm格式json自动转换 2.优化master选择最高清晰度逻辑 2021-07-04 00:38:17 +08:00
nilaoda
8a95e31b2f update docs 2021-07-03 22:35:00 +08:00
nilaoda
115b8a156a Update packages.config 2021-06-27 23:06:33 +08:00
nilaoda
120bcaebb5 Update N_m3u8DL-CLI.csproj 2021-06-27 23:01:22 +08:00
nilaoda
455d56707c Update N_m3u8DL-CLI.csproj 2021-06-27 22:55:05 +08:00
nilaoda
048adcf118 支持解压brotli 2021-06-27 22:41:12 +08:00
nilaoda
fe5aa27b1c 修正判断png图片时可能出现的数组越界bug 2021-06-27 22:39:16 +08:00
nilaoda
039aa489b1 优化范围选择结尾识别 2021-06-27 22:38:32 +08:00
nilaoda
14e80f0b06 优化输出并增加ETA显示 2021-03-27 14:04:31 +08:00
nilaoda
2256fff549 日志文件初始化时,精确到毫秒防止重复 2021-03-27 14:03:54 +08:00
nilaoda
84cfd4e138 Update README_ENG.md 2021-03-26 09:35:28 +08:00
nilaoda
e70c229135 Update README.md 2021-03-26 09:30:30 +08:00
nilaoda
8b520d0c19 修正AppleTV逻辑 2021-03-25 23:11:18 +08:00
nilaoda
71d69de51a Update changelog.txt 2021-03-25 22:09:44 +08:00
nilaoda
bc89ead00d 为下载分片增加了自动重试机制(3次) 2021-03-25 22:09:26 +08:00
nilaoda
ae79d6eb3a 优化下载监控 2021-03-25 22:07:04 +08:00
nilaoda
96bd8af883 适配AppleTv资源 2021-03-22 23:18:22 +08:00
nilaoda
89b1e30e0f 修复enableAudioOnly且下载MPD文件时留下冗余(Audio)文件夹的情况 2021-03-22 23:17:09 +08:00
nilaoda
7a741359ab Update README.md 2021-03-04 13:17:07 +08:00
nilaoda
564b6ad291 Update README_ENG.md 2021-03-04 13:12:06 +08:00
nilaoda
a0fc9404f7 Update README.md 2021-03-04 13:11:06 +08:00
nilaoda
5267be1699 Update changelog.txt 2021-03-04 12:26:20 +08:00
nilaoda
20bfda39e7 bug fix
- 修复M3U8选择音轨/字幕不生效问题
  - 外挂音轨时enableAudioOnly可仅下载音频
2021-03-04 12:23:51 +08:00
nilaoda
ba4c0eeda7 Update Program.cs 2021-02-22 11:58:52 +08:00
nilaoda
5d72e24002 update docs 2021-02-22 11:16:45 +08:00
nilaoda
a87c051d23 update readme 2021-02-22 11:00:32 +08:00
nilaoda
44e1b68d6b update help info 2021-02-22 10:57:53 +08:00
nilaoda
e65dfa52cd Merge pull request #375 from evanlabs/master
添加用户网络代理支持,使用--proxyAddress指定代理地址。
2021-02-22 10:49:42 +08:00
EvanYeung
965c173899 添加用户网络代理支持,使用--proxyAddress指定代理地址。 2021-02-22 09:50:18 +08:00
nilaoda
880af02cc2 打印MPD解析日志 2021-02-21 15:13:44 +08:00
nilaoda
2742de43c4 增加键值 2021-02-21 15:13:07 +08:00
nilaoda
9d8cb57390 MPD检测最后一个分片是否有效 2021-02-21 13:24:15 +08:00
nilaoda
9e2a192dab Update changelog.txt 2021-02-21 13:23:57 +08:00
nilaoda
33cf9e2256 Update Parser.cs 2021-02-11 17:06:19 +08:00
nilaoda
2959cbbb5c Update changelog.txt 2021-02-11 17:06:08 +08:00
nilaoda
c2eb8a6adc 修正MPD拼接BaseUrl逻辑 2021-02-10 22:32:06 +08:00
nilaoda
e1b591b81c Update N_m3u8DL-CLI.csproj 2021-02-03 11:54:02 +08:00
nilaoda
334b1939b5 修复气球云;优化独播库 2021-02-03 00:38:57 +08:00
nilaoda
7e916b65fd Update changelog.txt 2021-02-01 22:57:37 +08:00
nilaoda
4ead563fa2 修正自定义KEY切存在IV时的隐患 2021-02-01 22:57:27 +08:00
nilaoda
1b387a06e5 update docs 2021-02-01 22:52:07 +08:00
nilaoda
6e7b4ac7ea ddyun识别90mm 2021-02-01 22:42:56 +08:00
nilaoda
e98c5205d1 优化跳过png的算法 2021-02-01 14:18:03 +08:00
nilaoda
d7890dd124 优化跳过png的算法 2021-02-01 14:11:28 +08:00
nilaoda
82f2111522 update 2021-01-24 16:26:32 +08:00
nilaoda
4c3207586f Update MPDParser.cs 2021-01-24 16:24:03 +08:00
nilaoda
69b411e37c fix sub merge bug 2021-01-24 16:22:13 +08:00
nilaoda
1e8525041f Download from DSNP 2021-01-24 16:21:32 +08:00
nilaoda
65ae72d4a4 Update MPDParser.cs 2021-01-18 20:36:43 +08:00
nilaoda
4a4bfae5ab 优化MPD下载行为 2021-01-18 02:00:19 +08:00
nilaoda
d586dddfcd Update changelog.txt 2021-01-18 01:59:31 +08:00
nilaoda
fca6b3ff6c Update changelog.txt 2020-12-29 23:19:39 +08:00
nilaoda
5d75626a36 Update Global.cs 2020-12-29 23:18:16 +08:00
nilaoda
a94271c244 mpd - xigua 2020-12-20 18:53:40 +08:00
nilaoda
c51118dce7 解密huke88 2020-12-20 18:53:02 +08:00
nilaoda
81b2e87bf7 处理同一ID分散在不同Period的情况 2020-12-12 02:07:41 +08:00
nilaoda
71a9878aaa Update changelog.txt 2020-12-06 21:32:48 +08:00
nilaoda
769fe4e926 Update Global.cs 2020-12-06 21:32:38 +08:00
nilaoda
1f57ba7c09 Update Parser.cs 2020-12-06 21:32:29 +08:00
nilaoda
71282bda30 Update N_m3u8DL-CLI.csproj 2020-12-02 20:35:44 +08:00
nilaoda
41ee8aebdf update project 2020-12-02 20:31:44 +08:00
nilaoda
a4537bc093 del xml 2020-12-02 20:23:50 +08:00
nilaoda
b8a60b3917 Update packages.config 2020-12-02 20:23:36 +08:00
nilaoda
8091dd290f Update N_m3u8DL-CLI.csproj 2020-12-02 20:15:29 +08:00
nilaoda
d48e84e611 Update FodyWeavers.xml 2020-12-02 20:10:29 +08:00
nilaoda
9f5423a437 Costura.Fody 2020-12-02 19:56:20 +08:00
nilaoda
ce7e38770a Update N_m3u8DL-CLI.csproj 2020-12-02 19:51:29 +08:00
nilaoda
8fdb2e918e Update packages.config 2020-12-02 19:49:10 +08:00
nilaoda
d4b7d240c1 修正多语言识别问题 2020-12-02 11:56:46 +08:00
nilaoda
484d2941ed Update Global.cs 2020-12-02 11:56:17 +08:00
nilaoda
a0f2b66575 Update changelog.txt 2020-12-02 11:56:04 +08:00
nilaoda
65cc0681e2 Update changelog.txt 2020-11-26 21:02:04 +08:00
nilaoda
7d980ec9a2 Update Global.cs 2020-11-26 21:01:57 +08:00
nilaoda
ec5892c05a 修复可能存在的溢出问题 2020-11-26 21:01:48 +08:00
nilaoda
9aed50fbf9 优化MPD识别 2020-11-26 21:01:03 +08:00
nilaoda
d657b455cd BUG FIX 2020-11-25 21:16:41 +08:00
nilaoda
cda6575605 BUG FIX 2020-11-25 21:16:18 +08:00
nilaoda
0d6377d41b Update changelog.txt 2020-11-25 17:34:53 +08:00
nilaoda
9c76bdcbce 支持选择音轨 2020-11-25 17:34:12 +08:00
nilaoda
83915ff606 Update Global.cs 2020-11-25 14:12:46 +08:00
nilaoda
9cd4746f33 Update changelog.txt 2020-11-25 14:11:31 +08:00
nilaoda
33a5b917ac 修正MPD判断最高清晰度的逻辑 2020-11-25 14:08:40 +08:00
nilaoda
f69978bd82 修改芒果TV请求头 2020-11-23 21:53:41 +08:00
nilaoda
d9fd526886 Update changelog.txt 2020-11-23 21:53:26 +08:00
nilaoda
1cc8ecfaaf Update Global.cs 2020-11-23 21:30:12 +08:00
nilaoda
2bc2dde2ad 修改默认UA 2020-11-23 21:29:48 +08:00
nilaoda
c3ddcf9e0e Update Program.cs 2020-11-23 21:29:31 +08:00
nilaoda
ba5d20dd02 Update Parser.cs 2020-11-23 21:29:03 +08:00
nilaoda
16f705fe66 Update Global.cs 2020-11-22 23:13:59 +08:00
nilaoda
d141cabc4a Update changelog.txt 2020-11-22 23:08:51 +08:00
nilaoda
adcf884a93 新的任务监控逻辑 2020-11-22 23:08:33 +08:00
nilaoda
9d903a025f Update DownloadManager.cs 2020-11-22 22:36:21 +08:00
nilaoda
0bd23ab641 更新版本号 2020-11-22 18:48:54 +08:00
nilaoda
a38f27ccd7 手动处理重定向
解决HTTPS协议自动重定向后,Referer丢失问题
2020-11-22 18:29:40 +08:00
nilaoda
3acec5efd3 Update Global.cs 2020-11-22 15:40:47 +08:00
nilaoda
90874e4bfe Update Parser.cs 2020-11-22 14:30:41 +08:00
nilaoda
b94768e3e8 修复自定义MPD的BaseURL 2020-11-22 00:40:17 +08:00
nilaoda
311f3b882e 更新版本号 2020-11-21 19:36:04 +08:00
nilaoda
4b4f537984 BUG FIX 2020-11-21 19:34:47 +08:00
nilaoda
8032d50b42 传递MPD_URL时处理302 2020-11-21 19:34:27 +08:00
nilaoda
f615764e55 Update build_latest.yml 2020-11-21 13:48:20 +08:00
nilaoda
ccaa200ef8 Update build_latest.yml 2020-11-21 13:45:56 +08:00
nilaoda
6058d878eb Update N_m3u8DL-CLI.csproj 2020-11-21 13:40:15 +08:00
nilaoda
c712c6dee0 Create changelog.txt 2020-11-21 13:30:56 +08:00
nilaoda
bb24bb998f Update Global.cs 2020-11-21 13:11:15 +08:00
nilaoda
e9d951efa5 检测GIF HEADER 2020-11-21 12:25:01 +08:00
nilaoda
6f88a805ef 修复PNG检测逻辑
多写了一个分号……
2020-11-21 12:08:28 +08:00
nilaoda
6aa6d63a8d Convert MPD to M3U8
通过将MPD转换为m3u8进行下载
2020-11-20 23:34:35 +08:00
nilaoda
147246caba Update Parser.cs 2020-11-18 16:05:18 +08:00
nilaoda
35c1ee4777 Update Parser.cs 2020-11-18 15:52:36 +08:00
nilaoda
8b32081b85 识别m3u8文件中的EXT-X-PROGRAM-DATE-TIME 2020-11-18 15:33:39 +08:00
nilaoda
c00de328d1 修改默认UA 修改音轨判断逻辑
修改UA为Mozilla/5.0 (Linux; U; Android 7.0; zh-cn; 15 Plus Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.4 Mobile Safari/537.36

修改AAC滤镜的使用逻辑

当m3u8文本大小大于50MB时应当放弃
2020-11-18 15:03:21 +08:00
nilaoda
d5193c1645 fix bug 2020-11-06 22:11:21 +08:00
nilaoda
c06fbf5820 update docs 2020-11-03 11:38:24 +08:00
nilaoda
e700edba56 Update Program.cs
修正处理文件名过长的逻辑
2020-10-14 22:18:56 +08:00
nilaoda
aad948da7c v2.7.5 2020-10-14 22:01:03 +08:00
nilaoda
14f7b20176 Merge pull request #246 from Suwmlee/master
Fix build error
2020-09-22 10:19:45 +08:00
Mathhew
a66a9a4096 Fix build error 2020-09-22 09:49:58 +08:00
nilaoda
c862f23a9c v2.7.4
支持ddyun m3u8解密
2020-09-20 13:41:49 +08:00
nilaoda
1a722e80de Delete DecryptNfmovies.cs 2020-09-20 13:41:02 +08:00
nilaoda
eff43e8ac3 Merge pull request #240 from Suwmlee/master
Add github action
2020-09-17 18:58:30 +08:00
Mathhew
7648f8f8dc Add github action 2020-09-17 10:05:35 +08:00
nilaoda
1edc1a43fe v2.7.3 2020-09-14 21:53:51 +08:00
nilaoda
01e7735018 v2.7.2 2020-08-09 20:59:38 +08:00
nilaoda
a0c41d6116 v2.7.2 2020-08-09 20:34:32 +08:00
nilaoda
c3c25774de v2.7.2 2020-08-09 20:33:15 +08:00
nilaoda
ba40641a21 v2.7.1 2020-07-19 09:24:24 +08:00
nilaoda
5d76418780 v2.7.0
优酷杜比视界下载逻辑优化;支持IMOCO m3u8/key解密;从当前路径和exe路径同时寻找ffmpeg;支持多语言本地化(简繁英)
2020-07-18 17:20:28 +08:00
nilaoda
fd48b766b5 v2.6.3 2020-04-17 22:54:57 +08:00
nilaoda
0f25cc0ae8 v2.6.2 2020-04-17 20:17:56 +08:00
nilaoda
1c6bd688e3 Merge branch 'master' of https://github.com/nilaoda/N_m3u8DL-CLI 2020-04-17 19:25:37 +08:00
nilaoda
480857cc3b v2.6.1 2020-04-17 19:25:32 +08:00
nilaoda
52af9a44a8 Update README.md 2020-03-12 14:39:29 +08:00
nilaoda
84d137b504 Update README.md 2020-03-12 00:50:16 +08:00
nilaoda
a4f1064c81 Update README.md 2020-03-11 21:21:25 +08:00
nilaoda
eac08b12f8 v2.6.0 2020-03-11 18:01:33 +08:00
nilaoda
c81c911888 Update Docs 2020-03-11 17:58:03 +08:00
nilaoda
361e901e5b Update Docs 2020-03-11 17:55:47 +08:00
nilaoda
3567f4c4cc Update README_ENG.md 2020-03-11 17:42:10 +08:00
nilaoda
2f7b0f7304 Update README_ENG.md 2020-03-11 17:41:56 +08:00
nilaoda
000d0db4ae Update README.md 2020-03-11 17:41:42 +08:00
nilaoda
d551ad52c1 Update README.md 2020-03-11 17:01:56 +08:00
nilaoda
c064c83c21 Update README.md 2020-03-11 15:14:06 +08:00
nilaoda
f6ed0f9e4d Update youku.html 2020-03-11 11:26:59 +08:00
nilaoda
f65fee94ca Update Docs 2020-03-08 11:01:13 +08:00
nilaoda
ce64a92b0d Update README.md 2020-03-06 01:03:30 +08:00
nilaoda
42790ce540 Update README.md 2020-03-06 01:00:50 +08:00
nilaoda
83d8ca1c8c Update README.md 2020-03-06 00:58:20 +08:00
nilaoda
4b5a64eb98 v2.5.7 2020-03-05 20:05:15 +08:00
nilaoda
2b5af09c3b Update HLSLiveDownloader.cs 2020-03-05 14:46:03 +08:00
nilaoda
6368adc2ab Update HLSLiveDownloader.cs 2020-03-04 21:09:41 +08:00
nilaoda
fcd7840091 只认第一个"#EXT-X-MAP", 其余的全部丢弃 2020-03-04 02:13:02 +08:00
nilaoda
d77cb62dff Update HLSLiveDownloader.cs 2020-03-03 19:03:42 +08:00
nilaoda
34394c6a2b 修复输出太长只在最后一行显示的问题 2020-03-03 19:03:24 +08:00
nilaoda
091cba8555 Merge pull request #101 from youxia2016/master
Update Downloader.cs
2020-03-02 22:09:15 +08:00
游侠
b02b6b7168 Update Downloader.cs
跳过过期片段
2020-03-02 21:56:01 +08:00
nilaoda
b7408b0599 v2.5.6 2020-03-02 20:20:38 +08:00
nilaoda
b83cb35170 Merge pull request #100 from youxia2016/master
自动设置请求分段文件时间间隔
修复网络断线一直Downloading及cpu 100%
2020-03-02 20:19:20 +08:00
游侠
ed0a7b71a7 自动设置请求分段文件时间间隔
时间间隔一般为9-18秒,同一直播平台也不相同,自适应间隔,可以防止多次请求
2020-03-02 19:39:44 +08:00
游侠
50eae19bf3 修复网络掉线cpu 100%以及下载超时时间
网络如果突然断掉cpu会100% 风扇狂转
2020-03-02 18:35:28 +08:00
nilaoda
cba8f3ea52 v2.5.5 2020-03-02 17:14:15 +08:00
nilaoda
7fd93e1232 Merge pull request #98 from youxia2016/master
请求失败重试
2020-03-02 16:48:32 +08:00
游侠
74a7e3c3ec 加入savename参数可读取N_m3u8DL-CLI.args.txt
输入网址及保存文件名 更加人性化
2020-03-02 16:48:28 +08:00
nilaoda
849d712e11 Update README.md 2020-03-02 16:42:12 +08:00
游侠
7544f3a02c 添加Http重试次数
进行重试 可更稳定
2020-03-02 16:39:28 +08:00
nilaoda
0120736c53 Update README_ENG.md 2020-03-01 12:17:27 +08:00
nilaoda
e4bde4926c Update README.md 2020-03-01 12:17:08 +08:00
nilaoda
d42cd6a60d v2.5.4 2020-02-28 18:27:55 +08:00
nilaoda
175f13adc9 v2.5.3 2020-02-27 20:36:25 +08:00
nilaoda
72f1c043b1 腾讯视频DRM内容m3u8获取JS 2020-02-27 14:29:55 +08:00
nilaoda
a2e2070f5d update docs 2020-02-25 22:14:55 +08:00
nilaoda
6c96deb366 Update Downloader.cs 2020-02-25 01:10:38 +08:00
nilaoda
2bd900ee5d v2.5.2 2020-02-25 01:01:15 +08:00
nilaoda
1261810510 Update Downloader.cs 2020-02-24 17:05:01 +08:00
nilaoda
26823dbd7e Update docs 2020-02-24 16:29:18 +08:00
nilaoda
698699d9fc Update README.md 2020-02-24 16:25:13 +08:00
nilaoda
ebed7fa1e3 优化直播录制 2020-02-24 16:23:40 +08:00
nilaoda
73a8348155 v2.5.0 2020-02-23 21:07:36 +08:00
nilaoda
5ace0b3a4f 增加优酷教程 2020-02-23 20:23:23 +08:00
nilaoda
5abe889da0 Update README_ENG.md 2020-02-23 20:22:18 +08:00
nilaoda
f1070fd1b4 Update README.md 2020-02-23 20:13:23 +08:00
nilaoda
4af82cc7f9 v2.4.9 2020-02-18 15:20:04 +08:00
nilaoda
809380b7ab 修正VIKI下载JS 2020-02-11 23:59:37 +08:00
nilaoda
ab57420507 增加基于Firefox的网易云课堂KEY获取教程 2020-02-02 01:23:52 +08:00
nilaoda
80230f12fe 增加网易云课堂KEY获取教程 2020-02-02 00:43:55 +08:00
nilaoda
a6c7c0fd8c ?__gda__行为优化 2020-01-31 21:39:05 +08:00
nilaoda
bd6df6b58c JS增加naver tv 2020-01-31 21:38:27 +08:00
nilaoda
e0a9071d62 修改超链接错误 2020-01-30 19:16:11 +08:00
nilaoda
5c9bcf72d2 更新OnDemandChina获取JS 2020-01-29 23:48:15 +08:00
nilaoda
7cf2c12d0c 增加OnDemandChina获取JS 2020-01-29 23:41:03 +08:00
nilaoda
1b35fe2d2c Update DownloadManager.cs 2020-01-29 22:45:26 +08:00
nilaoda
ed3aae1cb9 v2.4.7 2020-01-29 22:28:07 +08:00
nilaoda
01c2ecbeb5 增加爱奇艺字幕JS 2020-01-28 23:48:35 +08:00
nilaoda
9993ec8177 添加获取爱奇艺高帧率视频JS 2020-01-09 23:01:20 +08:00
nilaoda
464300c860 Update N_m3u8DL-CLI.sln 2020-01-07 21:22:43 +08:00
nilaoda
45fa58a46f Update README.md 2020-01-06 09:44:09 +08:00
nilaoda
f93ddc7107 Update README.md 2019-12-30 16:38:08 +08:00
nilaoda
05f450fa6d 永远信任https 2019-12-28 23:45:31 +08:00
nilaoda
580c374d7b 更新腾讯视频代码 2019-12-28 14:17:43 +08:00
nilaoda
b58dc5d8e0 Update README_ENG.md 2019-12-26 23:29:03 +08:00
nilaoda
f50cf7862a Add link 2019-12-23 17:16:28 +08:00
nilaoda
fdad68d483 Create README_ENG.md 2019-12-23 17:04:46 +08:00
nilaoda
d7aaa5323b 修复腾讯视频HDR10下载问题 2019-12-18 23:04:05 +08:00
nilaoda
6c2e13b800 v2.4.4
修复part大于1时读取json混流文件的严重错误;自动去除优酷的广告分片及前情提要
2019-12-18 19:49:58 +08:00
nilaoda
086fc57958 修复解析bug;增加杜比视界识别场景 2019-12-18 09:15:42 +08:00
nilaoda
bc349b8977 文件名特殊字符处理 2019-12-16 22:16:54 +08:00
nilaoda
cc4efed3c6 完善芒果TV请求头的自动添加 2019-11-30 00:43:00 +08:00
nilaoda
cf958e833b fix doc 2019-11-15 23:55:30 +08:00
nilaoda
fb09add0cd update docs 2019-11-15 10:49:07 +08:00
nilaoda
5a3c5baefd 更新JS 2019-11-13 23:35:04 +08:00
nilaoda
839afd8e61 Update N_m3u8DL-CLI.csproj 2019-11-08 20:38:25 +08:00
nilaoda
338c7a25d0 更新文档 2019-10-24 15:40:28 +08:00
nilaoda
f57ce8c2da Update README.md 2019-10-24 15:32:14 +08:00
nilaoda
a5009e1683 增加不检测完整性及自定义KEY 2019-10-24 14:44:09 +08:00
nilaoda
66933da9de Update 2019-10-19 21:02:50 +08:00
nilaoda
136389e248 去掉了针对优酷链接的默认行为 2019-10-18 00:16:57 +08:00
nilaoda
7971104bd4 更新优酷DRM视频临时解决方法 2019-10-16 14:35:57 +08:00
nilaoda
cc4941554d Create N_m3u8DL-CLI.sln 2019-10-09 22:03:15 +08:00
nilaoda
c50371080a Update README.md 2019-10-09 21:59:22 +08:00
nilaoda
154c5f84a1 开放源代码 2019-10-09 21:52:07 +08:00
nilaoda
1556ee42df Update doc. 2019-10-05 11:28:21 +08:00
nilaoda
121ff9f226 Update README.md 2019-09-22 20:42:30 +08:00
nilaoda
e5b84c2f04 viki parser 2019-09-21 22:15:27 +08:00
nilaoda
7e562a06bb Add VIKI (JS) 2019-09-21 01:01:37 +08:00
nilaoda
1eae19c3ea Merge branch 'master' of https://github.com/nilaoda/N_m3u8DL-CLI 2019-09-18 19:32:46 +08:00
nilaoda
ba33049b99 Update README.md 2019-09-18 19:32:10 +08:00
nilaoda
9773087bf9 Update docs 2019-09-18 19:31:08 +08:00
nilaoda
b60d39ad0d Update README.md 2019-09-16 22:15:46 +08:00
nilaoda
e89705a370 Update README.md 2019-09-10 22:27:26 +08:00
69 changed files with 8931 additions and 1073 deletions

63
.gitattributes vendored Normal file
View File

@@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

34
.github/workflows/build_latest.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Build_Latest
on: [push]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v1
name: Checkout Code
- name: Setup MSBuild Path
uses: warrenbuckley/Setup-MSBuild@v1
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
- name: Setup NuGet
uses: NuGet/setup-nuget@v1.0.2
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: 'true'
- name: Restore NuGet Packages
run: nuget restore N_m3u8DL-CLI.sln
- name: Build
run: msbuild N_m3u8DL-CLI.sln /p:Configuration=Release /p:DebugSymbols=false /p:DebugType=None
- name: Upload Artifact
uses: actions/upload-artifact@v1.0.0
with:
name: N_m3u8DL-CLI_latest
path: N_m3u8DL-CLI\bin\Release\N_m3u8DL-CLI.exe

261
.gitignore vendored Normal file

File diff suppressed because it is too large Load Diff

25
N_m3u8DL-CLI.sln Normal file
View File

@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29215.179
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "N_m3u8DL-CLI", "N_m3u8DL-CLI\N_m3u8DL-CLI.csproj", "{4FB61439-B738-46AC-B3AF-2BF72150D057}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FB61439-B738-46AC-B3AF-2BF72150D057}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {82B9270D-B7B2-4591-BF8A-5B4EBCD0EA8A}
EndGlobalSection
EndGlobal

6
N_m3u8DL-CLI/App.config Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6"/></startup>
</configuration>

View File

@@ -0,0 +1,80 @@
using System.Collections.Generic;
namespace N_m3u8DL_CLI
{
/**
* https://www.cnblogs.com/linxuanchen/p/c-sharp-command-line-argument-parser.html
*/
public class CommandLineArgument
{
List<CommandLineArgument> _arguments;
int _index;
string _argumentText;
public CommandLineArgument Next
{
get
{
if (_index < _arguments.Count - 1)
{
return _arguments[_index + 1];
}
return null;
}
}
public CommandLineArgument Previous
{
get
{
if (_index > 0)
{
return _arguments[_index - 1];
}
return null;
}
}
internal CommandLineArgument(List<CommandLineArgument> args, int index, string argument)
{
_arguments = args;
_index = index;
_argumentText = argument;
}
public CommandLineArgument Take()
{
return Next;
}
public IEnumerable<CommandLineArgument> Take(int count)
{
var list = new List<CommandLineArgument>();
var parent = this;
for (int i = 0; i < count; i++)
{
var next = parent.Next;
if (next == null)
break;
list.Add(next);
parent = next;
}
return list;
}
public static implicit operator string(CommandLineArgument argument)
{
return argument._argumentText;
}
public override string ToString()
{
return _argumentText;
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
public class CommandLineArgumentParser
{
List<CommandLineArgument> _arguments;
public static CommandLineArgumentParser Parse(string[] args)
{
return new CommandLineArgumentParser(args);
}
public CommandLineArgumentParser(string[] args)
{
_arguments = new List<CommandLineArgument>();
for (int i = 0; i < args.Length; i++)
{
_arguments.Add(new CommandLineArgument(_arguments, i, args[i]));
}
}
public CommandLineArgument Get(string argumentName)
{
return _arguments.FirstOrDefault(p => p == argumentName);
}
public bool Has(string argumentName)
{
return _arguments.Count(p => p == argumentName) > 0;
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,41 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace N_m3u8DL_CLI
{
class DecodeDdyun
{
public static string DecryptM3u8(byte[] byteArray)
{
string tmp = DecodeNfmovies.DecryptM3u8(byteArray);
if (tmp.StartsWith("duoduo.key"))
{
tmp = Regex.Replace(tmp, @"#EXT-X-BYTERANGE:.*\s", "");
tmp = tmp.Replace("https:", "jump/https:")
.Replace("inews.gtimg.com", "puui.qpic.cn");
}
return tmp;
}
//https://player.ddyunp.com/jQuery.min.js?v1.5
public static string GetVaildM3u8Url(string url)
{
//url: https://hls.ddyunp.com/ddyun/id/1/key/playlist.m3u8
string id = Regex.Match(url, @"\w{20,}").Value;
string tm = Global.GetTimeStamp(false);
string t = ((long.Parse(tm) / 0x186a0) * 0x64).ToString();
string tmp = id + "duoduo" + "1" + t;
MD5 md5 = MD5.Create();
byte[] bs = Encoding.UTF8.GetBytes(tmp);
byte[] hs = md5.ComputeHash(bs);
StringBuilder sb = new StringBuilder();
foreach (byte b in hs)
{
sb.Append(b.ToString("x2"));
}
string key = sb.ToString();
return Regex.Replace(url, @"1/\w{20,}", "1/" + key);
}
}
}

View File

@@ -0,0 +1,53 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace N_m3u8DL_CLI
{
//https://js.huke88.com/assets/revision/js/plugins/tcplayer/tcplayer.v4.1.min.js?v=930
//https://js.huke88.com/assets/revision/js/plugins/tcplayer/libs/hls.min.0.13.2m.js?v=930
class DecodeHuke88Key
{
private static string[] GetOverlayInfo(string url)
{
var enc = new Regex("eyJ\\w{100,}").Match(url).Value;
var json = Encoding.UTF8.GetString(Convert.FromBase64String(enc));
JObject jObject = JObject.Parse(json);
var key = jObject["overlayKey"].ToString();
var iv = jObject["overlayIv"].ToString();
return new string[] { key, iv };
}
public static string DecodeKey(string url, byte[] data)
{
var info = GetOverlayInfo(url);
var overlayKey = info[0];
var overlayIv = info[1];
var l = new List<byte>();
var c = new List<byte>();
for (int h = 0; h < 16; h++)
{
var f = overlayKey.Substring(2 * h, 2);
var g = overlayIv.Substring(2 * h, 2);
l.Add(Convert.ToByte(f, 16));
c.Add(Convert.ToByte(g, 16));
}
var _lastCipherblock = c.ToArray();
var t = new byte[data.Length];
var r = data;
r = Decrypter.AES128Decrypt(data, l.ToArray(), Decrypter.HexStringToBytes("00000000000000000000000000000000"), CipherMode.CBC, PaddingMode.Zeros);
for (var o = 0; o < 16; o++)
t[o] = (byte)(r[o] ^ _lastCipherblock[o]);
var key = Convert.ToBase64String(t);
return key;
}
}
}

203
N_m3u8DL-CLI/DecodeImooc.cs Normal file
View File

@@ -0,0 +1,203 @@
using NiL.JS.BaseLibrary;
using NiL.JS.Core;
using NiL.JS.Extensions;
using System;
using Array = System.Array;
namespace N_m3u8DL_CLI
{
/*
* js代码来自https://www.imooc.com/static/moco/player/3.0.6.3/mocoplayer.js?v=202006122046
*
*/
class DecodeImooc
{
private static string JS = @"
function n(t, e) {
function r(t, e) {
var r = '';
if ('object' == typeof t)
for (var n = 0; n < t.length; n++)
r += String.fromCharCode(t[n]);
t = r || t;
for (var i, o, a = new Uint8Array(t.length), s = e.length, n = 0; n < t.length; n++)
o = n % s,
i = t[n],
i = i.toString().charCodeAt(0),
a[n] = i ^ e.charCodeAt(o);
return a
}
function n(t) {
var e = '';
if ('object' == typeof t)
for (var r = 0; r < t.length; r++)
e += String.fromCharCode(t[r]);
t = e || t;
var n = new Uint8Array(t.length);
for (r = 0; r < t.length; r++)
n[r] = t[r].toString().charCodeAt(0);
var i, o, r = 0;
for (r = 0; r < n.length; r++)
0 != (i = n[r] % 3) && r + i < n.length && (o = n[r + 1],
n[r + 1] = n[r + i],
n[r + i] = o,
r = r + i + 1);
return n
}
function i(t) {
var e = '';
if ('object' == typeof t)
for (var r = 0; r < t.length; r++)
e += String.fromCharCode(t[r]);
t = e || t;
var n = new Uint8Array(t.length);
for (r = 0; r < t.length; r++)
n[r] = t[r].toString().charCodeAt(0);
var r = 0
, i = 0
, o = 0
, a = 0;
for (r = 0; r < n.length; r++)
o = n[r] % 2,
o && r++,
a++;
var s = new Uint8Array(a);
for (r = 0; r < n.length; r++)
o = n[r] % 2,
s[i++] = o ? n[r++] : n[r];
return s
}
function o(t, e) {
var r = 0
, n = 0
, i = 0
, o = 0
, a = '';
if ('object' == typeof t)
for (var r = 0; r < t.length; r++)
a += String.fromCharCode(t[r]);
t = a || t;
var s = new Uint8Array(t.length);
for (r = 0; r < t.length; r++)
s[r] = t[r].toString().charCodeAt(0);
for (r = 0; r < t.length; r++)
if (0 != (o = s[r] % 5) && 1 != o && r + o < s.length && (i = s[r + 1],
n = r + 2,
s[r + 1] = s[r + o],
s[o + r] = i,
(r = r + o + 1) - 2 > n))
for (; n < r - 2; n++)
s[n] = s[n] ^ e.charCodeAt(n % e.length);
for (r = 0; r < t.length; r++)
s[r] = s[r] ^ e.charCodeAt(r % e.length);
return s
}
for (var a = {
data: {
info: t
}
}, s = {
q: r,
h: n,
m: i,
k: o
}, l = a.data.info, u = l.substring(l.length - 4).split(''), c = 0; c < u.length; c++)
u[c] = u[c].toString().charCodeAt(0) % 4;
u.reverse();
for (var d = [], c = 0; c < u.length; c++)
d.push(l.substring(u[c] + 1, u[c] + 2)),
l = l.substring(0, u[c] + 1) + l.substring(u[c] + 2);
a.data.encrypt_table = d,
a.data.key_table = [];
for (var c in a.data.encrypt_table)
'q' != a.data.encrypt_table[c] && 'k' != a.data.encrypt_table[c] || (a.data.key_table.push(l.substring(l.length - 12)),
l = l.substring(0, l.length - 12));
a.data.key_table.reverse(),
a.data.info = l;
var f = new Array(-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1);
a.data.info = function(t) {
var e, r, n, i, o, a, s;
for (a = t.length,
o = 0,
s = ''; o < a; ) {
do {
e = f[255 & t.charCodeAt(o++)]
} while (o < a && -1 == e);if (-1 == e)
break;
do {
r = f[255 & t.charCodeAt(o++)]
} while (o < a && -1 == r);if (-1 == r)
break;
s += String.fromCharCode(e << 2 | (48 & r) >> 4);
do {
if (61 == (n = 255 & t.charCodeAt(o++)))
return s;
n = f[n]
} while (o < a && -1 == n);if (-1 == n)
break;
s += String.fromCharCode((15 & r) << 4 | (60 & n) >> 2);
do {
if (61 == (i = 255 & t.charCodeAt(o++)))
return s;
i = f[i]
} while (o < a && -1 == i);if (-1 == i)
break;
s += String.fromCharCode((3 & n) << 6 | i)
}
return s
}(a.data.info);
for (var c in a.data.encrypt_table) {
var h = a.data.encrypt_table[c];
if ('q' == h || 'k' == h) {
var p = a.data.key_table.pop();
a.data.info = s[a.data.encrypt_table[c]](a.data.info, p)
} else
a.data.info = s[a.data.encrypt_table[c]](a.data.info)
}
if (e)
return a.data.info;
var g = '';
for (c = 0; c < a.data.info.length; c++)
g += String.fromCharCode(a.data.info[c]);
return g
}
function Uint8ArrayToString(fileData){
var dataString = '';
for (var i = 0; i < fileData.length; i++) {
dataString += Number(fileData[i]) + ',';
}
return dataString;
}
function decodeKey(resp){
var string = eval('('+resp+')');
//return btoa(String.fromCharCode.apply(null, new Uint8Array(n(string.data.info, 1))));
return Uint8ArrayToString(new Uint8Array(n(string.data.info, 1)));
}
function decodeM3u8(resp){
var string = eval('('+resp+')');
return n(string.data.info);
}
";
public static string DecodeM3u8(string resp)
{
var context = new Context();
context.Eval(JS);
var concatFunction = context.GetVariable("decodeM3u8").As<Function>();
string m3u8 = concatFunction.Call(new Arguments { resp }).ToString();
return m3u8;
}
public static string DecodeKey(string resp)
{
var context = new Context();
context.Eval(JS);
var concatFunction = context.GetVariable("decodeKey").As<Function>();
string key = concatFunction.Call(new Arguments { resp }).ToString();
byte[] v = Array.ConvertAll(key.Trim(',').Split(','), s => (byte)int.Parse(s));
string realKey = Convert.ToBase64String(v);
return realKey;
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
namespace N_m3u8DL_CLI
{
class DecodeNfmovies
{
//https://jx.nfmovies.com/hls.min.js
public static string DecryptM3u8(byte[] byteArray)
{
var t = byteArray;
var decrypt = "";
if (137 == t[0] && 80 == t[1] && 130 == t[354] && 96 == t[353]) t = t.Skip(355).ToArray();
else
{
if (137 != t[0] || 80 != t[1] || 130 != t[394] || 96 != t[393])
{
for (var i = 0; i < t.Length; i++) decrypt += Convert.ToChar(t[i]);
return decrypt;
}
t = t.Skip(395).ToArray();
}
using (var zipStream =
new System.IO.Compression.GZipStream(new MemoryStream(t), System.IO.Compression.CompressionMode.Decompress))
{
using (StreamReader sr = new StreamReader(zipStream, Encoding.UTF8))
{
decrypt = sr.ReadToEnd();
}
}
return decrypt;
}
}
}

78
N_m3u8DL-CLI/Decrypter.cs Normal file
View File

@@ -0,0 +1,78 @@
using System;
using System.IO;
using System.Security.Cryptography;
namespace N_m3u8DL_CLI
{
class Decrypter
{
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
FileStream fs = new FileStream(filePath, FileMode.Open);
//获取文件大小
long size = fs.Length;
byte[] inBuff = new byte[size];
fs.Read(inBuff, 0, inBuff.Length);
fs.Close();
Aes dcpt = Aes.Create();
dcpt.BlockSize = 128;
dcpt.KeySize = 128;
dcpt.Key = keyByte;
dcpt.IV = ivByte;
dcpt.Mode = mode;
dcpt.Padding = padding;
ICryptoTransform cTransform = dcpt.CreateDecryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
return resultArray;
}
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
byte[] inBuff = encryptedBuff;
Aes dcpt = Aes.Create();
dcpt.BlockSize = 128;
dcpt.KeySize = 128;
dcpt.Key = keyByte;
dcpt.IV = ivByte;
dcpt.Mode = mode;
dcpt.Padding = padding;
ICryptoTransform cTransform = dcpt.CreateDecryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
return resultArray;
}
public static byte[] HexStringToBytes(string hexStr)
{
if (string.IsNullOrEmpty(hexStr))
{
return new byte[0];
}
if (hexStr.StartsWith("0x") || hexStr.StartsWith("0X"))
{
hexStr = hexStr.Remove(0, 2);
}
int count = hexStr.Length;
if (count % 2 == 1)
{
throw new ArgumentException("Invalid length of bytes:" + count);
}
int byteCount = count / 2;
byte[] result = new byte[byteCount];
for (int ii = 0; ii < byteCount; ++ii)
{
var tempBytes = Byte.Parse(hexStr.Substring(2 * ii, 2), System.Globalization.NumberStyles.HexNumber);
result[ii] = tempBytes;
}
return result;
}
}
}

File diff suppressed because it is too large Load Diff

252
N_m3u8DL-CLI/Downloader.cs Normal file
View File

@@ -0,0 +1,252 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class Downloader
{
private int timeOut = 0;
private int retry = 5;
private int count = 0;
private int segIndex = 0;
private double segDur = 0;
private string fileUrl = string.Empty;
private string savePath = string.Empty;
private string headers = string.Empty;
private string method = string.Empty;
private string key = string.Empty;
private string iv = string.Empty;
private string liveFile = string.Empty;
private long expectByte = -1;
private long startByte = 0;
private bool isLive = false;
private bool isDone = false;
private bool firstSeg = true;
private FileStream liveStream = null;
public string FileUrl { get => fileUrl; set => fileUrl = value; }
public string SavePath { get => savePath; set => savePath = value; }
public string Headers { get => headers; set => headers = value; }
public string Method { get => method; set => method = value; }
public string Key { get => key; set => key = value; }
public string Iv { get => iv; set => iv = value; }
public bool IsLive { get => isLive; set => isLive = value; }
public int Retry { get => retry; set => retry = value; }
public bool IsDone { get => isDone; set => isDone = value; }
public int SegIndex { get => segIndex; set => segIndex = value; }
public int TimeOut { get => timeOut; set => timeOut = value; }
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
public string LiveFile { get => liveFile; set => liveFile = value; }
public long ExpectByte { get => expectByte; set => expectByte = value; }
public long StartByte { get => startByte; set => startByte = value; }
public double SegDur { get => segDur; set => segDur = value; }
//重写WebClinet
//private class WebClient : System.Net.WebClient
//{
// protected override WebRequest GetWebRequest(Uri uri)
// {
// WebRequest lWebRequest = base.GetWebRequest(uri);
// lWebRequest.Timeout = TimeOut;
// ((HttpWebRequest)lWebRequest).ReadWriteTimeout = TimeOut;
// return lWebRequest;
// }
//}
//WebClient client = new WebClient();
public void Down()
{
try
{
//直播下载
if (IsLive)
{
IsDone = false; //设置为未完成下载
if (Method == "NONE" || method.Contains("NOTSUPPORTED"))
{
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
LOGGER.WriteLine("<" + SegIndex + " Downloading>");
byte[] segBuff = Global.HttpDownloadFileToBytes(fileUrl, Headers, TimeOut);
//byte[] segBuff = Global.WebClientDownloadToBytes(fileUrl, Headers);
Global.AppendBytesToFileStreamAndDoNotClose(LiveStream, segBuff);
LOGGER.PrintLine("<" + SegIndex + " Complete>\r\n");
LOGGER.WriteLine("<" + SegIndex + " Complete>");
IsDone = true;
}
else if (Method == "AES-128")
{
LOGGER.PrintLine("<" + SegIndex + " Downloading>");
LOGGER.WriteLine("<" + SegIndex + " Downloading>");
byte[] encryptedBuff = Global.HttpDownloadFileToBytes(fileUrl, Headers, TimeOut);
//byte[] encryptedBuff = Global.WebClientDownloadToBytes(fileUrl, Headers);
byte[] decryptBuff = null;
decryptBuff = Decrypter.AES128Decrypt(
encryptedBuff,
Convert.FromBase64String(Key),
Decrypter.HexStringToBytes(Iv)
);
Global.AppendBytesToFileStreamAndDoNotClose(LiveStream, decryptBuff);
LOGGER.PrintLine("<" + SegIndex + " Complete>\r\n");
LOGGER.WriteLine("<" + SegIndex + " Complete>");
IsDone = true;
}
else
{
//LOGGER.PrintLine("不支持这种加密方式!", LOGGER.Error);
IsDone = true;
}
if (firstSeg && Global.FileSize(LiveFile) != 0)
{
//LOGGER.STOPLOG = false; //记录日志
foreach (string ss in (string[])Global.GetVideoInfo(LiveFile).ToArray(typeof(string)))
{
LOGGER.WriteLine(ss.Trim());
}
firstSeg = false;
//LOGGER.STOPLOG = true; //停止记录日志
}
HLSLiveDownloader.REC_DUR += SegDur;
if (HLSLiveDownloader.REC_DUR_LIMIT != -1 && HLSLiveDownloader.REC_DUR >= HLSLiveDownloader.REC_DUR_LIMIT)
{
LOGGER.PrintLine(strings.recordLimitReached, LOGGER.Warning);
LOGGER.WriteLine(strings.recordLimitReached);
Environment.Exit(0); //正常退出
}
return;
}
//点播下载
else
{
if (!Directory.Exists(Path.GetDirectoryName(SavePath)))
Directory.CreateDirectory(Path.GetDirectoryName(SavePath)); //新建文件夹
//是否存在文件,存在则不下载
if (File.Exists(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts"))
{
Global.BYTEDOWN++; //防止被速度监控程序杀死
//Console.WriteLine("Exists " + Path.GetFileNameWithoutExtension(savePath) + ".ts");
return;
}
//Console.WriteLine("开始下载 " + fileUrl);
//本地文件
if (fileUrl.StartsWith("file:"))
{
Uri t = new Uri(fileUrl);
fileUrl = t.LocalPath;
if (File.Exists(fileUrl))
{
if (ExpectByte == -1) //没有RANGE
{
FileInfo fi = new FileInfo(fileUrl);
fi.CopyTo(savePath);
Global.BYTEDOWN += fi.Length;
}
else
{
FileStream stream = new FileInfo(fileUrl).OpenRead();
//seek文件
stream.Seek(StartByte, SeekOrigin.Begin);
Byte[] buffer = new Byte[ExpectByte];
//从流中读取字节块并将该数据写入给定缓冲区buffer中
stream.Read(buffer, 0, Convert.ToInt32(buffer.Length));
stream.Close();
//写出文件
MemoryStream m = new MemoryStream(buffer);
FileStream fs = new FileStream(savePath, FileMode.OpenOrCreate);
m.WriteTo(fs);
m.Close();
fs.Close();
m = null;
fs = null;
}
}
}
else
{
//下载
Global.HttpDownloadFile(fileUrl, savePath, TimeOut, Headers, StartByte, ExpectByte);
}
}
if (File.Exists(savePath) && Global.ShouldStop == false)
{
FileInfo fi = new FileInfo(savePath);
if (Method == "NONE" || method.Contains("NOTSUPPORTED"))
{
fi.MoveTo(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts");
DownloadManager.DownloadedSize += fi.Length;
//Console.WriteLine(Path.GetFileNameWithoutExtension(savePath) + " Completed.");
}
else if (File.Exists(fi.FullName)
&& Method == "AES-128")
{
//解密
try
{
byte[] decryptBuff = null;
if(fileUrl.Contains(".51cto.com/")) //使用AES-128-ECB模式解密
{
decryptBuff = Decrypter.AES128Decrypt(
fi.FullName,
Convert.FromBase64String(Key),
Decrypter.HexStringToBytes(Iv),
System.Security.Cryptography.CipherMode.ECB
);
}
else
{
decryptBuff = Decrypter.AES128Decrypt(
fi.FullName,
Convert.FromBase64String(Key),
Decrypter.HexStringToBytes(Iv)
);
}
FileStream fs = new FileStream(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts", FileMode.Create);
fs.Write(decryptBuff, 0, decryptBuff.Length);
fs.Close();
DownloadManager.DownloadedSize += fi.Length;
fi.Delete();
//Console.WriteLine(Path.GetFileNameWithoutExtension(savePath) + " Completed & Decrypted.");
}
catch (Exception ex)
{
LOGGER.PrintLine(ex.Message, LOGGER.Error);
LOGGER.WriteLineError(ex.Message);
Thread.Sleep(3000);
Environment.Exit(-1);
}
}
else
{
LOGGER.WriteLineError(strings.SomethingWasWrong);
LOGGER.PrintLine(strings.SomethingWasWrong, LOGGER.Error);
return;
}
return;
}
}
catch (Exception ex)
{
LOGGER.WriteLineError(ex.Message);
if (ex.Message.Contains("404") || ex.Message.Contains("400"))//(400) 错误的请求,片段过期会提示400错误
{
IsDone = true;
return;
}
else if (IsLive && count++ < Retry)
{
Thread.Sleep(2000);//直播一般3-6秒一个片段
Down();
}
}
}
}
}

177
N_m3u8DL-CLI/FFmpeg.cs Normal file
View File

@@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class FFmpeg
{
public static string FFMPEG_PATH = "ffmpeg";
public static string REC_TIME = ""; //录制日期
public static string OutPutPath { get; set; } = string.Empty;
public static string ReportFile { get; set; } = string.Empty;
public static bool UseAACFilter { get; set; } = false; //是否启用滤镜
public static bool WriteDate { get; set; } = true; //是否写入录制日期
public static void Merge(string[] files, string muxFormat, bool fastStart,
string poster = "", string audioName = "", string title = "",
string copyright = "", string comment = "", string encodingTool = "")
{
string dateString = string.IsNullOrEmpty(REC_TIME) ? DateTime.Now.ToString("o") : REC_TIME;
//同名文件已存在的共存策略
if (File.Exists($"{OutPutPath}.{muxFormat.ToLower()}"))
{
OutPutPath = Path.Combine(Path.GetDirectoryName(OutPutPath),
Path.GetFileName(OutPutPath) + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss"));
}
string command = "-loglevel warning -i concat:\"";
string data = string.Empty;
string ddpAudio = string.Empty;
string addPoster = "-map 1 -c:v:1 copy -disposition:v:1 attached_pic";
ddpAudio = (File.Exists($"{Path.GetFileNameWithoutExtension(OutPutPath + ".mp4")}.txt") ? File.ReadAllText($"{Path.GetFileNameWithoutExtension(OutPutPath + ".mp4")}.txt") : "") ;
if (!string.IsNullOrEmpty(ddpAudio)) UseAACFilter = false;
foreach (string t in files)
{
command += Path.GetFileName(t) + "|";
}
switch (muxFormat.ToUpper())
{
case ("MP4"):
command += "\" " + (string.IsNullOrEmpty(poster) ? "" : "-i \"" + poster + "\"");
command += " " + (string.IsNullOrEmpty(ddpAudio) ? "" : "-i \"" + ddpAudio + "\"");
command +=
$" -map 0:v? {(string.IsNullOrEmpty(ddpAudio) ? "-map 0:a?" : $"-map {(string.IsNullOrEmpty(poster) ? "1" : "2")}:a -map 0:a?")} -map 0:s? " + (string.IsNullOrEmpty(poster) ? "" : addPoster)
+ (WriteDate ? " -metadata date=\"" + dateString + "\"" : "") +
" -metadata encoding_tool=\"" + encodingTool + "\" -metadata title=\"" + title +
"\" -metadata copyright=\"" + copyright + "\" -metadata comment=\"" + comment +
$"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler_name=\"" + audioName + $"\" -metadata:s:a:{(string.IsNullOrEmpty(ddpAudio) ? "0" : "1")} handler=\"" + audioName + "\" ";
command += (string.IsNullOrEmpty(ddpAudio) ? "" : " -metadata:s:a:0 handler_name=\"DD+\" -metadata:s:a:0 handler=\"DD+\" ");
if (fastStart)
command += "-movflags +faststart";
command += " -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".mp4\"";
break;
case ("MKV"):
command += "\" -map 0 -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".mkv\"";
break;
case ("FLV"):
command += "\" -map 0 -c copy -y " + (UseAACFilter ? "-bsf:a aac_adtstoasc" : "") + " \"" + OutPutPath + ".flv\"";
break;
case ("TS"):
command += "\" -map 0 -c copy -y -f mpegts -bsf:v h264_mp4toannexb \"" + OutPutPath + ".ts\"";
break;
case ("VTT"):
command += "\" -map 0 -y \"" + OutPutPath + ".srt\""; //Convert To Srt
break;
case ("EAC3"):
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".eac3\"";
break;
case ("AAC"):
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".m4a\"";
break;
case ("AC3"):
command += "\" -map 0:a -c copy -y \"" + OutPutPath + ".ac3\"";
break;
}
Run(FFMPEG_PATH, command, Path.GetDirectoryName(files[0]));
LOGGER.WriteLine(strings.ffmpegDone);
//Console.WriteLine(command);
}
public static void ConvertToMPEGTS(string file)
{
if (Global.VIDEO_TYPE == "H264")
{
Run(FFMPEG_PATH,
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -f mpegts -bsf:v h264_mp4toannexb \""
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
Path.GetDirectoryName(file));
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
{
File.Delete(file);
File.Move(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts", file);
}
}
else if (Global.VIDEO_TYPE == "H265")
{
Run(FFMPEG_PATH,
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -f mpegts -bsf:v hevc_mp4toannexb \""
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
Path.GetDirectoryName(file));
if (File.Exists(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts"))
{
File.Delete(file);
File.Move(Path.GetDirectoryName(file) + "\\" + Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts", file);
}
}
else
{
LOGGER.WriteLineError("Unkown Video Type");
}
}
public static void Run(string path, string args, string workDir)
{
string nowDir = Directory.GetCurrentDirectory(); //当前工作路径
Directory.SetCurrentDirectory(workDir);
Process p = new Process();//建立外部调用线程
p.StartInfo.FileName = path;//要调用外部程序的绝对路径
Environment.SetEnvironmentVariable("FFREPORT", "file=" + ReportFile + ":level=32"); //兼容XP系统
//p.StartInfo.Environment.Add("FFREPORT", "file=" + ReportFile + ":level=32");
p.StartInfo.Arguments = args;//参数(这里就是FFMPEG的参数了)
p.StartInfo.UseShellExecute = false;//不使用操作系统外壳程序启动线程(一定为FALSE,详细的请看MSDN)
p.StartInfo.RedirectStandardError = true;//把外部程序错误输出写到StandardError流中(这个一定要注意,FFMPEG的所有输出信息,都为错误输出流,用StandardOutput是捕获不到任何消息的...这是我耗费了2个多月得出来的经验...mencoder就是用standardOutput来捕获的)
p.StartInfo.CreateNoWindow = false;//不创建进程窗口
p.ErrorDataReceived += new DataReceivedEventHandler(Output);//外部程序(这里是FFMPEG)输出流时候产生的事件,这里是把流的处理过程转移到下面的方法中,详细请查阅MSDN
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
p.Start();//启动线程
p.BeginErrorReadLine();//开始异步读取
p.WaitForExit();//阻塞等待进程结束
p.Close();//关闭进程
p.Dispose();//释放资源
Environment.SetEnvironmentVariable("FFREPORT", null); //兼容XP系统
Directory.SetCurrentDirectory(nowDir);
}
private static void Output(object sendProcess, DataReceivedEventArgs output)
{
if (!String.IsNullOrEmpty(output.Data))
{
LOGGER.PrintLine(output.Data, LOGGER.Warning);
}
}
public static bool CheckMPEGTS(string file)
{
//放行杜比视界或纯音频文件
if (Global.VIDEO_TYPE == "DV" || Global.AUDIO_TYPE != "")
return true;
//如果是多分片也认为不是MPEGTS
if (DownloadManager.PartsCount > 1)
return false;
using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
{
byte[] firstByte = new byte[1];
fs.Read(firstByte, 0, 1);
//第一字节的16进制字符串
string _1_byte_str = Convert.ToString(firstByte[0], 16);
//syncword不为47就不处理
if (_1_byte_str != "47")
return false;
}
return true;
}
}
}

1240
N_m3u8DL-CLI/Global.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,153 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Timers;
namespace N_m3u8DL_CLI
{
class HLSLiveDownloader
{
public static int REC_DUR_LIMIT = -1; //默认不限制录制时长
public static double REC_DUR = 0; //已录制时长
private string liveFile = string.Empty;
private string jsonFile = string.Empty;
private string headers = string.Empty;
private string downDir = string.Empty;
private FileStream liveStream = null;
private double targetduration = 10;
private bool isFirstJson = true;
public double TotalDuration { get; set; }
public string Headers { get => headers; set => headers = value; }
public string DownDir { get => downDir; set => downDir = value; }
public FileStream LiveStream { get => liveStream; set => liveStream = value; }
public string LiveFile { get => liveFile; set => liveFile = value; }
ArrayList toDownList = new ArrayList(); //所有待下载的列表
System.Timers.Timer timer = new System.Timers.Timer();
Downloader sd = new Downloader(); //只有一个实例
public void TimerStart()
{
timer.Enabled = true;
//timer.Interval = (targetduration - 2) * 1000; //执行间隔时间,单位为毫秒
timer.Start();
timer.Elapsed += new ElapsedEventHandler(UpdateList);
UpdateList(timer, new EventArgs()); //立即执行一次
Record();
}
public void TimerStop()
{
timer.Stop();
}
//更新列表
private void UpdateList(object source, EventArgs e)
{
jsonFile = Path.Combine(DownDir, "meta.json");
if (!File.Exists(jsonFile))
{
TimerStop();
return;
}
string jsonContent = File.ReadAllText(jsonFile);
JObject initJson = JObject.Parse(jsonContent);
string m3u8Url = initJson["m3u8"].Value<string>();
targetduration = initJson["m3u8Info"]["targetDuration"].Value<double>();
TotalDuration = initJson["m3u8Info"]["totalDuration"].Value<double>();
timer.Interval = (TotalDuration - targetduration) * 1000;//设置定时器运行间隔
JArray lastSegments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString().Trim()); //上次的分段,用于比对新分段
ArrayList tempList = new ArrayList(); //所有待下载的列表
tempList.Clear();
foreach (JObject seg in lastSegments)
{
tempList.Add(seg.ToString());
}
if(isFirstJson)
{
toDownList = tempList;
isFirstJson = false;
return;
}
Parser parser = new Parser();
parser.DownDir = Path.GetDirectoryName(jsonFile);
parser.M3u8Url = m3u8Url;
parser.LiveStream = true;
parser.Parse(); //产生新的json文件
jsonContent = File.ReadAllText(jsonFile);
initJson = JObject.Parse(jsonContent);
JArray segments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString()); //大分组
foreach (JObject seg in segments)
{
if (!tempList.Contains(seg.ToString()))
{
toDownList.Add(seg.ToString()); //加入真正的待下载队列
//Console.WriteLine(seg.ToString());
}
}
if (toDownList.Count > 0)
Record();
}
//public void TryDownload()
//{
// Thread t = new Thread(Download);
// while (toDownList.Count != 0)
// {
// t = new Thread(Download);
// t.Start();
// t.Join();
// while (sd.IsDone != true) ; //忙等待
// if (toDownList.Count > 0)
// toDownList.RemoveAt(0); //下完删除一项
// }
// Console.WriteLine("Waiting...");
//}
private void Record()
{
while (toDownList.Count > 0 && (sd.FileUrl != "" ? sd.IsDone : true))
{
JObject info = JObject.Parse(toDownList[0].ToString());
int index = info["index"].Value<int>();
sd.FileUrl = info["segUri"].Value<string>();
sd.Method = info["method"].Value<string>();
if (sd.Method != "NONE")
{
sd.Key = info["key"].Value<string>();
sd.Iv = info["iv"].Value<string>();
}
sd.TimeOut = (int)timer.Interval - 1000;//超时时间不超过下次执行时间
sd.SegIndex = index;
sd.Headers = Headers;
sd.SegDur = info["duration"].Value<double>();
sd.IsLive = true; //标记为直播
sd.LiveFile = LiveFile;
sd.LiveStream = LiveStream;
sd.Down(); //开始下载
while (sd.IsDone != true) { Thread.Sleep(1); }; //忙等待 Thread.Sleep(1) 可防止cpu 100% 防止电脑风扇狂转
if (toDownList.Count > 0)
toDownList.RemoveAt(0); //下完删除一项
}
LOGGER.PrintLine("Waiting...", LOGGER.Warning);
LOGGER.WriteLine("Waiting...");
}
//检测是否有新分片
private bool isNewSeg()
{
if (toDownList.Count > 0)
return true;
return false;
}
}
}

38
N_m3u8DL-CLI/HLSTags.cs Normal file
View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class HLSTags
{
public static string ext_m3u = "#EXTM3U";
public static string ext_x_targetduration = "#EXT-X-TARGETDURATION";
public static string ext_x_media_sequence = "#EXT-X-MEDIA-SEQUENCE";
public static string ext_x_discontinuity_sequence = "#EXT-X-DISCONTINUITY-SEQUENCE";
public static string ext_x_program_date_time = "#EXT-X-PROGRAM-DATE-TIME";
public static string ext_x_media = "#EXT-X-MEDIA";
public static string ext_x_playlist_type = "#EXT-X-PLAYLIST-TYPE";
public static string ext_x_key = "#EXT-X-KEY";
public static string ext_x_stream_inf = "#EXT-X-STREAM-INF";
public static string ext_x_version = "#EXT-X-VERSION";
public static string ext_x_allow_cache = "#EXT-X-ALLOW-CACHE";
public static string ext_x_endlist = "#EXT-X-ENDLIST";
public static string extinf = "#EXTINF";
public static string ext_i_frames_only = "#EXT-X-I-FRAMES-ONLY";
public static string ext_x_byterange = "#EXT-X-BYTERANGE";
public static string ext_x_i_frame_stream_inf = "#EXT-X-I-FRAME-STREAM-INF";
public static string ext_x_discontinuity = "#EXT-X-DISCONTINUITY";
public static string ext_x_cue_out_start = "#EXT-X-CUE-OUT";
public static string ext_x_cue_out = "#EXT-X-CUE-OUT-CONT";
public static string ext_is_independent_segments = "#EXT-X-INDEPENDENT-SEGMENTS";
public static string ext_x_scte35 = "#EXT-OATCLS-SCTE35";
public static string ext_x_cue_start = "#EXT-X-CUE-OUT";
public static string ext_x_cue_end = "#EXT-X-CUE-IN";
public static string ext_x_cue_span = "#EXT-X-CUE-SPAN";
public static string ext_x_map = "#EXT-X-MAP";
public static string ext_x_start = "#EXT-X-START";
}
}

View File

@@ -0,0 +1,116 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class IqJsonParser
{
public static string Parse(string downDir, string json)
{
JObject jObject = JObject.Parse(json);
var aClips = jObject["payload"]["wm_a"]["audio_track1"]["files"].Value<JArray>();
var vClips = jObject["payload"]["wm_a"]["video_track1"]["files"].Value<JArray>();
var codecsList = new List<string>();
var audioPath = "";
var videoPath = "";
var audioInitPath = "";
var videoInitPath = "";
if (aClips.Count > 0)
{
var init = jObject["payload"]["wm_a"]["audio_track1"]["codec_init"].Value<string>();
byte[] bytes = Convert.FromBase64String(init);
//输出init文件
audioInitPath = Path.Combine(downDir, "iqAudioInit.mp4");
File.WriteAllBytes(audioInitPath, bytes);
StringBuilder sb = new StringBuilder();
sb.AppendLine("#EXTM3U");
sb.AppendLine("#EXT-X-VERSION:3");
sb.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
sb.AppendLine("#CREATED-BY:N_m3u8DL-CLI");
sb.AppendLine($"#EXT-CODEC:{jObject["payload"]["wm_a"]["audio_track1"]["codec"].Value<string>()}");
sb.AppendLine($"#EXT-KID:{jObject["payload"]["wm_a"]["audio_track1"]["key_id"].Value<string>()}");
sb.AppendLine($"#EXT-X-MAP:URI=\"{new Uri(Path.Combine(downDir + "(Audio)", "iqAudioInit.mp4")).ToString()}\"");
sb.AppendLine("#EXT-X-KEY:METHOD=PLZ-KEEP-RAW,URI=\"None\"");
foreach (var a in aClips)
{
sb.AppendLine($"#EXTINF:{a["duration_second"].ToString()}");
sb.AppendLine(a["file_name"].Value<string>());
}
sb.AppendLine("#EXT-X-ENDLIST");
//输出m3u8文件
var _path = Path.Combine(downDir, "iqAudio.m3u8");
File.WriteAllText(_path, sb.ToString());
audioPath = new Uri(_path).ToString();
codecsList.Add(jObject["payload"]["wm_a"]["audio_track1"]["codec"].Value<string>());
}
if (vClips.Count > 0)
{
var init = jObject["payload"]["wm_a"]["video_track1"]["codec_init"].Value<string>();
byte[] bytes = Convert.FromBase64String(init);
//输出init文件
videoInitPath = Path.Combine(downDir, "iqVideoInit.mp4");
File.WriteAllBytes(videoInitPath, bytes);
StringBuilder sb = new StringBuilder();
sb.AppendLine("#EXTM3U");
sb.AppendLine("#EXT-X-VERSION:3");
sb.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD");
sb.AppendLine("#CREATED-BY:N_m3u8DL-CLI");
sb.AppendLine($"#EXT-CODEC:{jObject["payload"]["wm_a"]["video_track1"]["codec"].Value<string>()}");
sb.AppendLine($"#EXT-KID:{jObject["payload"]["wm_a"]["video_track1"]["key_id"].Value<string>()}");
sb.AppendLine($"#EXT-X-MAP:URI=\"{new Uri(videoInitPath).ToString()}\"");
sb.AppendLine("#EXT-X-KEY:METHOD=PLZ-KEEP-RAW,URI=\"None\"");
foreach (var a in vClips)
{
var start = a["seekable"]["pos_start"].Value<long>();
var size = a["size"].Value<long>();
sb.AppendLine($"#EXTINF:{a["duration_second"].ToString()}");
sb.AppendLine($"#EXT-X-BYTERANGE:{size}@{start}");
sb.AppendLine(a["file_name"].Value<string>());
}
sb.AppendLine("#EXT-X-ENDLIST");
//输出m3u8文件
var _path = Path.Combine(downDir, "iqVideo.m3u8");
File.WriteAllText(_path, sb.ToString());
videoPath = new Uri(_path).ToString();
codecsList.Add(jObject["payload"]["wm_a"]["video_track1"]["codec"].Value<string>());
}
var content = "";
if ((videoPath == "" && audioPath != "") || Global.VIDEO_TYPE == "IGNORE")
{
return audioPath;
}
else if (audioPath == "" && videoPath != "")
{
return videoPath;
}
else
{
if (!Directory.Exists(downDir + "(Audio)"))
Directory.CreateDirectory(downDir + "(Audio)");
var _path = Path.Combine(downDir + "(Audio)", "iqAudio.m3u8");
var _pathInit = Path.Combine(downDir + "(Audio)", "iqAudioInit.mp4");
File.Copy(new Uri(audioPath).LocalPath, _path, true);
File.Copy(new Uri(audioInitPath).LocalPath, _pathInit, true);
audioPath = new Uri(_path).ToString();
content = $"#EXTM3U\r\n" +
$"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"{audioPath}\",GROUP-ID=\"default-audio-group\",NAME=\"stream_0\",AUTOSELECT=YES,CHANNELS=\"0\"\r\n" +
$"#EXT-X-STREAM-INF:BANDWIDTH=99999,CODECS=\"{string.Join(",", codecsList)}\",RESOLUTION=0x0,AUDIO=\"default-audio-group\"\r\n" +
$"{videoPath}";
}
var _masterPath = Path.Combine(downDir, "master.m3u8");
File.WriteAllText(_masterPath, content);
return new Uri(_masterPath).ToString();
}
}
}

154
N_m3u8DL-CLI/LOGGER.cs Normal file
View File

@@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class LOGGER
{
public const int Default = 1;
public const int Error = 2;
public const int Warning = 3;
public static string LOGFILE;
public static bool STOPLOG = false;
public static string FindLog(string dir)
{
DirectoryInfo d = new DirectoryInfo(dir);
foreach (FileInfo fi in d.GetFiles())
{
if (fi.Extension.ToUpper() == ".LOG")
{
return fi.FullName;
}
}
return "";
}
public static void InitLog()
{
if (!Directory.Exists(Path.GetDirectoryName(LOGFILE)))//若文件夹不存在则新建文件夹
Directory.CreateDirectory(Path.GetDirectoryName(LOGFILE)); //新建文件夹
//若文件存在则加序号
int index = 1;
var fileName = Path.GetFileNameWithoutExtension(LOGFILE);
while (File.Exists(LOGFILE))
{
LOGFILE = Path.Combine(Path.GetDirectoryName(LOGFILE), $"{fileName}-{index++}.log");
}
string file = LOGFILE;
string now = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
string init = "LOG " + DateTime.Now.ToString("yyyy/MM/dd") + "\r\n"
+ "Save Path: " + Path.GetDirectoryName(LOGFILE) + "\r\n"
+ "Task Start: " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + "\r\n"
+ "Task CommandLine: " + Environment.CommandLine;
if (File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "N_m3u8DL-CLI.args.txt")))
{
init += "\r\nAdditional Args: " + File.ReadAllText(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "N_m3u8DL-CLI.args.txt")); //解析命令行
}
init += "\r\n\r\n";
File.WriteAllText(file, init, Encoding.UTF8);
}
//读写锁机制,当资源被占用,其他线程等待
static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
public static void PrintLine(string text, int printLevel = 1)
{
switch (printLevel)
{
case 0:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.WriteLine(" ".PadRight(12) + " " + text);
break;
case 1:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.WriteLine(text);
break;
case 2:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(text);
Console.ResetColor();
break;
case 3:
Console.Write("\r" + new string(' ', Console.WindowWidth - 1) + "\r");
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(text);
Console.ResetColor();
break;
}
}
public static void WriteLine(string text)
{
if (STOPLOG)
return;
if (!File.Exists(LOGFILE))
return;
try
{
string file = LOGFILE;
//进入写入
LogWriteLock.EnterWriteLock();
using (StreamWriter sw = File.AppendText(file))
{
sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " / (NORMAL) " + text, Encoding.UTF8);
}
}
catch (Exception)
{
}
finally
{
//释放占用
LogWriteLock.ExitWriteLock();
}
}
public static void WriteLineError(string text)
{
if (!File.Exists(LOGFILE))
return;
try
{
string file = LOGFILE;
//进入写入
LogWriteLock.EnterWriteLock();
using (StreamWriter sw = File.AppendText(file))
{
sw.WriteLine(DateTime.Now.ToString("HH:mm:ss.fff") + " / (ERROR) " + text, Encoding.UTF8);
}
}
catch (Exception)
{
}
finally
{
//释放占用
LogWriteLock.ExitWriteLock();
}
}
public static void Show(string text)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(DateTime.Now.ToString("o") + " " + text);
while (Console.ForegroundColor == ConsoleColor.Red)
Console.ResetColor();
}
}
}

836
N_m3u8DL-CLI/MPDParser.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props" Condition="Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" />
<Import Project="..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props" Condition="Exists('..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{4FB61439-B738-46AC-B3AF-2BF72150D057}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>N_m3u8DL_CLI</RootNamespace>
<AssemblyName>N_m3u8DL-CLI</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>logo_3Iv_icon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="BrotliSharpLib, Version=0.3.2.0, Culture=neutral, PublicKeyToken=3f4e2a1cd615fcb7, processorArchitecture=MSIL">
<HintPath>..\packages\BrotliSharpLib.0.3.3\lib\net451\BrotliSharpLib.dll</HintPath>
</Reference>
<Reference Include="Costura, Version=4.1.0.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.4.1.0\lib\net40\Costura.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities.v4.0" />
<Reference Include="Microsoft.JScript" />
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NiL.JS, Version=2.5.1428.0, Culture=neutral, PublicKeyToken=fa941a7c2a4de689, processorArchitecture=MSIL">
<HintPath>..\packages\NiL.JS.2.5.1428\lib\net45\NiL.JS.dll</HintPath>
</Reference>
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Collections" />
<Reference Include="System.Core" />
<Reference Include="System.IO" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CommandLineArgument.cs" />
<Compile Include="CommandLineArgumentParser.cs" />
<Compile Include="Decode51CtoKey.cs" />
<Compile Include="DecodeDdyun.cs" />
<Compile Include="DecodeHuke88Key.cs" />
<Compile Include="DecodeImooc.cs" />
<Compile Include="DecodeNfmovies.cs" />
<Compile Include="Decrypter.cs" />
<Compile Include="FFmpeg.cs" />
<Compile Include="Global.cs" />
<Compile Include="HLSLiveDownloader.cs" />
<Compile Include="HLSTags.cs" />
<Compile Include="IqJsonParser.cs" />
<Compile Include="LOGGER.cs" />
<Compile Include="DownloadManager.cs" />
<Compile Include="MPDParser.cs" />
<Compile Include="Parser.cs" />
<Compile Include="Program.cs" />
<Compile Include="ProgressReporter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Downloader.cs" />
<Compile Include="strings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>strings.resx</DependentUpon>
</Compile>
<Compile Include="strings.en-US.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>strings.en-US.resx</DependentUpon>
</Compile>
<Compile Include="strings.zh-TW.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>strings.zh-TW.resx</DependentUpon>
</Compile>
<Compile Include="Watcher.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<COMReference Include="Scripting">
<Guid>{420B2830-E718-11CF-893D-00A0C9054228}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<Content Include="logo_3Iv_icon.ico" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="strings.en-US.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>strings.en-US.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>strings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="strings.zh-TW.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>strings.zh-TW.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>这台计算机上缺少此项目引用的 NuGet 程序包。使用“NuGet 程序包还原”可下载这些程序包。有关更多信息,请参见 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Resource.Embedder.2.1.1\build\Resource.Embedder.props'))" />
<Error Condition="!Exists('..\packages\Fody.6.0.0\build\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.6.0.0\build\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.4.1.0\build\Costura.Fody.props'))" />
</Target>
<Import Project="..\packages\Fody.6.0.0\build\Fody.targets" Condition="Exists('..\packages\Fody.6.0.0\build\Fody.targets')" />
</Project>

982
N_m3u8DL-CLI/Parser.cs Normal file

File diff suppressed because it is too large Load Diff

493
N_m3u8DL-CLI/Program.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class ProgressReporter
{
private static string speed = "";
private static string progress = "";
static object lockThis = new object();
public static void Report(string progress, string speed)
{
lock (lockThis)
{
if (!string.IsNullOrEmpty(progress)) ProgressReporter.progress = progress;
if (!string.IsNullOrEmpty(speed)) ProgressReporter.speed = speed;
string now = DateTime.Now.ToString("HH:mm:ss.000");
var sub = Console.WindowWidth - 4 - ProgressReporter.progress.Length - ProgressReporter.speed.Length - now.Length;
if (sub <= 0) sub = 0;
string print = now + " " + ProgressReporter.progress + " " + ProgressReporter.speed + new string(' ', sub);
Console.Write("\r" + print + "\r");
//Console.Write(print);
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 有关程序集的一般信息由以下
// 控制。更改这些特性值可修改
// 与程序集关联的信息。
[assembly: AssemblyTitle("N_m3u8DL-CLI")]
[assembly: AssemblyDescription("一款命令行m3u8下载器")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("nilaoda")]
[assembly: AssemblyProduct("N_m3u8DL-CLI")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// 将 ComVisible 设置为 false 会使此程序集中的类型
//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
//请将此类型的 ComVisible 特性设置为 true。
[assembly: ComVisible(false)]
// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
[assembly: Guid("4fb61439-b738-46ac-b3af-2bf72150d057")]
// 程序集的版本信息由下列四个值组成:
//
// 主版本
// 次版本
// 生成号
// 修订号
//
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.9.8.0")]
[assembly: AssemblyFileVersion("2.9.8.0")]

110
N_m3u8DL-CLI/Watcher.cs Normal file
View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace N_m3u8DL_CLI
{
class Watcher
{
private string dir = string.Empty;
private int total = 0;
private static double totalDuration = 0; //总时长
private int now = 0;
private int partsCount = 0;
FileSystemWatcher watcher = new FileSystemWatcher();
public int Total { get => total; set => total = value; }
public int Now { get => now; set => now = value; }
public int PartsCount { get => partsCount; set => partsCount = value; }
public static double TotalDuration { get => totalDuration; set => totalDuration = value; }
public Watcher(string Dir)
{
this.dir = Dir;
}
public void WatcherStrat()
{
for (int i = 0; i < PartsCount; i++)
{
Now += Global.GetFileCount(dir + "\\Part_" + i.ToString(DownloadManager.partsPadZero), ".ts");
}
watcher.Path = dir;
watcher.Filter = "*.ts";
watcher.IncludeSubdirectories = true; //包括子目录
watcher.EnableRaisingEvents = true; //开启提交事件
watcher.Created += new FileSystemEventHandler(OnCreated);
watcher.Renamed += new RenamedEventHandler(OnCreated);
watcher.Deleted += new FileSystemEventHandler(OnDeleted);
}
public void WatcherStop()
{
watcher.Dispose();
}
private void OnCreated(object source, FileSystemEventArgs e)
{
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
return;
Now++;
if (Now > Total)
{
return;
}
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
var print = "Progress: " + Now + "/" + Total
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
ProgressReporter.Report(print, "");
}
private void OnRenamed(object source, RenamedEventArgs e)
{
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
return;
Now++;
if (Now > Total)
{
return;
}
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
var print = "Progress: " + Now + "/" + Total
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
ProgressReporter.Report(print, "");
}
private void OnDeleted(object source, FileSystemEventArgs e)
{
if (Path.GetFileNameWithoutExtension(e.FullPath).StartsWith("Part"))
return;
Now--;
if (Now > Total)
{
return;
}
//Console.Title = Now + " / " + Total;
string downloadedSize = Global.FormatFileSize(DownloadManager.DownloadedSize);
string estimatedSize = Global.FormatFileSize(DownloadManager.DownloadedSize * total / now);
int padding = downloadedSize.Length > estimatedSize.Length ? downloadedSize.Length : estimatedSize.Length;
DownloadManager.ToDoSize = (DownloadManager.DownloadedSize * total / now) - DownloadManager.DownloadedSize;
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
var print = "Progress: " + Now + "/" + Total
+ $" ({percent}) -- {downloadedSize.PadLeft(padding)}/{estimatedSize.PadRight(padding)}";
ProgressReporter.Report(print, "");
}
}
}

362
N_m3u8DL-CLI/changelog.txt Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="BrotliSharpLib" version="0.3.3" targetFramework="net46" />
<package id="Costura.Fody" version="4.1.0" targetFramework="net46" />
<package id="Fody" version="6.0.0" targetFramework="net46" developmentDependency="true" />
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net46" />
<package id="NiL.JS" version="2.5.1428" targetFramework="net46" />
<package id="Resource.Embedder" version="2.1.1" targetFramework="net46" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net46" />
</packages>

559
N_m3u8DL-CLI/strings.Designer.cs generated Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More