You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-10-09 00:22:13 +02:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1edc1a43fe | ||
![]() |
01e7735018 | ||
![]() |
a0c41d6116 | ||
![]() |
c3c25774de |
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
/**
|
||||
* https://www.cnblogs.com/linxuanchen/p/c-sharp-command-line-argument-parser.html
|
||||
*/
|
||||
public class CommandLineArgument
|
||||
{
|
||||
List<CommandLineArgument> _arguments;
|
||||
|
36
N_m3u8DL-CLI/DecryptNfmovies.cs
Normal file
36
N_m3u8DL-CLI/DecryptNfmovies.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace N_m3u8DL_CLI
|
||||
{
|
||||
class DecryptNfmovies
|
||||
{
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -192,7 +192,7 @@ namespace N_m3u8DL_CLI
|
||||
if (sd.SegDur < 0) sd.SegDur = 0; //防止负数
|
||||
sd.FileUrl = firstSeg["segUri"].Value<string>();
|
||||
//VTT字幕
|
||||
if (isVTT == false && sd.FileUrl.Trim('\"').EndsWith(".vtt"))
|
||||
if (isVTT == false && (sd.FileUrl.Trim('\"').EndsWith(".vtt") || sd.FileUrl.Trim('\"').EndsWith(".webvtt")))
|
||||
isVTT = true;
|
||||
sd.Method = firstSeg["method"].Value<string>();
|
||||
if (sd.Method != "NONE")
|
||||
@@ -281,8 +281,8 @@ namespace N_m3u8DL_CLI
|
||||
sd.SegDur = info["duration"].Value<double>();
|
||||
if (sd.SegDur < 0) sd.SegDur = 0; //防止负数
|
||||
sd.FileUrl = info["segUri"].Value<string>();
|
||||
//VTT字幕
|
||||
if (isVTT == false && sd.FileUrl.Trim('\"').EndsWith(".vtt"))
|
||||
//VTT字幕
|
||||
if (isVTT == false && (sd.FileUrl.Trim('\"').EndsWith(".vtt") || sd.FileUrl.Trim('\"').EndsWith(".webvtt")))
|
||||
isVTT = true;
|
||||
sd.Method = info["method"].Value<string>();
|
||||
if (sd.Method != "NONE")
|
||||
@@ -370,7 +370,7 @@ namespace N_m3u8DL_CLI
|
||||
}
|
||||
else //开始合并
|
||||
{
|
||||
LOGGER.PrintLine(strings.downloadComplete + (DisableIntegrityCheck ? "(" + strings.disableIntegrityCheck + ")" : ""));
|
||||
LOGGER.PrintLine(strings.downloadComplete + (DisableIntegrityCheck ? "("+strings.disableIntegrityCheck+")" : ""));
|
||||
Console.WriteLine();
|
||||
if (NoMerge == false)
|
||||
{
|
||||
@@ -381,7 +381,10 @@ namespace N_m3u8DL_CLI
|
||||
LOGGER.PrintLine(strings.startMerging, LOGGER.Warning);
|
||||
//VTT字幕
|
||||
if (isVTT == true)
|
||||
{
|
||||
MuxFormat = "vtt";
|
||||
Global.ReAdjustVtt(Global.GetFiles(DownDir + "\\Part_0", ".ts"));
|
||||
}
|
||||
//只有一个Part直接用ffmpeg合并
|
||||
if (PartsCount == 1)
|
||||
{
|
||||
@@ -497,6 +500,9 @@ namespace N_m3u8DL_CLI
|
||||
parser.Parse(); //开始解析
|
||||
Thread.Sleep(1000);
|
||||
LOGGER.CursorIndex = 5;
|
||||
Global.HadReadInfo = false;
|
||||
Global.VIDEO_TYPE = "";
|
||||
Global.AUDIO_TYPE = "";
|
||||
DoDownload();
|
||||
}
|
||||
if (externalSub) //下载独立字幕
|
||||
@@ -509,16 +515,19 @@ namespace N_m3u8DL_CLI
|
||||
parser.Headers = Headers; //继承Header
|
||||
parser.BaseUrl = "";
|
||||
parser.M3u8Url = externalSubUrl;
|
||||
parser.DownName = DownName + "(Subtitle)";
|
||||
parser.DownName = DownName.Replace("(Audio)", "") + "(Subtitle)";
|
||||
parser.DownDir = Path.Combine(Path.GetDirectoryName(DownDir), parser.DownName);
|
||||
LOGGER.WriteLine(strings.startParsing + externalSubUrl);
|
||||
LOGGER.WriteLine(strings.downloadingExternalSubtitleTrack);
|
||||
DownName = DownName + "(Subtitle)";
|
||||
DownName = parser.DownName;
|
||||
fflogName = "_ffreport(Subtitle).log";
|
||||
DownDir = parser.DownDir;
|
||||
parser.Parse(); //开始解析
|
||||
Thread.Sleep(1000);
|
||||
LOGGER.CursorIndex = 5;
|
||||
Global.HadReadInfo = false;
|
||||
Global.VIDEO_TYPE = "";
|
||||
Global.AUDIO_TYPE = "";
|
||||
DoDownload();
|
||||
}
|
||||
LOGGER.PrintLine(strings.taskDone, LOGGER.Warning);
|
||||
@@ -530,7 +539,7 @@ namespace N_m3u8DL_CLI
|
||||
|
||||
FFmpeg.OutPutPath = Path.Combine(Directory.GetParent(DownDir).FullName, DownName);
|
||||
FFmpeg.ReportFile = driverName + "\\:" + exePath.Remove(0, exePath.IndexOf(':') + 1).Replace("\\", "/") + "/Logs/" + Path.GetFileNameWithoutExtension(LOGGER.LOGFILE) + fflogName;
|
||||
|
||||
|
||||
//合并分段
|
||||
LOGGER.PrintLine(strings.startMerging);
|
||||
for (int i = 0; i < PartsCount; i++)
|
||||
@@ -637,6 +646,9 @@ namespace N_m3u8DL_CLI
|
||||
parser.Parse(); //开始解析
|
||||
Thread.Sleep(1000);
|
||||
LOGGER.CursorIndex = 5;
|
||||
Global.HadReadInfo = false;
|
||||
Global.VIDEO_TYPE = "";
|
||||
Global.AUDIO_TYPE = "";
|
||||
DoDownload();
|
||||
}
|
||||
if (externalSub) //下载独立字幕
|
||||
@@ -649,16 +661,19 @@ namespace N_m3u8DL_CLI
|
||||
parser.Headers = Headers; //继承Header
|
||||
parser.BaseUrl = "";
|
||||
parser.M3u8Url = externalSubUrl;
|
||||
parser.DownName = DownName + "(Subtitle)";
|
||||
parser.DownName = DownName.Replace("(Audio)", "") + "(Subtitle)";
|
||||
parser.DownDir = Path.Combine(Path.GetDirectoryName(DownDir), parser.DownName);
|
||||
LOGGER.WriteLine(strings.startParsing + externalSubUrl);
|
||||
LOGGER.WriteLine(strings.downloadingExternalSubtitleTrack);
|
||||
DownName = DownName + "(Subtitle)";
|
||||
DownName = parser.DownName;
|
||||
fflogName = "_ffreport(Subtitle).log";
|
||||
DownDir = parser.DownDir;
|
||||
parser.Parse(); //开始解析
|
||||
Thread.Sleep(1000);
|
||||
LOGGER.CursorIndex = 5;
|
||||
Global.HadReadInfo = false;
|
||||
Global.VIDEO_TYPE = "";
|
||||
Global.AUDIO_TYPE = "";
|
||||
DoDownload();
|
||||
}
|
||||
LOGGER.PrintLine(strings.taskDone, LOGGER.Warning);
|
||||
|
@@ -30,8 +30,8 @@ namespace N_m3u8DL_CLI
|
||||
|
||||
|
||||
/*===============================================================================*/
|
||||
static string nowVer = "2.7.1";
|
||||
static string nowDate = "20200719";
|
||||
static string nowVer = "2.7.3";
|
||||
static string nowDate = "20200914";
|
||||
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)
|
||||
{
|
||||
@@ -1015,5 +1067,58 @@ namespace N_m3u8DL_CLI
|
||||
return wr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过X-TIMESTAMP-MAP 调整VTT字幕的时间轴
|
||||
*/
|
||||
public static void ReAdjustVtt(string[] vtts)
|
||||
{
|
||||
string MsToTime(int ms)
|
||||
{
|
||||
TimeSpan ts = new TimeSpan(0, 0, 0, 0, ms);
|
||||
string str = "";
|
||||
str = (ts.Hours.ToString("00") + ":") + ts.Minutes.ToString("00") + ":" + ts.Seconds.ToString("00") + "." + ts.Milliseconds.ToString("000");
|
||||
return str;
|
||||
}
|
||||
|
||||
int TimeToMs(string line)
|
||||
{
|
||||
int hh = Convert.ToInt32(line.Split(':')[0]);
|
||||
int mm = Convert.ToInt32(line.Split(':')[1]);
|
||||
int ss = Convert.ToInt32(line.Split(':')[2].Split('.')[0]);
|
||||
int ms = Convert.ToInt32(line.Split(':')[2].Split('.')[1]);
|
||||
return hh * 60 * 60 * 1000 + mm * 60 * 1000 + ss * 1000 + ms;
|
||||
}
|
||||
|
||||
int addTime = 0;
|
||||
int baseTime = 0;
|
||||
for (int i = 0; i < vtts.Length; i++)
|
||||
{
|
||||
string tmp = File.ReadAllText(vtts[i], Encoding.UTF8);
|
||||
if (!Regex.IsMatch(tmp, "X-TIMESTAMP-MAP.*MPEGTS:(\\d+)"))
|
||||
break;
|
||||
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);
|
||||
if ((newTime - baseTime) == 6300000)
|
||||
addTime -= 3000;
|
||||
//将新的作为基准时间
|
||||
baseTime = newTime;
|
||||
foreach (Match m in Regex.Matches(tmp, @"(\d{2}:\d{2}:\d{2}\.\d{3}) --> (\d{2}:\d{2}:\d{2}\.\d{3})"))
|
||||
{
|
||||
string start = m.Groups[1].Value;
|
||||
string end = m.Groups[2].Value;
|
||||
tmp = tmp.Replace(m.Value, MsToTime(TimeToMs(start) + addTime) + " --> " + MsToTime(TimeToMs(end) + addTime));
|
||||
}
|
||||
}
|
||||
File.WriteAllText(vtts[i], Regex.Replace(tmp, "X-TIMESTAMP-MAP=.*", ""), Encoding.UTF8);
|
||||
}
|
||||
//Console.ReadLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -41,10 +41,10 @@
|
||||
<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>
|
||||
<HintPath>..\..\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,7 +63,9 @@
|
||||
<Compile Include="CommandLineArgument.cs" />
|
||||
<Compile Include="CommandLineArgumentParser.cs" />
|
||||
<Compile Include="Decode51CtoKey.cs" />
|
||||
<Compile Include="DecodeImooc.cs" />
|
||||
<Compile Include="Decrypter.cs" />
|
||||
<Compile Include="DecryptNfmovies.cs" />
|
||||
<Compile Include="FFmpeg.cs" />
|
||||
<Compile Include="Global.cs" />
|
||||
<Compile Include="HLSLiveDownloader.cs" />
|
||||
@@ -74,6 +76,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>
|
||||
@@ -100,16 +117,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>
|
@@ -24,6 +24,7 @@ namespace N_m3u8DL_CLI
|
||||
private string downName = string.Empty;
|
||||
private string keyFile = string.Empty;
|
||||
private string keyBase64 = string.Empty;
|
||||
private string keyIV = string.Empty;
|
||||
private bool liveStream = false;
|
||||
private long bestBandwidth = 0;
|
||||
private string bestUrl = string.Empty;
|
||||
@@ -60,6 +61,7 @@ namespace N_m3u8DL_CLI
|
||||
public string KeyFile { get => keyFile; set => keyFile = value; }
|
||||
public string KeyBase64 { get => keyBase64; set => keyBase64 = value; }
|
||||
public bool LiveStream { get => liveStream; set => liveStream = value; }
|
||||
public string KeyIV { get => keyIV; set => keyIV = value; }
|
||||
|
||||
public void Parse()
|
||||
{
|
||||
@@ -92,7 +94,12 @@ 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 = DecryptNfmovies.DecryptM3u8(Global.HttpDownloadFileToBytes(M3u8Url, headers));
|
||||
else
|
||||
m3u8Content = Global.GetWebSource(M3u8Url, headers);
|
||||
}
|
||||
else if (M3u8Url.StartsWith("file:"))
|
||||
{
|
||||
Uri t = new Uri(M3u8Url);
|
||||
@@ -125,7 +132,7 @@ namespace N_m3u8DL_CLI
|
||||
File.WriteAllText(m3u8SavePath, m3u8Content);
|
||||
|
||||
//针对优酷#EXT-X-VERSION:7杜比视界片源修正
|
||||
if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && m3u8Content.Contains("ott.cibntv.net") && m3u8Content.Contains("ccode="))
|
||||
if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && m3u8Content.Contains("ott.cibntv.net") && m3u8Content.Contains("ccode="))
|
||||
{
|
||||
Regex ykmap = new Regex("#EXT-X-DISCONTINUITY\\s+#EXT-X-MAP:URI=\\\"(.*?)\\\",BYTERANGE=\\\"(.*?)\\\"");
|
||||
foreach (Match m in ykmap.Matches(m3u8Content))
|
||||
@@ -151,13 +158,22 @@ namespace N_m3u8DL_CLI
|
||||
|
||||
if (!string.IsNullOrEmpty(keyBase64))
|
||||
{
|
||||
string line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\"";
|
||||
string line = "";
|
||||
if (string.IsNullOrEmpty(keyIV))
|
||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\"";
|
||||
else
|
||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\",IV=0x{keyIV.Replace("0x", "")}";
|
||||
m3u8CurrentKey = ParseKey(line);
|
||||
}
|
||||
if (!string.IsNullOrEmpty(keyFile))
|
||||
{
|
||||
string line = "";
|
||||
Uri u = new Uri(keyFile);
|
||||
string line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\"";
|
||||
if (string.IsNullOrEmpty(keyIV))
|
||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\"";
|
||||
else
|
||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\",IV=0x{keyIV.Replace("0x", "")}";
|
||||
|
||||
m3u8CurrentKey = ParseKey(line);
|
||||
}
|
||||
|
||||
@@ -267,7 +283,7 @@ namespace N_m3u8DL_CLI
|
||||
segInfo.Add("key", m3u8CurrentKey[1]);
|
||||
//没有读取到IV,自己生成
|
||||
if (m3u8CurrentKey[2] == "")
|
||||
segInfo.Add("iv", "0x" + Convert.ToString(segIndex, 2).PadLeft(32, '0'));
|
||||
segInfo.Add("iv", "0x" + Convert.ToString(segIndex, 16).PadLeft(32, '0'));
|
||||
else
|
||||
segInfo.Add("iv", m3u8CurrentKey[2]);
|
||||
}
|
||||
@@ -580,8 +596,14 @@ namespace N_m3u8DL_CLI
|
||||
MasterListCheck();
|
||||
}
|
||||
|
||||
bool downloadingM3u8KeyTip = false;
|
||||
public string[] ParseKey(string line)
|
||||
{
|
||||
if (!downloadingM3u8KeyTip)
|
||||
{
|
||||
LOGGER.PrintLine(strings.downloadingM3u8Key, LOGGER.Warning);
|
||||
downloadingM3u8KeyTip = true;
|
||||
}
|
||||
string[] tmp = line.Replace(HLSTags.ext_x_key + ":", "").Split(',');
|
||||
string[] key = new string[] { "NONE", "", "" };
|
||||
string u_l = Global.GetTagAttribute(lastKeyLine.Replace(HLSTags.ext_x_key + ":", ""), "URI");
|
||||
@@ -608,7 +630,6 @@ namespace N_m3u8DL_CLI
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.PrintLine(strings.downloadingM3u8Key, LOGGER.Warning);
|
||||
LOGGER.WriteLine(strings.downloadingM3u8Key + " " + key[1]);
|
||||
if (key[1].StartsWith("http"))
|
||||
{
|
||||
@@ -632,11 +653,11 @@ namespace N_m3u8DL_CLI
|
||||
{
|
||||
indexs = "0-1-2-3-4-5-6-7-18-16-15-13-12-11-10-8".Split('-');
|
||||
}
|
||||
else if (algorithmNum == 0)
|
||||
else if (algorithmNum == 0)
|
||||
{
|
||||
indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-');
|
||||
}
|
||||
else if(algorithmNum == 2)
|
||||
else if (algorithmNum == 2)
|
||||
{
|
||||
var a_CODE = (int)Encoding.ASCII.GetBytes("a")[0];
|
||||
|
||||
@@ -681,7 +702,7 @@ namespace N_m3u8DL_CLI
|
||||
return key;
|
||||
}
|
||||
}
|
||||
else if(encKey.Length == 17)
|
||||
else if (encKey.Length == 17)
|
||||
{
|
||||
indexs = "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16".Split('-');
|
||||
}
|
||||
|
@@ -273,6 +273,20 @@ namespace N_m3u8DL_CLI.NetCore
|
||||
/// 2020年7月18日
|
||||
/// - 从当前路径和exe路径同时寻找ffmpeg
|
||||
/// - 支持多语言本地化(简繁英)
|
||||
/// 2020年8月4日
|
||||
/// - 修复外挂字幕命名问题
|
||||
/// - 修复外挂字幕识别问题
|
||||
/// - 修复外挂轨道的一些逻辑问题
|
||||
/// - 优化多语言识别逻辑
|
||||
/// 2020年8月5日
|
||||
/// - 支持相对时间的vtt合并(还存在问题)
|
||||
/// 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合并的一些错误逻辑(还存在问题)
|
||||
/// </summary>
|
||||
///
|
||||
|
||||
@@ -318,7 +332,11 @@ namespace N_m3u8DL_CLI.NetCore
|
||||
{
|
||||
loc = "zh-TW";
|
||||
}
|
||||
else if (loc != "zh-CN" && loc != "zh-SG")
|
||||
else if (loc == "zh-CN" || loc == "zh-SG")
|
||||
{
|
||||
loc = "zh-CN";
|
||||
}
|
||||
else
|
||||
{
|
||||
loc = "en-US";
|
||||
}
|
||||
@@ -384,6 +402,7 @@ namespace N_m3u8DL_CLI.NetCore
|
||||
string reqHeaders = "";
|
||||
string keyFile = "";
|
||||
string keyBase64 = "";
|
||||
string keyIV = "";
|
||||
string muxSetJson = "MUXSETS.json";
|
||||
string workDir = CURRENT_PATH + "\\Downloads";
|
||||
bool muxFastStart = false;
|
||||
@@ -472,6 +491,10 @@ namespace N_m3u8DL_CLI.NetCore
|
||||
{
|
||||
keyBase64 = arguments.Get("--useKeyBase64").Next;
|
||||
}
|
||||
if (arguments.Has("--useKeyIV"))
|
||||
{
|
||||
keyIV = arguments.Get("--useKeyIV").Next;
|
||||
}
|
||||
if (arguments.Has("--stopSpeed"))
|
||||
{
|
||||
Global.STOP_SPEED = Convert.ToInt64(arguments.Get("--stopSpeed").Next);
|
||||
@@ -631,6 +654,7 @@ namespace N_m3u8DL_CLI.NetCore
|
||||
parser.DownDir = Path.Combine(workDir, parser.DownName);
|
||||
parser.M3u8Url = testurl;
|
||||
parser.KeyBase64 = keyBase64;
|
||||
parser.KeyIV = keyIV;
|
||||
parser.KeyFile = keyFile;
|
||||
if (baseUrl != "")
|
||||
parser.BaseUrl = baseUrl;
|
||||
|
@@ -197,6 +197,7 @@
|
||||
--muxSetJson File Set a json file for mux
|
||||
--useKeyFile File Use 16 bytes file as KEY for AES-128 decryption
|
||||
--useKeyBase64 Base64String Use Base64 String as KEY for AES-128 decryption
|
||||
--useKeyIV HEXString Use HEX String as IV for AES-128 decryption
|
||||
--downloadRange Range Set range for a video
|
||||
--stopSpeed Number Speed below this, retry(KB/s)
|
||||
--maxSpeed Number Set max download speed(KB/s)
|
||||
|
@@ -197,6 +197,7 @@
|
||||
--muxSetJson File 使用外部json文件定义混流选项
|
||||
--useKeyFile File 使用外部16字节文件定义AES-128解密KEY
|
||||
--useKeyBase64 Base64String 使用Base64字符串定义AES-128解密KEY
|
||||
--useKeyIV HEXString 使用HEX字符串定义AES-128解密IV
|
||||
--downloadRange Range 仅下载视频的一部分分片或长度
|
||||
--liveRecDur HH:MM:SS 直播录制时,达到此长度自动退出软件
|
||||
--stopSpeed Number 当速度低于此值时,重试(单位为KB/s)
|
||||
|
@@ -197,6 +197,7 @@
|
||||
--muxSetJson File 使用外部json文件定義混流選項
|
||||
--useKeyFile File 使用外部16字節文件定義AES-128解密KEY
|
||||
--useKeyBase64 Base64String 使用Base64字符串定義AES-128解密KEY
|
||||
--useKeyIV HEXString 使用HEX字符串定義AES-128解密IV
|
||||
--downloadRange Range 僅下載視頻的壹部分分片或長度
|
||||
--liveRecDur HH:MM:SS 直播錄制時,達到此長度自動退出軟件
|
||||
--stopSpeed Number 當速度低於此值時,重試(單位為KB/s)
|
||||
|
@@ -61,6 +61,7 @@ N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]
|
||||
--muxSetJson File 使用外部json文件定义混流选项
|
||||
--useKeyFile File 使用外部16字节文件定义AES-128解密KEY
|
||||
--useKeyBase64 Base64String 使用Base64字符串定义AES-128解密KEY
|
||||
--useKeyIV HEXString 使用HEX字符串定义AES-128解密IV
|
||||
--downloadRange Range 仅下载视频的一部分分片或长度
|
||||
--liveRecDur HH:MM:SS 直播录制时,达到此长度自动退出软件
|
||||
--stopSpeed Number 当速度低于此值时,重试(单位为KB/s)
|
||||
|
@@ -43,6 +43,7 @@ N_m3u8DL-CLI.exe <URL|JSON|FILE> [OPTIONS]
|
||||
--muxSetJson File Set a json file for mux
|
||||
--useKeyFile File Use 16 bytes file as KEY for AES-128 decryption
|
||||
--useKeyBase64 Base64String Use Base64 String as KEY for AES-128 decryption
|
||||
--useKeyIV HEXString Use HEX String as IV for AES-128 decryption
|
||||
--downloadRange Range Set range for a video
|
||||
--stopSpeed Number Speed below this, retry(KB/s)
|
||||
--maxSpeed Number Set max download speed(KB/s)
|
||||
|
Reference in New Issue
Block a user