You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-09-13 22:40:51 +02:00
Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
83915ff606 | ||
![]() |
9cd4746f33 | ||
![]() |
33a5b917ac | ||
![]() |
f69978bd82 | ||
![]() |
d9fd526886 | ||
![]() |
1cc8ecfaaf | ||
![]() |
2bc2dde2ad | ||
![]() |
c3ddcf9e0e | ||
![]() |
ba5d20dd02 | ||
![]() |
16f705fe66 | ||
![]() |
d141cabc4a | ||
![]() |
adcf884a93 | ||
![]() |
9d903a025f | ||
![]() |
0bd23ab641 | ||
![]() |
a38f27ccd7 | ||
![]() |
3acec5efd3 | ||
![]() |
90874e4bfe | ||
![]() |
b94768e3e8 | ||
![]() |
311f3b882e | ||
![]() |
4b4f537984 | ||
![]() |
8032d50b42 | ||
![]() |
f615764e55 | ||
![]() |
ccaa200ef8 | ||
![]() |
6058d878eb | ||
![]() |
c712c6dee0 | ||
![]() |
bb24bb998f | ||
![]() |
e9d951efa5 | ||
![]() |
6f88a805ef | ||
![]() |
6aa6d63a8d | ||
![]() |
147246caba | ||
![]() |
35c1ee4777 | ||
![]() |
8b32081b85 | ||
![]() |
c00de328d1 | ||
![]() |
d5193c1645 | ||
![]() |
c06fbf5820 | ||
![]() |
e700edba56 | ||
![]() |
aad948da7c | ||
![]() |
14f7b20176 | ||
![]() |
a66a9a4096 | ||
![]() |
c862f23a9c | ||
![]() |
1a722e80de | ||
![]() |
eff43e8ac3 | ||
![]() |
7648f8f8dc | ||
![]() |
1edc1a43fe | ||
![]() |
01e7735018 | ||
![]() |
a0c41d6116 | ||
![]() |
c3c25774de |
34
.github/workflows/build_latest.yml
vendored
Normal file
34
.github/workflows/build_latest.yml
vendored
Normal 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\
|
@@ -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;
|
||||
|
41
N_m3u8DL-CLI/DecodeDdyun.cs
Normal file
41
N_m3u8DL-CLI/DecodeDdyun.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
36
N_m3u8DL-CLI/DecodeNfmovies.cs
Normal file
36
N_m3u8DL-CLI/DecodeNfmovies.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -10,19 +10,20 @@ namespace N_m3u8DL_CLI
|
||||
{
|
||||
class FFmpeg
|
||||
{
|
||||
private static string outPutPath = string.Empty;
|
||||
private static string reportFile = string.Empty;
|
||||
private static bool useAACFilter = false; //是否启用滤镜
|
||||
private static bool writeDate = true; //是否写入录制日期
|
||||
public static string OutPutPath { get => outPutPath; set => outPutPath = value; }
|
||||
public static string ReportFile { get => reportFile; set => reportFile = value; }
|
||||
public static bool UseAACFilter { get => useAACFilter; set => useAACFilter = value; }
|
||||
public static bool WriteDate { get => writeDate; set => writeDate = value; }
|
||||
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()}"))
|
||||
{
|
||||
@@ -50,7 +51,7 @@ namespace N_m3u8DL_CLI
|
||||
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=\"" + DateTime.Now.ToString("o") + "\"" : "") +
|
||||
+ (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 + "\" ";
|
||||
@@ -83,7 +84,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 +93,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 +105,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));
|
||||
|
File diff suppressed because it is too large
Load Diff
620
N_m3u8DL-CLI/MPDParser.cs
Normal file
620
N_m3u8DL-CLI/MPDParser.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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" />
|
||||
@@ -70,10 +73,26 @@
|
||||
<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>
|
||||
@@ -82,9 +101,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 +116,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>
|
@@ -17,14 +17,6 @@ namespace N_m3u8DL_CLI
|
||||
string[] m3u8CurrentKey = new string[] { "NONE", "", "" };
|
||||
private string m3u8SavePath = string.Empty;
|
||||
private string jsonSavePath = string.Empty;
|
||||
private string headers = string.Empty;
|
||||
private string baseUrl = string.Empty;
|
||||
private string m3u8Url = string.Empty;
|
||||
private string downDir = string.Empty;
|
||||
private string downName = string.Empty;
|
||||
private string keyFile = string.Empty;
|
||||
private string keyBase64 = string.Empty;
|
||||
private bool liveStream = false;
|
||||
private long bestBandwidth = 0;
|
||||
private string bestUrl = string.Empty;
|
||||
private string bestUrlAudio = string.Empty;
|
||||
@@ -36,33 +28,31 @@ namespace N_m3u8DL_CLI
|
||||
//存放多轨道的信息
|
||||
private ArrayList extLists = new ArrayList();
|
||||
private static bool isQiQiuYun = false;
|
||||
//存放Range信息,允许用户只下载部分视频
|
||||
private static int rangeStart = 0;
|
||||
private static int rangeEnd = -1;
|
||||
//存放Range信息,允许用户只下载部分视频
|
||||
private static string durStart = "";
|
||||
private static string durEnd = "";
|
||||
//是否自动清除优酷广告分片
|
||||
private static bool delAd = true;
|
||||
//标记是否已清除优酷广告分片
|
||||
private static bool hasAd = false;
|
||||
|
||||
public string BaseUrl { get => baseUrl; set => baseUrl = value; }
|
||||
public string M3u8Url { get => m3u8Url; set => m3u8Url = value; }
|
||||
public string DownDir { get => downDir; set => downDir = value; }
|
||||
public string DownName { get => downName; set => downName = value; }
|
||||
public string Headers { get => headers; set => headers = value; }
|
||||
public static int RangeStart { get => rangeStart; set => rangeStart = value; }
|
||||
public static int RangeEnd { get => rangeEnd; set => rangeEnd = value; }
|
||||
public static bool DelAd { get => delAd; set => delAd = value; }
|
||||
public static string DurStart { get => durStart; set => durStart = value; }
|
||||
public static string DurEnd { get => durEnd; set => durEnd = value; }
|
||||
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 BaseUrl { get; set; } = string.Empty;
|
||||
public string M3u8Url { get; set; } = string.Empty;
|
||||
public string DownDir { get; set; } = string.Empty;
|
||||
public string DownName { get; set; } = string.Empty;
|
||||
public string Headers { get; set; } = string.Empty;
|
||||
//存放Range信息,允许用户只下载部分视频
|
||||
public static int RangeStart { get; set; } = 0;
|
||||
public static int RangeEnd { get; set; } = -1;
|
||||
//是否自动清除优酷广告分片
|
||||
public static bool DelAd { get; set; } = true;
|
||||
//存放Range信息,允许用户只下载部分视频
|
||||
public static string DurStart { get; set; } = "";
|
||||
public static string DurEnd { get; set; } = "";
|
||||
public string KeyFile { get; set; } = string.Empty;
|
||||
public string KeyBase64 { get; set; } = string.Empty;
|
||||
public bool LiveStream { get; set; } = false;
|
||||
public string KeyIV { get; set; } = string.Empty;
|
||||
|
||||
public void Parse()
|
||||
{
|
||||
FFmpeg.REC_TIME = "";
|
||||
|
||||
m3u8SavePath = Path.Combine(DownDir, "raw.m3u8");
|
||||
jsonSavePath = Path.Combine(DownDir, "meta.json");
|
||||
|
||||
@@ -88,11 +78,18 @@ namespace N_m3u8DL_CLI
|
||||
|
||||
|
||||
//获取m3u8内容
|
||||
if (!liveStream)
|
||||
if (!LiveStream)
|
||||
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);
|
||||
@@ -121,6 +118,18 @@ namespace N_m3u8DL_CLI
|
||||
m3u8Content = DecodeImooc.DecodeM3u8(m3u8Content);
|
||||
}
|
||||
|
||||
if (m3u8Content.Trim().StartsWith("<?xml version") && m3u8Content.Contains("<MPD"))
|
||||
{
|
||||
var mpdSavePath = Path.Combine(DownDir, "dash.mpd");
|
||||
//输出mpd文件
|
||||
File.WriteAllText(mpdSavePath, m3u8Content);
|
||||
//分析mpd文件
|
||||
M3u8Url = Global.Get302(M3u8Url, Headers);
|
||||
var newUri = MPDParser.Parse(DownDir, M3u8Url, m3u8Content, BaseUrl);
|
||||
M3u8Url = newUri;
|
||||
m3u8Content = File.ReadAllText(new Uri(M3u8Url).LocalPath);
|
||||
}
|
||||
|
||||
//输出m3u8文件
|
||||
File.WriteAllText(m3u8SavePath, m3u8Content);
|
||||
|
||||
@@ -140,24 +149,33 @@ namespace N_m3u8DL_CLI
|
||||
if (new Regex("#YUMING\\|(.*)").IsMatch(m3u8Content))
|
||||
BaseUrl = new Regex("#YUMING\\|(.*)").Match(m3u8Content).Groups[1].Value;
|
||||
else
|
||||
BaseUrl = GetBaseUrl(M3u8Url, headers);
|
||||
BaseUrl = GetBaseUrl(M3u8Url, Headers);
|
||||
}
|
||||
|
||||
if (!liveStream)
|
||||
if (!LiveStream)
|
||||
{
|
||||
LOGGER.WriteLine(strings.parsingM3u8);
|
||||
LOGGER.PrintLine(strings.parsingM3u8);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(keyBase64))
|
||||
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))
|
||||
if (!string.IsNullOrEmpty(KeyFile))
|
||||
{
|
||||
Uri u = new Uri(keyFile);
|
||||
string line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\"";
|
||||
string line = "";
|
||||
Uri u = new Uri(KeyFile);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -223,7 +241,13 @@ namespace N_m3u8DL_CLI
|
||||
startIndex = segIndex;
|
||||
}
|
||||
else if (line.StartsWith(HLSTags.ext_x_discontinuity_sequence)) ;
|
||||
else if (line.StartsWith(HLSTags.ext_x_program_date_time)) ;
|
||||
else if (line.StartsWith(HLSTags.ext_x_program_date_time))
|
||||
{
|
||||
if (string.IsNullOrEmpty(FFmpeg.REC_TIME))
|
||||
{
|
||||
FFmpeg.REC_TIME = line.Replace(HLSTags.ext_x_program_date_time + ":", "").Trim();
|
||||
}
|
||||
}
|
||||
//解析不连续标记,需要单独合并(timestamp不同)
|
||||
else if (line.StartsWith(HLSTags.ext_x_discontinuity))
|
||||
{
|
||||
@@ -248,12 +272,24 @@ 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))
|
||||
{
|
||||
//自定义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))
|
||||
{
|
||||
@@ -267,7 +303,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]);
|
||||
}
|
||||
@@ -296,7 +332,7 @@ namespace N_m3u8DL_CLI
|
||||
else if (line.StartsWith(HLSTags.ext_x_i_frame_stream_inf)) ;
|
||||
else if (line.StartsWith(HLSTags.ext_x_media))
|
||||
{
|
||||
if (Global.GetTagAttribute(line, "TYPE") == "AUDIO")
|
||||
if (Global.GetTagAttribute(line, "TYPE") == "AUDIO" && !MEDIA_AUDIO.ContainsKey(Global.GetTagAttribute(line, "GROUP-ID")))
|
||||
MEDIA_AUDIO.Add(Global.GetTagAttribute(line, "GROUP-ID"), CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI")));
|
||||
if (Global.GetTagAttribute(line, "TYPE") == "SUBTITLES")
|
||||
{
|
||||
@@ -354,6 +390,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();
|
||||
@@ -570,7 +610,7 @@ namespace N_m3u8DL_CLI
|
||||
|
||||
|
||||
//输出JSON文件
|
||||
if (!liveStream)
|
||||
if (!LiveStream)
|
||||
{
|
||||
LOGGER.WriteLine(strings.wrtingMeta);
|
||||
LOGGER.PrintLine(strings.wrtingMeta);
|
||||
@@ -580,8 +620,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 +654,6 @@ namespace N_m3u8DL_CLI
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.PrintLine(strings.downloadingM3u8Key, LOGGER.Warning);
|
||||
LOGGER.WriteLine(strings.downloadingM3u8Key + " " + key[1]);
|
||||
if (key[1].StartsWith("http"))
|
||||
{
|
||||
@@ -819,9 +864,7 @@ namespace N_m3u8DL_CLI
|
||||
/// <returns></returns>
|
||||
public static string GetBaseUrl(string m3u8url, string headers)
|
||||
{
|
||||
if (!isQiQiuYun && Global.Get302(m3u8url, headers) != m3u8url)
|
||||
m3u8url = Global.Get302(m3u8url, headers);
|
||||
string url = Global.Get302(m3u8url);
|
||||
string url = Global.Get302(m3u8url, headers);
|
||||
if (url.Contains("?"))
|
||||
url = url.Remove(url.LastIndexOf('?'));
|
||||
url = url.Substring(0, url.LastIndexOf('/') + 1);
|
||||
|
File diff suppressed because it is too large
Load Diff
295
N_m3u8DL-CLI/changelog.txt
Normal file
295
N_m3u8DL-CLI/changelog.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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>
|
@@ -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)
|
||||
|
File diff suppressed because one or more lines are too long
555
docs/GetM3u8.html
Normal file
555
docs/GetM3u8.html
Normal file
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user