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

Compare commits

...

9 Commits
2.7.2 ... 2.7.5

Author SHA1 Message Date
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
9 changed files with 263 additions and 33 deletions

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

@@ -0,0 +1,30 @@
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
- name: Setup NuGet
uses: NuGet/setup-nuget@v1.0.2
- 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\

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

View File

@@ -10,6 +10,8 @@ namespace N_m3u8DL_CLI
{
class FFmpeg
{
public static string FFMPEG_PATH = "ffmpeg";
private static string outPutPath = string.Empty;
private static string reportFile = string.Empty;
private static bool useAACFilter = false; //是否启用滤镜
@@ -83,7 +85,7 @@ namespace N_m3u8DL_CLI
}
Run("ffmpeg", command, Path.GetDirectoryName(files[0]));
Run(FFMPEG_PATH, command, Path.GetDirectoryName(files[0]));
LOGGER.WriteLine(strings.ffmpegDone);
//Console.WriteLine(command);
}
@@ -92,7 +94,7 @@ namespace N_m3u8DL_CLI
{
if (Global.VIDEO_TYPE == "H264")
{
Run("ffmpeg",
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));
@@ -104,7 +106,7 @@ namespace N_m3u8DL_CLI
}
else if (Global.VIDEO_TYPE == "H265")
{
Run("ffmpeg",
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));

View File

