1
mirror of https://github.com/nilaoda/N_m3u8DL-CLI synced 2025-09-05 23:29:33 +02:00

Compare commits

...

192 Commits
1.3 ... 2.8.1

Author SHA1 Message Date
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
nilaoda
4aea16d672 Update README.md 2019-09-10 13:56:43 +08:00
nilaoda
afc69eae1a Merge pull request #19 from nilaoda/add-license-1
Create LICENSE
2019-09-10 11:59:13 +08:00
nilaoda
2446a25b35 Create LICENSE 2019-09-10 11:58:37 +08:00
nilaoda
d37524180d Update README.md 2019-09-10 11:21:59 +08:00
nilaoda
b02a29fbb7 更新文档 2019-09-09 17:22:59 +08:00
nilaoda
e87d02230d Merge branch 'master' of https://github.com/nilaoda/N_m3u8DL-CLI 2019-09-09 17:20:08 +08:00
nilaoda
63609a0970 增加--enableAudioOnly 2019-09-09 17:19:47 +08:00
nilaoda
1fe798ab19 Update README.md 2019-09-08 22:40:36 +08:00
nilaoda
21b9977501 Update README.md 2019-09-08 22:40:01 +08:00
nilaoda
fbe37be499 更新截图 2019-09-08 22:17:37 +08:00
nilaoda
6416dd642d 更新爱奇艺4K-H264 2019-08-27 01:52:23 +08:00
nilaoda
967f0582a6 修复台湾爱奇艺杜比 2019-08-25 00:02:02 +08:00
nilaoda
a5aeffc6ec 更新js 2019-08-24 23:09:25 +08:00
nilaoda
745cbd92ad 更新文档 2019-08-22 23:26:29 +08:00
nilaoda
4a4412a262 常规更新 2019-08-21 14:55:24 +08:00
nilaoda
084989b3ce update js 2019-08-11 23:38:52 +08:00
nilaoda
640208d95c 增加wetv字幕js 2019-08-11 23:31:33 +08:00
nilaoda
ad148ba4c7 更新芒果TV 2019-08-08 21:47:54 +08:00
nilaoda
7c961dbbbf 修正文档日期 2019-08-08 21:12:34 +08:00
nilaoda
83df7b4301 增加爱奇艺1080P H265 2019-08-08 21:08:41 +08:00
nilaoda
860282a463 支持愛奇藝 2019-08-07 12:06:07 +08:00
nilaoda
70582029dd 修复爱奇艺4K_H264 2019-08-07 11:23:53 +08:00
nilaoda
1f3002ef66 更新爱奇艺4K264、4K265代码 2019-08-07 10:21:56 +08:00
nilaoda
07c758dd00 增加爱奇艺杜比音轨JS 2019-08-07 02:09:59 +08:00
nilaoda
38f2f5589f 更新爱奇艺JS 2019-08-04 23:58:34 +08:00
nilaoda
c7b9414613 更新说明书 2019-07-24 00:45:03 +08:00
nilaoda
84953fa16a 更新爱奇艺JS 2019-07-14 21:43:03 +08:00
nilaoda
4cf88711d6 更新爱奇艺JS 2019-07-13 19:11:54 +08:00
nilaoda
b59940f719 修正错别字 2019-07-10 23:54:30 +08:00
nilaoda
6a6a338868 增加阿里云大学教程 2019-07-10 23:51:03 +08:00
nilaoda
0d74380e1c 更新文档 2019-07-10 22:42:30 +08:00
nilaoda
5a398bdb79 更新文档 2019-07-10 22:40:36 +08:00
nilaoda
c2953a3d43 修复腾讯视频m3u8获取代码 2019-07-10 21:31:41 +08:00
nilaoda
6f2ebfba2d 增加气球云教程 2019-07-10 00:49:39 +08:00
nilaoda
3c1b893919 Update README.md 2019-07-09 01:54:59 +08:00
nilaoda
8b6f411af9 add doc 2019-07-09 01:54:12 +08:00
nilaoda
6cfd74f3a0 add docs 2019-07-09 01:42:37 +08:00
nilaoda
044068016b Update README.md 2019-07-07 22:35:53 +08:00
nilaoda
6d21ef0a10 Update README.md 2019-07-07 21:36:36 +08:00
nilaoda
6d1834ca9f Update README.md 2019-07-07 21:28:37 +08:00
nilaoda
3e215359b4 Javascript 2019-07-07 21:26:57 +08:00
nilaoda
3d9610d3bb 搜狐 2019-07-07 16:41:17 +08:00
nilaoda
377c575c72 Update README.md 2019-06-21 21:41:12 +08:00
nilaoda
c02d737125 Update README.md 2019-06-21 21:26:04 +08:00
nilaoda
c1fb395ee0 爱奇艺代码 2019-06-19 20:53:54 +08:00
nilaoda
3920ae6178 Update README.md 2019-06-19 20:34:42 +08:00
nilaoda
f96ea497b7 Update README.md 2019-06-16 16:24:29 +08:00
nilaoda
0013ec4bb5 Update README.md 2019-06-16 16:05:26 +08:00
nilaoda
604c25a2be Update README.md 2019-06-16 16:05:15 +08:00
nilaoda
a83afd8908 Update README.md 2019-06-16 15:46:33 +08:00
nilaoda
206342c4cf Update README.md 2019-06-14 11:29:26 +08:00
nilaoda
296739910e Update README.md 2019-06-14 11:29:02 +08:00
nilaoda
5a62342145 add mgtv 2019-06-13 17:28:59 +08:00
nilaoda
64df11fefb 更新优酷代码 2019-06-13 17:05:50 +08:00
nilaoda
5d82bc9135 Update README.md 2019-06-12 11:04:41 +08:00
nilaoda
2a61b61205 Update README.md 2019-06-11 12:54:37 +08:00
nilaoda
15568f917c Update README.md 2019-06-11 12:52:05 +08:00
nilaoda
b196990972 Update README.md 2019-06-11 11:23:19 +08:00
nilaoda
d4a94d185a Update README.md 2019-06-08 23:04:43 +08:00
nilaoda
d865c05d9a Update README.md 2019-06-08 01:14:18 +08:00
109 changed files with 15207 additions and 30 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\

