You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-10-03 07:40:49 +02:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 |
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\
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -10,19 +10,20 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
class FFmpeg
|
class FFmpeg
|
||||||
{
|
{
|
||||||
private static string outPutPath = string.Empty;
|
public static string FFMPEG_PATH = "ffmpeg";
|
||||||
private static string reportFile = string.Empty;
|
public static string REC_TIME = ""; //录制日期
|
||||||
private static bool useAACFilter = false; //是否启用滤镜
|
|
||||||
private static bool writeDate = true; //是否写入录制日期
|
public static string OutPutPath { get; set; } = string.Empty;
|
||||||
public static string OutPutPath { get => outPutPath; set => outPutPath = value; }
|
public static string ReportFile { get; set; } = string.Empty;
|
||||||
public static string ReportFile { get => reportFile; set => reportFile = value; }
|
public static bool UseAACFilter { get; set; } = false; //是否启用滤镜
|
||||||
public static bool UseAACFilter { get => useAACFilter; set => useAACFilter = value; }
|
public static bool WriteDate { get; set; } = true; //是否写入录制日期
|
||||||
public static bool WriteDate { get => writeDate; set => writeDate = value; }
|
|
||||||
|
|
||||||
public static void Merge(string[] files, string muxFormat, bool fastStart,
|
public static void Merge(string[] files, string muxFormat, bool fastStart,
|
||||||
string poster = "", string audioName = "", string title = "",
|
string poster = "", string audioName = "", string title = "",
|
||||||
string copyright = "", string comment = "", string encodingTool = "")
|
string copyright = "", string comment = "", string encodingTool = "")
|
||||||
{
|
{
|
||||||
|
string dateString = string.IsNullOrEmpty(REC_TIME) ? DateTime.Now.ToString("o") : REC_TIME;
|
||||||
|
|
||||||
//同名文件已存在的共存策略
|
//同名文件已存在的共存策略
|
||||||
if (File.Exists($"{OutPutPath}.{muxFormat.ToLower()}"))
|
if (File.Exists($"{OutPutPath}.{muxFormat.ToLower()}"))
|
||||||
{
|
{
|
||||||
@@ -50,7 +51,7 @@ namespace N_m3u8DL_CLI
|
|||||||
command += " " + (string.IsNullOrEmpty(ddpAudio) ? "" : "-i \"" + ddpAudio + "\"");
|
command += " " + (string.IsNullOrEmpty(ddpAudio) ? "" : "-i \"" + ddpAudio + "\"");
|
||||||
command +=
|
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)
|
$" -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 encoding_tool=\"" + encodingTool + "\" -metadata title=\"" + title +
|
||||||
"\" -metadata copyright=\"" + copyright + "\" -metadata comment=\"" + comment +
|
"\" -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 + "\" ";
|
$"\" -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);
|
LOGGER.WriteLine(strings.ffmpegDone);
|
||||||
//Console.WriteLine(command);
|
//Console.WriteLine(command);
|
||||||
}
|
}
|
||||||
@@ -92,7 +93,7 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
if (Global.VIDEO_TYPE == "H264")
|
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 \""
|
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -f mpegts -bsf:v h264_mp4toannexb \""
|
||||||
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
||||||
Path.GetDirectoryName(file));
|
Path.GetDirectoryName(file));
|
||||||
@@ -104,7 +105,7 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
else if (Global.VIDEO_TYPE == "H265")
|
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 \""
|
"-loglevel quiet -i \"" + file + "\" -map 0 -c copy -copy_unknown -f mpegts -bsf:v hevc_mp4toannexb \""
|
||||||
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
+ Path.GetFileNameWithoutExtension(file) + "[MPEGTS].ts\"",
|
||||||
Path.GetDirectoryName(file));
|
Path.GetDirectoryName(file));
|
||||||
|
@@ -30,8 +30,8 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
|
|
||||||
/*===============================================================================*/
|
/*===============================================================================*/
|
||||||
static string nowVer = "2.5.7";
|
static string nowVer = "2.8.1";
|
||||||
static string nowDate = "20200809";
|
static string nowDate = "20201121";
|
||||||
public static void WriteInit()
|
public static void WriteInit()
|
||||||
{
|
{
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
@@ -108,7 +108,7 @@ namespace N_m3u8DL_CLI
|
|||||||
HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
|
HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(url);
|
||||||
webRequest.Method = "GET";
|
webRequest.Method = "GET";
|
||||||
if (NoProxy) webRequest.Proxy = null;
|
if (NoProxy) webRequest.Proxy = null;
|
||||||
webRequest.UserAgent = "Mozilla/4.0";
|
webRequest.UserAgent = "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";
|
||||||
webRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
|
webRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
|
||||||
webRequest.Timeout = TimeOut; //设置超时
|
webRequest.Timeout = TimeOut; //设置超时
|
||||||
webRequest.KeepAlive = false;
|
webRequest.KeepAlive = false;
|
||||||
@@ -145,6 +145,10 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
|
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
|
||||||
|
|
||||||
|
//文件过大则认为不是m3u8
|
||||||
|
if (webResponse.ContentLength != -1 && webRequest.ContentLength > 50 * 1024 * 1024) return "";
|
||||||
|
|
||||||
if (webResponse.ContentEncoding != null
|
if (webResponse.ContentEncoding != null
|
||||||
&& webResponse.ContentEncoding.ToLower() == "gzip") //如果使用了GZip则先解压
|
&& webResponse.ContentEncoding.ToLower() == "gzip") //如果使用了GZip则先解压
|
||||||
{
|
{
|
||||||
@@ -514,7 +518,7 @@ namespace N_m3u8DL_CLI
|
|||||||
request.Headers.Add("Cookie", "MQGUID");
|
request.Headers.Add("Cookie", "MQGUID");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
request.UserAgent = "VLC/2.2.1 LibVLC/2.2.1";
|
request.UserAgent = "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";
|
||||||
//下载部分字节
|
//下载部分字节
|
||||||
if (expectByte != -1)
|
if (expectByte != -1)
|
||||||
request.AddRange("bytes", startByte, startByte + expectByte - 1);
|
request.AddRange("bytes", startByte, startByte + expectByte - 1);
|
||||||
@@ -545,6 +549,7 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
long totalLen = 0;
|
long totalLen = 0;
|
||||||
long downLen = 0;
|
long downLen = 0;
|
||||||
|
bool pngHeader = false; //PNG HEADER检测
|
||||||
using (var response = (HttpWebResponse)request.GetResponse())
|
using (var response = (HttpWebResponse)request.GetResponse())
|
||||||
{
|
{
|
||||||
using (var responseStream = response.GetResponseStream())
|
using (var responseStream = response.GetResponseStream())
|
||||||
@@ -555,6 +560,17 @@ namespace N_m3u8DL_CLI
|
|||||||
totalLen = response.ContentLength;
|
totalLen = response.ContentLength;
|
||||||
byte[] bArr = new byte[1024];
|
byte[] bArr = new byte[1024];
|
||||||
int size = responseStream.Read(bArr, 0, (int)bArr.Length);
|
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;
|
||||||
|
}
|
||||||
|
//GIF HEADER检测
|
||||||
|
if (!pngHeader && size > 3 && 0x47 == bArr[0] && 0x49 == bArr[1] && 0x46 == bArr[2] && 0x38 == bArr[3])
|
||||||
|
{
|
||||||
|
bArr = bArr.Skip(42).ToArray();
|
||||||
|
size -= 42;
|
||||||
|
downLen += 42;
|
||||||
|
}
|
||||||
while (size > 0)
|
while (size > 0)
|
||||||
{
|
{
|
||||||
stream.Write(bArr, 0, size);
|
stream.Write(bArr, 0, size);
|
||||||
@@ -579,6 +595,8 @@ namespace N_m3u8DL_CLI
|
|||||||
try { File.Delete(path); } catch (Exception) { }
|
try { File.Delete(path); } catch (Exception) { }
|
||||||
if (totalLen != -1 && downLen != totalLen)
|
if (totalLen != -1 && downLen != totalLen)
|
||||||
try { File.Delete(path); } catch (Exception) { }
|
try { File.Delete(path); } catch (Exception) { }
|
||||||
|
if (pngHeader)
|
||||||
|
TrySkipPngHeader(path);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -587,6 +605,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字符串
|
//格式化json字符串
|
||||||
public static string ConvertJsonString(string str)
|
public static string ConvertJsonString(string str)
|
||||||
{
|
{
|
||||||
@@ -753,6 +816,12 @@ namespace N_m3u8DL_CLI
|
|||||||
FFmpeg.UseAACFilter = true;
|
FFmpeg.UseAACFilter = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//有非AAC音轨则关闭UseAACFilter
|
||||||
|
if (res.Contains("Audio") && !res.Contains("Audio aac"))
|
||||||
|
{
|
||||||
|
FFmpeg.UseAACFilter = false;
|
||||||
|
}
|
||||||
|
|
||||||
if ((VIDEO_TYPE == "" || VIDEO_TYPE == "IGNORE") && res.Contains("Audio eac3"))
|
if ((VIDEO_TYPE == "" || VIDEO_TYPE == "IGNORE") && res.Contains("Audio eac3"))
|
||||||
{
|
{
|
||||||
AUDIO_TYPE = "eac3";
|
AUDIO_TYPE = "eac3";
|
||||||
@@ -1048,6 +1117,8 @@ namespace N_m3u8DL_CLI
|
|||||||
if (i > 0)
|
if (i > 0)
|
||||||
{
|
{
|
||||||
int newTime = Convert.ToInt32(Regex.Match(tmp, "X-TIMESTAMP-MAP.*MPEGTS:(\\d+)").Groups[1].Value);
|
int newTime = Convert.ToInt32(Regex.Match(tmp, "X-TIMESTAMP-MAP.*MPEGTS:(\\d+)").Groups[1].Value);
|
||||||
|
if (newTime == 900000)
|
||||||
|
continue;
|
||||||
//计算偏移量
|
//计算偏移量
|
||||||
//LOGGER.PrintLine((newTime - baseTime).ToString());
|
//LOGGER.PrintLine((newTime - baseTime).ToString());
|
||||||
addTime = addTime + ((newTime - baseTime) / 100);
|
addTime = addTime + ((newTime - baseTime) / 100);
|
||||||
|
617
N_m3u8DL-CLI/MPDParser.cs
Normal file
617
N_m3u8DL-CLI/MPDParser.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -40,11 +40,11 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.JScript" />
|
<Reference Include="Microsoft.JScript" />
|
||||||
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
|
<Reference Include="netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" />
|
||||||
<Reference Include="Newtonsoft.Json">
|
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||||
<HintPath>..\packages\Newtonsoft.Json.dll</HintPath>
|
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="NiL.JS, Version=2.5.1428.0, Culture=neutral, PublicKeyToken=fa941a7c2a4de689, processorArchitecture=MSIL">
|
<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>
|
||||||
<Reference Include="PresentationFramework" />
|
<Reference Include="PresentationFramework" />
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
@@ -63,6 +63,9 @@
|
|||||||
<Compile Include="CommandLineArgument.cs" />
|
<Compile Include="CommandLineArgument.cs" />
|
||||||
<Compile Include="CommandLineArgumentParser.cs" />
|
<Compile Include="CommandLineArgumentParser.cs" />
|
||||||
<Compile Include="Decode51CtoKey.cs" />
|
<Compile Include="Decode51CtoKey.cs" />
|
||||||
|
<Compile Include="DecodeDdyun.cs" />
|
||||||
|
<Compile Include="DecodeImooc.cs" />
|
||||||
|
<Compile Include="DecodeNfmovies.cs" />
|
||||||
<Compile Include="Decrypter.cs" />
|
<Compile Include="Decrypter.cs" />
|
||||||
<Compile Include="FFmpeg.cs" />
|
<Compile Include="FFmpeg.cs" />
|
||||||
<Compile Include="Global.cs" />
|
<Compile Include="Global.cs" />
|
||||||
@@ -70,10 +73,26 @@
|
|||||||
<Compile Include="HLSTags.cs" />
|
<Compile Include="HLSTags.cs" />
|
||||||
<Compile Include="LOGGER.cs" />
|
<Compile Include="LOGGER.cs" />
|
||||||
<Compile Include="DownloadManager.cs" />
|
<Compile Include="DownloadManager.cs" />
|
||||||
|
<Compile Include="MPDParser.cs" />
|
||||||
<Compile Include="Parser.cs" />
|
<Compile Include="Parser.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Downloader.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" />
|
<Compile Include="Watcher.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -82,9 +101,6 @@
|
|||||||
</None>
|
</None>
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="bin\Debug\Newtonsoft.Json.dll" />
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<COMReference Include="Scripting">
|
<COMReference Include="Scripting">
|
||||||
<Guid>{420B2830-E718-11CF-893D-00A0C9054228}</Guid>
|
<Guid>{420B2830-E718-11CF-893D-00A0C9054228}</Guid>
|
||||||
@@ -100,16 +116,18 @@
|
|||||||
<Content Include="logo_3Iv_icon.ico" />
|
<Content Include="logo_3Iv_icon.ico" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="Properties\Resources.resx">
|
<EmbeddedResource Include="strings.en-US.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>strings.en-US.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
<EmbeddedResource Include="strings.en-US.resx" />
|
|
||||||
<EmbeddedResource Include="strings.resx">
|
<EmbeddedResource Include="strings.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>strings.Designer.cs</LastGenOutput>
|
<LastGenOutput>strings.Designer.cs</LastGenOutput>
|
||||||
</EmbeddedResource>
|
</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>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
</Project>
|
</Project>
|
@@ -17,15 +17,6 @@ namespace N_m3u8DL_CLI
|
|||||||
string[] m3u8CurrentKey = new string[] { "NONE", "", "" };
|
string[] m3u8CurrentKey = new string[] { "NONE", "", "" };
|
||||||
private string m3u8SavePath = string.Empty;
|
private string m3u8SavePath = string.Empty;
|
||||||
private string jsonSavePath = 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 string keyIV = string.Empty;
|
|
||||||
private bool liveStream = false;
|
|
||||||
private long bestBandwidth = 0;
|
private long bestBandwidth = 0;
|
||||||
private string bestUrl = string.Empty;
|
private string bestUrl = string.Empty;
|
||||||
private string bestUrlAudio = string.Empty;
|
private string bestUrlAudio = string.Empty;
|
||||||
@@ -37,34 +28,31 @@ namespace N_m3u8DL_CLI
|
|||||||
//存放多轨道的信息
|
//存放多轨道的信息
|
||||||
private ArrayList extLists = new ArrayList();
|
private ArrayList extLists = new ArrayList();
|
||||||
private static bool isQiQiuYun = false;
|
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;
|
private static bool hasAd = false;
|
||||||
|
|
||||||
public string BaseUrl { get => baseUrl; set => baseUrl = value; }
|
public string BaseUrl { get; set; } = string.Empty;
|
||||||
public string M3u8Url { get => m3u8Url; set => m3u8Url = value; }
|
public string M3u8Url { get; set; } = string.Empty;
|
||||||
public string DownDir { get => downDir; set => downDir = value; }
|
public string DownDir { get; set; } = string.Empty;
|
||||||
public string DownName { get => downName; set => downName = value; }
|
public string DownName { get; set; } = string.Empty;
|
||||||
public string Headers { get => headers; set => headers = value; }
|
public string Headers { get; set; } = string.Empty;
|
||||||
public static int RangeStart { get => rangeStart; set => rangeStart = value; }
|
//存放Range信息,允许用户只下载部分视频
|
||||||
public static int RangeEnd { get => rangeEnd; set => rangeEnd = value; }
|
public static int RangeStart { get; set; } = 0;
|
||||||
public static bool DelAd { get => delAd; set => delAd = value; }
|
public static int RangeEnd { get; set; } = -1;
|
||||||
public static string DurStart { get => durStart; set => durStart = value; }
|
//是否自动清除优酷广告分片
|
||||||
public static string DurEnd { get => durEnd; set => durEnd = value; }
|
public static bool DelAd { get; set; } = true;
|
||||||
public string KeyFile { get => keyFile; set => keyFile = value; }
|
//存放Range信息,允许用户只下载部分视频
|
||||||
public string KeyBase64 { get => keyBase64; set => keyBase64 = value; }
|
public static string DurStart { get; set; } = "";
|
||||||
public bool LiveStream { get => liveStream; set => liveStream = value; }
|
public static string DurEnd { get; set; } = "";
|
||||||
public string KeyIV { get => keyIV; set => keyIV = value; }
|
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()
|
public void Parse()
|
||||||
{
|
{
|
||||||
|
FFmpeg.REC_TIME = "";
|
||||||
|
|
||||||
m3u8SavePath = Path.Combine(DownDir, "raw.m3u8");
|
m3u8SavePath = Path.Combine(DownDir, "raw.m3u8");
|
||||||
jsonSavePath = Path.Combine(DownDir, "meta.json");
|
jsonSavePath = Path.Combine(DownDir, "meta.json");
|
||||||
|
|
||||||
@@ -90,11 +78,18 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
|
|
||||||
//获取m3u8内容
|
//获取m3u8内容
|
||||||
if (!liveStream)
|
if (!LiveStream)
|
||||||
LOGGER.PrintLine(strings.downloadingM3u8, LOGGER.Warning);
|
LOGGER.PrintLine(strings.downloadingM3u8, LOGGER.Warning);
|
||||||
|
|
||||||
if (M3u8Url.StartsWith("http"))
|
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:"))
|
else if (M3u8Url.StartsWith("file:"))
|
||||||
{
|
{
|
||||||
Uri t = new Uri(M3u8Url);
|
Uri t = new Uri(M3u8Url);
|
||||||
@@ -123,6 +118,18 @@ namespace N_m3u8DL_CLI
|
|||||||
m3u8Content = DecodeImooc.DecodeM3u8(m3u8Content);
|
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);
|
||||||
|
M3u8Url = newUri;
|
||||||
|
m3u8Content = File.ReadAllText(new Uri(M3u8Url).LocalPath);
|
||||||
|
}
|
||||||
|
|
||||||
//输出m3u8文件
|
//输出m3u8文件
|
||||||
File.WriteAllText(m3u8SavePath, m3u8Content);
|
File.WriteAllText(m3u8SavePath, m3u8Content);
|
||||||
|
|
||||||
@@ -142,32 +149,32 @@ namespace N_m3u8DL_CLI
|
|||||||
if (new Regex("#YUMING\\|(.*)").IsMatch(m3u8Content))
|
if (new Regex("#YUMING\\|(.*)").IsMatch(m3u8Content))
|
||||||
BaseUrl = new Regex("#YUMING\\|(.*)").Match(m3u8Content).Groups[1].Value;
|
BaseUrl = new Regex("#YUMING\\|(.*)").Match(m3u8Content).Groups[1].Value;
|
||||||
else
|
else
|
||||||
BaseUrl = GetBaseUrl(M3u8Url, headers);
|
BaseUrl = GetBaseUrl(M3u8Url, Headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!liveStream)
|
if (!LiveStream)
|
||||||
{
|
{
|
||||||
LOGGER.WriteLine(strings.parsingM3u8);
|
LOGGER.WriteLine(strings.parsingM3u8);
|
||||||
LOGGER.PrintLine(strings.parsingM3u8);
|
LOGGER.PrintLine(strings.parsingM3u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(keyBase64))
|
if (!string.IsNullOrEmpty(KeyBase64))
|
||||||
{
|
{
|
||||||
string line = "";
|
string line = "";
|
||||||
if (string.IsNullOrEmpty(keyIV))
|
if (string.IsNullOrEmpty(KeyIV))
|
||||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\"";
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{KeyBase64}\"";
|
||||||
else
|
else
|
||||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{keyBase64}\",IV=0x{keyIV.Replace("0x", "")}";
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{KeyBase64}\",IV=0x{KeyIV.Replace("0x", "")}";
|
||||||
m3u8CurrentKey = ParseKey(line);
|
m3u8CurrentKey = ParseKey(line);
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(keyFile))
|
if (!string.IsNullOrEmpty(KeyFile))
|
||||||
{
|
{
|
||||||
string line = "";
|
string line = "";
|
||||||
Uri u = new Uri(keyFile);
|
Uri u = new Uri(KeyFile);
|
||||||
if (string.IsNullOrEmpty(keyIV))
|
if (string.IsNullOrEmpty(KeyIV))
|
||||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\"";
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\"";
|
||||||
else
|
else
|
||||||
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\",IV=0x{keyIV.Replace("0x", "")}";
|
line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\",IV=0x{KeyIV.Replace("0x", "")}";
|
||||||
|
|
||||||
m3u8CurrentKey = ParseKey(line);
|
m3u8CurrentKey = ParseKey(line);
|
||||||
}
|
}
|
||||||
@@ -234,7 +241,13 @@ namespace N_m3u8DL_CLI
|
|||||||
startIndex = segIndex;
|
startIndex = segIndex;
|
||||||
}
|
}
|
||||||
else if (line.StartsWith(HLSTags.ext_x_discontinuity_sequence)) ;
|
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不同)
|
//解析不连续标记,需要单独合并(timestamp不同)
|
||||||
else if (line.StartsWith(HLSTags.ext_x_discontinuity))
|
else if (line.StartsWith(HLSTags.ext_x_discontinuity))
|
||||||
{
|
{
|
||||||
@@ -259,12 +272,24 @@ namespace N_m3u8DL_CLI
|
|||||||
else if (line.StartsWith(HLSTags.ext_x_version)) ;
|
else if (line.StartsWith(HLSTags.ext_x_version)) ;
|
||||||
else if (line.StartsWith(HLSTags.ext_x_allow_cache)) ;
|
else if (line.StartsWith(HLSTags.ext_x_allow_cache)) ;
|
||||||
//解析KEY
|
//解析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);
|
m3u8CurrentKey = ParseKey(line);
|
||||||
//存储为上一行的key信息
|
//存储为上一行的key信息
|
||||||
lastKeyLine = line;
|
lastKeyLine = line;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//解析分片时长(暂时不考虑标题属性)
|
//解析分片时长(暂时不考虑标题属性)
|
||||||
else if (line.StartsWith(HLSTags.extinf))
|
else if (line.StartsWith(HLSTags.extinf))
|
||||||
{
|
{
|
||||||
@@ -365,6 +390,10 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
segUrl += new Regex("\\?__gda__.*").Match(M3u8Url).Value;
|
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);
|
segInfo.Add("segUri", segUrl);
|
||||||
segments.Add(segInfo);
|
segments.Add(segInfo);
|
||||||
segInfo = new JObject();
|
segInfo = new JObject();
|
||||||
@@ -581,7 +610,7 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
|
|
||||||
//输出JSON文件
|
//输出JSON文件
|
||||||
if (!liveStream)
|
if (!LiveStream)
|
||||||
{
|
{
|
||||||
LOGGER.WriteLine(strings.wrtingMeta);
|
LOGGER.WriteLine(strings.wrtingMeta);
|
||||||
LOGGER.PrintLine(strings.wrtingMeta);
|
LOGGER.PrintLine(strings.wrtingMeta);
|
||||||
|
@@ -235,14 +235,11 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
/// 2020年2月23日
|
/// 2020年2月23日
|
||||||
/// - 不支持的加密方式将标记为NOTSUPPORTED并强制启用二进制合并
|
/// - 不支持的加密方式将标记为NOTSUPPORTED并强制启用二进制合并
|
||||||
/// - 启用二进制合并的情况下,如果m3u8文件中存在map文件,则合并为mp4格式
|
/// - 启用二进制合并的情况下,如果m3u8文件中存在map文件,则合并为mp4格式
|
||||||
/// - 支持优酷视频解密
|
|
||||||
/// 2020年2月24日
|
/// 2020年2月24日
|
||||||
/// - 直播流录制优化逻辑,避免忙等待
|
/// - 直播流录制优化逻辑,避免忙等待
|
||||||
/// - 直播Waiting时,不再输出Parser内容
|
/// - 直播Waiting时,不再输出Parser内容
|
||||||
/// - 直播录制的日志记录
|
/// - 直播录制的日志记录
|
||||||
/// - 增加新的选项--liveRecDur限制直播录制时长
|
/// - 增加新的选项--liveRecDur限制直播录制时长
|
||||||
/// 2020年2月25日
|
|
||||||
/// - 修复优酷解密过程错误写入冗余数据的bug
|
|
||||||
/// 2020年2月27日
|
/// 2020年2月27日
|
||||||
/// - 细节bug修复
|
/// - 细节bug修复
|
||||||
/// 2020年2月28日
|
/// 2020年2月28日
|
||||||
@@ -266,7 +263,6 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
/// - 增加同名文件合并时共存策略
|
/// - 增加同名文件合并时共存策略
|
||||||
/// 2020年4月17日
|
/// 2020年4月17日
|
||||||
/// - 优化异常捕获
|
/// - 优化异常捕获
|
||||||
/// - 控制台输出设置为UTF-8
|
|
||||||
/// - 细节优化
|
/// - 细节优化
|
||||||
/// 2020年4月22日
|
/// 2020年4月22日
|
||||||
/// - 51cto getsign
|
/// - 51cto getsign
|
||||||
@@ -287,6 +283,17 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
/// 2020年8月9日
|
/// 2020年8月9日
|
||||||
/// - 修复IV错误导致的AES-128解密异常问题
|
/// - 修复IV错误导致的AES-128解密异常问题
|
||||||
/// - 支持自定义IV(--useKeyIV)
|
/// - 支持自定义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>
|
/// </summary>
|
||||||
///
|
///
|
||||||
|
|
||||||
@@ -326,7 +333,6 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
{
|
{
|
||||||
SetConsoleCtrlHandler(cancelHandler, true);
|
SetConsoleCtrlHandler(cancelHandler, true);
|
||||||
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
|
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
|
||||||
|
|
||||||
string loc = "zh-CN";
|
string loc = "zh-CN";
|
||||||
string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
|
string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
|
||||||
if (currLoc == "zh-TW" || currLoc == "zh-HK" || currLoc == "zh-MO")
|
if (currLoc == "zh-TW" || currLoc == "zh-HK" || currLoc == "zh-MO")
|
||||||
@@ -353,7 +359,15 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
string fileName = "";
|
string fileName = "";
|
||||||
|
|
||||||
//寻找ffmpeg.exe
|
//寻找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
|
try
|
||||||
{
|
{
|
||||||
@@ -361,9 +375,12 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
foreach (var de in EnvironmentPath)
|
foreach (var de in EnvironmentPath)
|
||||||
{
|
{
|
||||||
if (File.Exists(Path.Combine(de.Trim('\"').Trim(), "ffmpeg.exe")))
|
if (File.Exists(Path.Combine(de.Trim('\"').Trim(), "ffmpeg.exe")))
|
||||||
|
{
|
||||||
|
FFmpeg.FFMPEG_PATH = Path.Combine(de.Trim('\"').Trim(), "ffmpeg.exe");
|
||||||
goto HasFFmpeg;
|
goto HasFFmpeg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
;
|
;
|
||||||
@@ -375,8 +392,7 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
Console.ResetColor(); //将控制台的前景色和背景色设为默认值
|
Console.ResetColor(); //将控制台的前景色和背景色设为默认值
|
||||||
Console.WriteLine(strings.ffmpegTip);
|
Console.WriteLine(strings.ffmpegTip);
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("x86 https://ffmpeg.zeranoe.com/builds/win32/static/");
|
Console.WriteLine("http://ffmpeg.org/download.html#build-windows");
|
||||||
Console.WriteLine("x64 https://ffmpeg.zeranoe.com/builds/win64/static/");
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine(strings.pressAnyKeyExit);
|
Console.WriteLine(strings.pressAnyKeyExit);
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
@@ -462,10 +478,6 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
{
|
{
|
||||||
muxFastStart = true;
|
muxFastStart = true;
|
||||||
}
|
}
|
||||||
if (arguments.Has("--enableYouKuAes"))
|
|
||||||
{
|
|
||||||
Downloader.YouKuAES = true;
|
|
||||||
}
|
|
||||||
if (arguments.Has("--disableIntegrityCheck"))
|
if (arguments.Has("--disableIntegrityCheck"))
|
||||||
{
|
{
|
||||||
DownloadManager.DisableIntegrityCheck = true;
|
DownloadManager.DisableIntegrityCheck = true;
|
||||||
@@ -647,6 +659,20 @@ namespace N_m3u8DL_CLI.NetCore
|
|||||||
string m3u8Content = string.Empty;
|
string m3u8Content = string.Empty;
|
||||||
bool isVOD = true;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//开始解析
|
//开始解析
|
||||||
|
|
||||||
|
287
N_m3u8DL-CLI/changelog.txt
Normal file
287
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"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<packages>
|
<packages>
|
||||||
|
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net46" />
|
||||||
<package id="NiL.JS" version="2.5.1428" targetFramework="net46" />
|
<package id="NiL.JS" version="2.5.1428" targetFramework="net46" />
|
||||||
</packages>
|
</packages>
|
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