1
mirror of https://github.com/nilaoda/N_m3u8DL-CLI synced 2025-09-11 16:00:49 +02:00

Compare commits

..

20 Commits

Author SHA1 Message Date
nilaoda
985f6e57c3 Fix #785 2023-06-03 17:30:51 +08:00
nilaoda
d9eea2e80f 修正Part分割 2023-03-25 23:17:47 +08:00
nilaoda
0cc4a87a4c Update FUNDING.yml 2022-11-17 23:10:19 +08:00
nilaoda
66d0864d72 update discord link 2022-10-10 13:44:53 +08:00
nilaoda
965ac2b513 update discord link 2022-10-10 13:43:58 +08:00
nilaoda
a95334ec57 fix #718 2022-09-05 21:55:09 +08:00
nilaoda
e05a21a034 Merge pull request #714 from xifangczy/master
修复 录制间隔负数导致错误
2022-08-19 20:12:37 +08:00
O2bmm
13cd5d0870 修复 录制间隔负数导致错误 2022-08-19 19:38:03 +08:00
nilaoda
8d9ad7af41 Update README.md 2022-07-20 21:44:36 +08:00
nilaoda
0a11816acf fix #708 2022-07-20 21:34:18 +08:00
nilaoda
81ba4ff7d3 Create FUNDING.yml 2022-07-14 20:08:45 +08:00
nilaoda
f5a9ed08a1 update version 2022-07-12 00:21:39 +08:00
nilaoda
6b8bfde19f update docs 2022-07-12 00:10:53 +08:00
nilaoda
ff738f2849 Merge pull request #698 from xifangczy/master
fix: 修正m3u8dl协议 转义符问题
2022-07-11 23:44:31 +08:00
O2bmm
3d35c24a2e fix: 修正m3u8dl协议 转义符问题 2022-07-11 01:32:30 +08:00
nilaoda
41d7151e7e Update HLSLiveDownloader.cs 2022-06-22 11:43:36 +08:00
nilaoda
94246ef163 支持ChaCha20解密 2022-06-20 23:18:27 +08:00
nilaoda
9c118631cf 优化对#EXT-X-BYTERANGE的处理 2022-06-05 02:15:24 +08:00
nilaoda
2ee9ab234f fix #665 #661 2022-05-26 20:18:05 +08:00
nilaoda
606f2184df 修正对于m3u8dl://的描述 2022-05-26 01:26:18 +08:00
21 changed files with 827 additions and 46 deletions

13
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: ['https://nilaoda.github.io/N_m3u8DL-CLI/source/images/alipay.png','https://www.buymeacoffee.com/nilaoda']

661
N_m3u8DL-CLI/CSChaCha20.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
using System;
using CSChaCha20;
using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace N_m3u8DL_CLI
@@ -45,6 +47,40 @@ namespace N_m3u8DL_CLI
return resultArray;
}
public static byte[] CHACHA20Decrypt(byte[] encryptedBuff, byte[] keyBytes, byte[] nonceBytes)
{
if (keyBytes.Length != 32)
throw new Exception("Key must be 32 bytes!");
if (nonceBytes.Length != 12 && nonceBytes.Length != 8)
throw new Exception("Key must be 12 or 8 bytes!");
if (nonceBytes.Length == 8)
nonceBytes = (new byte[4] { 0, 0, 0, 0 }).Concat(nonceBytes).ToArray();
var decStream = new MemoryStream();
using (BinaryReader reader = new BinaryReader(new MemoryStream(encryptedBuff)))
{
using (BinaryWriter writer = new BinaryWriter(decStream))
{
while (true)
{
var buffer = reader.ReadBytes(1024);
byte[] dec = new byte[buffer.Length];
if (buffer.Length > 0)
{
ChaCha20 forDecrypting = new ChaCha20(keyBytes, nonceBytes, 0);
forDecrypting.DecryptBytes(dec, buffer);
writer.Write(dec, 0, dec.Length);
}
else
{
break;
}
}
}
}
return decStream.ToArray();
}
public static byte[] HexStringToBytes(string hexStr)
{
if (string.IsNullOrEmpty(hexStr))

View File

@@ -47,6 +47,10 @@ namespace N_m3u8DL_CLI
public long ExpectByte { get => expectByte; set => expectByte = value; }
public long StartByte { get => startByte; set => startByte = value; }
public double SegDur { get => segDur; set => segDur = value; }
public static bool EnableChaCha20 { get; set; } = false;
public static string ChaCha20KeyBase64 { get; set; }
public static string ChaCha20NonceBase64 { get; set; }
//重写WebClinet
//private class WebClient : System.Net.WebClient
@@ -179,9 +183,18 @@ namespace N_m3u8DL_CLI
if (File.Exists(savePath) && Global.ShouldStop == false)
{
FileInfo fi = new FileInfo(savePath);
if (Method == "NONE" || method.Contains("NOTSUPPORTED"))
if (File.Exists(fi.FullName) && EnableChaCha20)
{
fi.MoveTo(Path.GetDirectoryName(savePath) + "\\" + Path.GetFileNameWithoutExtension(savePath) + ".ts");
byte[] decryptBuff = Decrypter.CHACHA20Decrypt(File.ReadAllBytes(fi.FullName), Convert.FromBase64String(ChaCha20KeyBase64), Convert.FromBase64String(ChaCha20NonceBase64));
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();
}
else 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.");
}
@@ -249,4 +262,4 @@ namespace N_m3u8DL_CLI
}
}
}
}
}