261
.gitignore vendored Normal file

File diff suppressed because it is too large Load Diff

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 nilaoda
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

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);
}
}
}

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;
}
}
}

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

@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class Decrypter
{
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC)
{
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 = PaddingMode.PKCS7;
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)
{
byte[] inBuff = encryptedBuff;
Aes dcpt = Aes.Create();
dcpt.BlockSize = 128;
dcpt.KeySize = 128;
dcpt.Key = keyByte;
dcpt.IV = ivByte;
dcpt.Mode = mode;
dcpt.Padding = PaddingMode.PKCS7;
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;
}
}
}

1141
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";
}
}

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

@@ -0,0 +1,163 @@
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 static int CursorIndex = 5;
public static int FFmpegCorsorIndex = 5;
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)); //新建文件夹
if (File.Exists(LOGFILE))//若文件存在则删除
File.Delete(LOGFILE);
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, int cursorIndex = 0)
{
try
{
if (CursorIndex > 1000)
{
Console.Clear();
CursorIndex = 0;
}
if (cursorIndex == 0)
Console.SetCursorPosition(0, CursorIndex++);
else
Console.SetCursorPosition(0, cursorIndex);
}
catch (Exception)
{
;
}
switch (printLevel)
{
case 0:
Console.WriteLine(" ".PadRight(12) + " " + text);
break;
case 1:
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.WriteLine(text);
break;
case 2:
Console.Write(DateTime.Now.ToString("HH:mm:ss.fff") + " ");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(text);
Console.ResetColor();
break;
case 3:
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();
}
}
}

617
N_m3u8DL-CLI/MPDParser.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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 />
</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="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.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="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="LOGGER.cs" />
<Compile Include="DownloadManager.cs" />
<Compile Include="MPDParser.cs" />
<Compile Include="Parser.cs" />
<Compile Include="Program.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" />
</Project>

876
N_m3u8DL-CLI/Parser.cs Normal file

File diff suppressed because it is too large Load Diff

801
N_m3u8DL-CLI/Program.cs Normal file

File diff suppressed because it is too large Load Diff

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 © 2020")]
[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("1.0.0.0")]
[assembly: AssemblyFileVersion("2.7.0.0")]

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

@@ -0,0 +1,104 @@
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);
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
Console.SetCursorPosition(0, 2);
Console.Write(("Progress: " + Now + " of " + Total
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
}
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);
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
Console.SetCursorPosition(0, 2);
Console.Write(("Progress: " + Now + " of " + Total
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
}
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);
string percent = (Convert.ToDouble(now) / Convert.ToDouble(total) * 100).ToString("0.00") + "%";
Console.SetCursorPosition(0, 2);
Console.Write(("Progress: " + Now + " of " + Total
+ $" ({percent}/{downloadedSize}/{estimatedSize}/{Global.FormatTime(Convert.ToInt32(TotalDuration))})").PadRight(62));
}
}
}

287
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,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net46" />
<package id="NiL.JS" version="2.5.1428" targetFramework="net46" />
</packages>

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

File diff suppressed because it is too large Load Diff

0
N_m3u8DL-CLI/strings.en-US.Designer.cs generated Normal file
View 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