@@ -30,8 +30,8 @@ namespace N_m3u8DL_CLI
/*===============================================================================*/
static string nowVer = "2.5.7";
static string nowDate = "20200809";
static string nowVer = "2.7.5";
static string nowDate = "20201014";
public static void WriteInit()
{
Console.Clear();
@@ -545,6 +545,7 @@ namespace N_m3u8DL_CLI
long totalLen = 0;
long downLen = 0;
bool pngHeader = false; //PNG HEADER检测
using (var response = (HttpWebResponse)request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
@@ -555,6 +556,10 @@ namespace N_m3u8DL_CLI
totalLen = response.ContentLength;
byte[] bArr = new byte[1024];
int size = responseStream.Read(bArr, 0, (int)bArr.Length);
if (!pngHeader && size > 3 && 137 == bArr[0] && 80 == bArr[1] && 78 == bArr[2] && 71 == bArr[3]) ;
{
pngHeader = true;
}
while (size > 0)
{
stream.Write(bArr, 0, size);
@@ -579,6 +584,8 @@ namespace N_m3u8DL_CLI
try { File.Delete(path); } catch (Exception) { }
if (totalLen != -1 && downLen != totalLen)
try { File.Delete(path); } catch (Exception) { }
if (pngHeader)
TrySkipPngHeader(path);
}
catch (Exception e)
{
@@ -587,6 +594,51 @@ namespace N_m3u8DL_CLI
}
}
/// <summary>
/// 用于处理利用图床上传TS导致前面被插入PNG Header的情况
/// </summary>
/// <param name="filePath"></param>
public static void TrySkipPngHeader(string filePath)
{
var u = File.ReadAllBytes(filePath);
if (0x47 == u[0])
{
return;
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[118] && 130 == u[119])
{
u = u.Skip(120).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[6100] && 130 == u[6101])
{
u = u.Skip(6102).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[67] && 130 == u[68])
{
u = u.Skip(69).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3] && 96 == u[769] && 130 == u[770])
{
u = u.Skip(771).ToArray();
}
else if (137 == u[0] && 80 == u[1] && 78 == u[2] && 71 == u[3])
{
//确定是PNG但是需要手动查询结尾标记(0x60 0x82 0x47)
int skip = 0;
for (int i = 4; i < u.Length - 3; i++)
{
if (u[i] == 0x60 && u[i + 1] == 0x82 && u[i + 2] == 0x47)
{
skip = i + 2;
break;
}
}
u = u.Skip(skip).ToArray();
}
File.WriteAllBytes(filePath, u);
}
//格式化json字符串
public static string ConvertJsonString(string str)
{
@@ -1048,6 +1100,8 @@ namespace N_m3u8DL_CLI
if (i > 0)
{
int newTime = Convert.ToInt32(Regex.Match(tmp, "X-TIMESTAMP-MAP.*MPEGTS:(\\d+)").Groups[1].Value);
if (newTime == 900000)
continue;
//计算偏移量
//LOGGER.PrintLine((newTime - baseTime).ToString());
addTime = addTime + ((newTime - baseTime) / 100);

View File

@@ -40,11 +40,11 @@
<ItemGroup>
<Reference Include="Microsoft.JScript" />
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.dll</HintPath>
<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.dll</HintPath>
<HintPath>..\packages\NiL.JS.2.5.1428\lib\net45\NiL.JS.dll</HintPath>
</Reference>
<Reference Include="PresentationFramework" />
<Reference Include="System" />
@@ -63,6 +63,9 @@
<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" />
@@ -74,6 +77,21 @@
<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>
@@ -82,9 +100,6 @@
</None>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="bin\Debug\Newtonsoft.Json.dll" />
</ItemGroup>
<ItemGroup>
<COMReference Include="Scripting">
<Guid>{420B2830-E718-11CF-893D-00A0C9054228}</Guid>
@@ -100,16 +115,18 @@
<Content Include="logo_3Iv_icon.ico" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<EmbeddedResource Include="strings.en-US.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<LastGenOutput>strings.en-US.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="strings.en-US.resx" />
<EmbeddedResource Include="strings.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>strings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="strings.zh-TW.resx" />
<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>

View File

@@ -94,7 +94,14 @@ namespace N_m3u8DL_CLI
LOGGER.PrintLine(strings.downloadingM3u8, LOGGER.Warning);
if (M3u8Url.StartsWith("http"))
m3u8Content = Global.GetWebSource(M3u8Url, headers);
{
if (M3u8Url.Contains("nfmovies.com/hls"))
m3u8Content = DecodeNfmovies.DecryptM3u8(Global.HttpDownloadFileToBytes(M3u8Url, headers));
else if (M3u8Url.Contains("hls.ddyunp.com/ddyun"))
m3u8Content = DecodeDdyun.DecryptM3u8(Global.HttpDownloadFileToBytes(DecodeDdyun.GetVaildM3u8Url(M3u8Url), headers));
else
m3u8Content = Global.GetWebSource(M3u8Url, headers);
}
else if (M3u8Url.StartsWith("file:"))
{
Uri t = new Uri(M3u8Url);
@@ -259,11 +266,23 @@ namespace N_m3u8DL_CLI
else if (line.StartsWith(HLSTags.ext_x_version)) ;
else if (line.StartsWith(HLSTags.ext_x_allow_cache)) ;
//解析KEY
else if (line.StartsWith(HLSTags.ext_x_key) && string.IsNullOrEmpty(keyFile) && string.IsNullOrEmpty(keyBase64))
else if (line.StartsWith(HLSTags.ext_x_key))
{
m3u8CurrentKey = ParseKey(line);
//存储为上一行的key信息
lastKeyLine = line;
//自定义KEY情况 判断是否需要读取IV
if (!string.IsNullOrEmpty(keyFile) || !string.IsNullOrEmpty(keyBase64))
{
if (m3u8CurrentKey[2] == "" && line.Contains("IV=0x"))
{
var temp = ParseKey(line);
m3u8CurrentKey[2] = temp[2]; //使用m3u8中的IV
}
}
else
{
m3u8CurrentKey = ParseKey(line);
//存储为上一行的key信息
lastKeyLine = line;
}
}
//解析分片时长(暂时不考虑标题属性)
else if (line.StartsWith(HLSTags.extinf))
@@ -365,6 +384,10 @@ namespace N_m3u8DL_CLI
{
segUrl += new Regex("\\?__gda__.*").Match(M3u8Url).Value;
}
if (M3u8Url.Contains("//dlsc.hcs.cmvideo.cn") && (segUrl.EndsWith(".ts") || segUrl.EndsWith(".mp4")))
{
segUrl += new Regex("\\?.*").Match(M3u8Url).Value;
}
segInfo.Add("segUri", segUrl);
segments.Add(segInfo);
segInfo = new JObject();

View File

@@ -235,14 +235,11 @@ namespace N_m3u8DL_CLI.NetCore
/// 2020年2月23日
/// - 不支持的加密方式将标记为NOTSUPPORTED并强制启用二进制合并
/// - 启用二进制合并的情况下如果m3u8文件中存在map文件则合并为mp4格式
/// - 支持优酷视频解密
/// 2020年2月24日
/// - 直播流录制优化逻辑,避免忙等待
/// - 直播Waiting时不再输出Parser内容
/// - 直播录制的日志记录
/// - 增加新的选项--liveRecDur限制直播录制时长
/// 2020年2月25日
/// - 修复优酷解密过程错误写入冗余数据的bug
/// 2020年2月27日
/// - 细节bug修复
/// 2020年2月28日
@@ -266,7 +263,6 @@ namespace N_m3u8DL_CLI.NetCore
/// - 增加同名文件合并时共存策略
/// 2020年4月17日
/// - 优化异常捕获
/// - 控制台输出设置为UTF-8
/// - 细节优化
/// 2020年4月22日
/// - 51cto getsign
@@ -287,6 +283,17 @@ namespace N_m3u8DL_CLI.NetCore
/// 2020年8月9日
/// - 修复IV错误导致的AES-128解密异常问题
/// - 支持自定义IV(--useKeyIV)
/// 2020年9月12日
/// - 支持nfmovies m3u8解密
/// - 支持自动去除PNG Header(https://puui.qpic.cn/newsapp_ls/0/12418116195/0)
/// - 修复相对时间的vtt合并的一些错误逻辑(还存在问题)
/// 2020年9月19日
/// - 在自定义KEY且未自定义IV情况下自动读取m3u8中存在的IV
/// - 支持阿房影视等ddyun m3u8解密
/// 2020年10月14日
/// - 咪咕分片链接后拼接m3u8_url参数
/// - 修复文件名过长导致的BUG
/// - 优化ffmpeg调用逻辑
/// </summary>
///
@@ -326,14 +333,13 @@ namespace N_m3u8DL_CLI.NetCore
{
SetConsoleCtrlHandler(cancelHandler, true);
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
string loc = "zh-CN";
string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
if (currLoc == "zh-TW" || currLoc == "zh-HK" || currLoc == "zh-MO")
{
loc = "zh-TW";
}
else if (loc == "zh-CN" || loc == "zh-SG")
else if (loc == "zh-CN" || loc == "zh-SG")
{
loc = "zh-CN";
}
@@ -353,7 +359,15 @@ namespace N_m3u8DL_CLI.NetCore
string fileName = "";
//寻找ffmpeg.exe
if (!File.Exists("ffmpeg.exe") && !File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "ffmpeg.exe")))
if (File.Exists("ffmpeg.exe"))
{
FFmpeg.FFMPEG_PATH = Path.Combine(Environment.CurrentDirectory, "ffmpeg.exe");
}
else if (File.Exists(Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "ffmpeg.exe")))
{
FFmpeg.FFMPEG_PATH = Path.Combine(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), "ffmpeg.exe");
}
else
{
try
{
@@ -361,7 +375,10 @@ namespace N_m3u8DL_CLI.NetCore
foreach (var de in EnvironmentPath)
{
if (File.Exists(Path.Combine(de.Trim('\"').Trim(), "ffmpeg.exe")))
{
FFmpeg.FFMPEG_PATH = Path.Combine(de.Trim('\"').Trim(), "ffmpeg.exe");
goto HasFFmpeg;
}
}
}
catch (Exception)
@@ -375,8 +392,7 @@ namespace N_m3u8DL_CLI.NetCore
Console.ResetColor(); //将控制台的前景色和背景色设为默认值
Console.WriteLine(strings.ffmpegTip);
Console.WriteLine();
Console.WriteLine("x86 https://ffmpeg.zeranoe.com/builds/win32/static/");
Console.WriteLine("x64 https://ffmpeg.zeranoe.com/builds/win64/static/");
Console.WriteLine("http://ffmpeg.org/download.html#build-windows");
Console.WriteLine();
Console.WriteLine(strings.pressAnyKeyExit);
Console.ReadKey();
@@ -462,10 +478,6 @@ namespace N_m3u8DL_CLI.NetCore
{
muxFastStart = true;
}
if (arguments.Has("--enableYouKuAes"))
{
Downloader.YouKuAES = true;
}
if (arguments.Has("--disableIntegrityCheck"))
{
DownloadManager.DisableIntegrityCheck = true;
@@ -647,6 +659,20 @@ namespace N_m3u8DL_CLI.NetCore
string m3u8Content = string.Empty;
bool isVOD = true;
//避免文件路径过长
if (workDir.Length >= 200)
{
//目录不能随便改 直接抛出异常
throw new Exception("保存目录过长!");
}
else if (workDir.Length + fileName.Length >= 200)
{
//尝试缩短文件名
while (workDir.Length + fileName.Length >= 200)
{
fileName = fileName.Substring(0, fileName.Length - 1);
}
}
//开始解析

View File

@@ -1,4 +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>