View File

@@ -35,7 +35,7 @@ namespace N_m3u8DL_CLI
/*===============================================================================*/
static Version ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
static string nowVer = $"{ver.Major}.{ver.Minor}.{ver.Build}";
static string nowDate = "20220524";
static string nowDate = "20220711";
public static void WriteInit()
{
Console.WriteLine($"N_m3u8DL-CLI version {nowVer} 2018-2022");

View File

@@ -61,7 +61,7 @@ namespace N_m3u8DL_CLI
string m3u8Url = initJson["m3u8"].Value<string>();
targetduration = initJson["m3u8Info"]["targetDuration"].Value<double>();
TotalDuration = initJson["m3u8Info"]["totalDuration"].Value<double>();
timer.Interval = (TotalDuration - targetduration) * 1000;//设置定时器运行间隔
timer.Interval = Math.Abs(TotalDuration - targetduration) * 1000;//设置定时器运行间隔
if (timer.Interval <= 1000) timer.Interval = 10000;
JArray lastSegments = JArray.Parse(initJson["m3u8Info"]["segments"][0].ToString().Trim()); //上次的分段,用于比对新分段
ArrayList tempList = new ArrayList(); //所有待下载的列表
@@ -79,6 +79,7 @@ namespace N_m3u8DL_CLI
}
Parser parser = new Parser();
parser.Headers = Headers;
parser.DownDir = Path.GetDirectoryName(jsonFile);
parser.M3u8Url = m3u8Url;
parser.LiveStream = true;
@@ -128,6 +129,7 @@ namespace N_m3u8DL_CLI
sd.Iv = info["iv"].Value<string>();
}
sd.TimeOut = (int)timer.Interval - 1000;//超时时间不超过下次执行时间
if (sd.TimeOut <= 0) sd.TimeOut = (int)timer.Interval;
sd.SegIndex = index;
sd.Headers = Headers;
sd.SegDur = info["duration"].Value<double>();

View File

@@ -563,12 +563,19 @@ namespace N_m3u8DL_CLI
Console.Write("".PadRight(13) + "Enter Numbers Separated By A Space: ");
var input = Console.ReadLine();
cursorIndex += 2;
for (int i = startCursorIndex; i < cursorIndex; i++)
try
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
for (int i = startCursorIndex; i < cursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
Console.SetCursorPosition(0, startCursorIndex);
}
catch (Exception)
{
;
}
Console.SetCursorPosition(0, startCursorIndex);
if (!string.IsNullOrEmpty(input))
{
bestVideo = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };

View File

@@ -97,5 +97,14 @@ namespace N_m3u8DL_CLI
[Option("unregisterUrlProtocol", HelpText = "Help_unregisterUrlProtocol", ResourceType = typeof(strings))]
public bool UnregisterUrlProtocol { get; set; }
[Option("enableChaCha20", HelpText = "enableChaCha20")]
public bool EnableChaCha20 { get; set; }
[Option("chaCha20KeyBase64", HelpText = "ChaCha20KeyBase64")]
public string ChaCha20KeyBase64 { get; set; }
[Option("chaCha20NonceBase64", HelpText = "ChaCha20NonceBase64")]
public string ChaCha20NonceBase64 { get; set; }
}
}

View File

@@ -90,6 +90,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CSChaCha20.cs" />
<Compile Include="Decode51CtoKey.cs" />
<Compile Include="DecodeCdeledu.cs" />
<Compile Include="DecodeDdyun.cs" />

View File

@@ -271,6 +271,7 @@ namespace N_m3u8DL_CLI
{
expectByte = Convert.ToInt64(t[0]);
segInfo.Add("expectByte", expectByte);
segInfo.Add("startByte", segments.Last["startByte"].Value<long>() + segments.Last["expectByte"].Value<long>());
}
if (t.Length == 2)
{
@@ -326,7 +327,7 @@ namespace N_m3u8DL_CLI
continue;
}
//常规情况的#EXT-X-DISCONTINUITY标记新建part
if (!hasAd && segments.Count > 1)
if (!hasAd && segments.Count >= 1)
{
parts.Add(segments);
segments = new JArray();
@@ -361,20 +362,20 @@ namespace N_m3u8DL_CLI
{
string[] tmp = line.Replace(HLSTags.extinf + ":", "").Split(',');
segDuration = Convert.ToDouble(tmp[0]);
segInfo.Add("index", segIndex);
segInfo.Add("method", m3u8CurrentKey[0]);
segInfo["index"] = segIndex;
segInfo["method"] = m3u8CurrentKey[0];
//是否有加密有的话写入KEY和IV
if (m3u8CurrentKey[0] != "NONE")
{
segInfo.Add("key", m3u8CurrentKey[1]);
segInfo["key"] = m3u8CurrentKey[1];
//没有读取到IV自己生成
if (m3u8CurrentKey[2] == "")
segInfo.Add("iv", "0x" + Convert.ToString(segIndex, 16).PadLeft(32, '0'));
segInfo["iv"] = "0x" + Convert.ToString(segIndex, 16).PadLeft(32, '0');
else
segInfo.Add("iv", m3u8CurrentKey[2]);
segInfo["iv"] = m3u8CurrentKey[2];
}
totalDuration += segDuration;
segInfo.Add("duration", segDuration);
segInfo["duration"] = segDuration;
expectSegment = true;
segIndex++;
}
@@ -635,12 +636,19 @@ namespace N_m3u8DL_CLI
Console.Write("".PadRight(13) + "Enter Number: ");
var input = Console.ReadLine();
cursorIndex += 2;
for (int i = startCursorIndex; i < cursorIndex; i++)
try
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
for (int i = startCursorIndex; i < cursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
Console.SetCursorPosition(0, startCursorIndex);
}
catch (Exception)
{
;
}
Console.SetCursorPosition(0, startCursorIndex);
audioUrl = MEDIA_AUDIO_GROUP[bestUrlAudio][int.Parse(input)].Uri;
}
}
@@ -665,12 +673,19 @@ namespace N_m3u8DL_CLI
Console.Write("".PadRight(13) + "Enter Number: ");
var input = Console.ReadLine();
cursorIndex += 2;
for (int i = startCursorIndex; i < cursorIndex; i++)
try
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
for (int i = startCursorIndex; i < cursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
Console.SetCursorPosition(0, startCursorIndex);
}
catch (Exception)
{
;
}
Console.SetCursorPosition(0, startCursorIndex);
subUrl = MEDIA_SUB_GROUP[bestUrlSub][int.Parse(input)].Uri;
}
}

View File

@@ -77,8 +77,14 @@ namespace N_m3u8DL_CLI.NetCore
{
if (args[0].ToLower().StartsWith("m3u8dl:"))
{
var valueBytes = Convert.FromBase64String(args[0].Substring(7));
var cmd = Encoding.UTF8.GetString(valueBytes);
var base64 = args[0].Replace("m3u8dl://", "").Replace("m3u8dl:", "");
var cmd = "";
try { cmd = Encoding.UTF8.GetString(Convert.FromBase64String(base64)); }
catch (FormatException) { cmd = Encoding.UTF8.GetString(Convert.FromBase64String(base64.TrimEnd('/'))); }
//修正参数转义符
cmd = cmd.Replace("\\\"", "\"");
//修正工作目录
Environment.CurrentDirectory = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName);
args = Global.ParseArguments(cmd).ToArray(); //解析命令行
}
else if (args[0] == "--registerUrlProtocol")
@@ -221,6 +227,13 @@ namespace N_m3u8DL_CLI.NetCore
workDir = Environment.ExpandEnvironmentVariables(o.WorkDir);
DownloadManager.HasSetDir = true;
}
//CHACHA20
if (o.EnableChaCha20 && !string.IsNullOrEmpty(o.ChaCha20KeyBase64) && !string.IsNullOrEmpty(o.ChaCha20NonceBase64))
{
Downloader.EnableChaCha20 = true;
Downloader.ChaCha20KeyBase64 = o.ChaCha20KeyBase64;
Downloader.ChaCha20NonceBase64 = o.ChaCha20NonceBase64;
}
//Proxy
if (!string.IsNullOrEmpty(o.ProxyAddress))

View File

@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号
// 方法是按如下所示使用“*”: :
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.0.0.0")]
[assembly: AssemblyFileVersion("3.0.0.0")]
[assembly: AssemblyVersion("3.0.2.0")]
[assembly: AssemblyFileVersion("3.0.2.0")]

View File

@@ -22,7 +22,9 @@
本项目已于2019年10月9日开源采用MIT许可证各取所需。
# 关于跨平台
未来可期
* N_m3u8DL-CLI `(本项目)`: 基于 .NET Framework, 不具备跨平台能力. 目前已进入维护阶段.
* [N_m3u8DL-RE](https://github.com/nilaoda/N_m3u8DL-RE) : 抛弃历史包袱从零做起, 支持Win/Linux/Mac, 更丰富的功能会在这里出现 ...
# N_m3u8DL-CLI
一个**简单易用的**m3u8下载器下载地址https://github.com/nilaoda/N_m3u8DL-CLI/releases
@@ -89,6 +91,9 @@ OPTIONS:
--noProxy 不自动使用系统代理
--registerUrlProtocol 注册m3u8dl链接协议
--unregisterUrlProtocol 取消注册m3u8dl链接协议
--enableChaCha20 enableChaCha20
--chaCha20KeyBase64 ChaCha20KeyBase64
--chaCha20NonceBase64 ChaCha20NonceBase64
--help Display this help screen.
--version Display version information.
```
@@ -102,24 +107,24 @@ OPTIONS:
URI格式
```
m3u8dl:<base64编码的客户端命令行文本>
m3u8dl://<base64编码的客户端命令行文本>
```
URI示例
```
m3u8dl:Imh0dHBzOi8vZXhhbXBsZS5jb20vYWJjLm0zdTgiIC0td29ya0RpciAiJVVTRVJQUk9GSUxFJVxEb3dubG9hZHNcbTN1OGRsIiAtLXNhdmVOYW1lICJhYmMiIC0tZW5hYmxlRGVsQWZ0ZXJEb25lIC0tZGlzYWJsZURhdGVJbmZvIC0tbm9Qcm94eQ==
m3u8dl://Imh0dHBzOi8vZXhhbXBsZS5jb20vYWJjLm0zdTgiIC0td29ya0RpciAiJVVTRVJQUk9GSUxFJVxEb3dubG9hZHNcbTN1OGRsIiAtLXNhdmVOYW1lICJhYmMiIC0tZW5hYmxlRGVsQWZ0ZXJEb25lIC0tZGlzYWJsZURhdGVJbmZvIC0tbm9Qcm94eQ==
```
URI解码结果
```
m3u8dl:"https://example.com/abc.m3u8" --workDir "%USERPROFILE%\Downloads\m3u8dl" --saveName "abc" --enableDelAfterDone --disableDateInfo --noProxy
"https://example.com/abc.m3u8" --workDir "%USERPROFILE%\Downloads\m3u8dl" --saveName "abc" --enableDelAfterDone --disableDateInfo --noProxy
```
# 用户文档
https://nilaoda.github.io/N_m3u8DL-CLI/
# 聊聊
https://discord.gg/W5tvcRJDPs
https://discord.gg/SSGwKrjC44
# 赞赏
![Wow](https://nilaoda.github.io/N_m3u8DL-CLI/source/images/alipay.png)

View File

@@ -68,6 +68,9 @@ OPTIONS:
--noProxy Disable use system proxy
--registerUrlProtocol Register m3u8dl URL protocol
--unregisterUrlProtocol Unregister m3u8dl URL protocol
--enableChaCha20 enableChaCha20
--chaCha20KeyBase64 ChaCha20KeyBase64
--chaCha20NonceBase64 ChaCha20NonceBase64
--help Display this help screen.
--version Display version information.
```
@@ -81,21 +84,21 @@ New commandline options
URI Format
```
m3u8dl:<base64 encoded params>
m3u8dl://<base64 encoded params>
```
URI Example
```
m3u8dl:Imh0dHBzOi8vZXhhbXBsZS5jb20vYWJjLm0zdTgiIC0td29ya0RpciAiJVVTRVJQUk9GSUxFJVxEb3dubG9hZHNcbTN1OGRsIiAtLXNhdmVOYW1lICJhYmMiIC0tZW5hYmxlRGVsQWZ0ZXJEb25lIC0tZGlzYWJsZURhdGVJbmZvIC0tbm9Qcm94eQ==
m3u8dl://Imh0dHBzOi8vZXhhbXBsZS5jb20vYWJjLm0zdTgiIC0td29ya0RpciAiJVVTRVJQUk9GSUxFJVxEb3dubG9hZHNcbTN1OGRsIiAtLXNhdmVOYW1lICJhYmMiIC0tZW5hYmxlRGVsQWZ0ZXJEb25lIC0tZGlzYWJsZURhdGVJbmZvIC0tbm9Qcm94eQ==
```
URI Decode Result
```
m3u8dl:"https://example.com/abc.m3u8" --workDir "%USERPROFILE%\Downloads\m3u8dl" --saveName "abc" --enableDelAfterDone --disableDateInfo --noProxy
"https://example.com/abc.m3u8" --workDir "%USERPROFILE%\Downloads\m3u8dl" --saveName "abc" --enableDelAfterDone --disableDateInfo --noProxy
```
## Document
https://nilaoda.github.io/N_m3u8DL-CLI/
## Chit-chat
https://discord.gg/W5tvcRJDPs
https://discord.gg/RscAJZv3Yq

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long