You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-09-17 08:40:50 +02:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d657b455cd | ||
![]() |
cda6575605 | ||
![]() |
0d6377d41b | ||
![]() |
9c76bdcbce | ||
![]() |
83915ff606 | ||
![]() |
9cd4746f33 | ||
![]() |
33a5b917ac | ||
![]() |
f69978bd82 | ||
![]() |
d9fd526886 | ||
![]() |
1cc8ecfaaf | ||
![]() |
2bc2dde2ad | ||
![]() |
c3ddcf9e0e | ||
![]() |
ba5d20dd02 | ||
![]() |
16f705fe66 | ||
![]() |
d141cabc4a | ||
![]() |
adcf884a93 | ||
![]() |
9d903a025f |
@@ -11,25 +11,12 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
class DownloadManager
|
class DownloadManager
|
||||||
{
|
{
|
||||||
private static int calcTime = 1; //计算文件夹大小的间隔
|
|
||||||
private int stopCount = 0; //速度为零的停止
|
private int stopCount = 0; //速度为零的停止
|
||||||
private int timeOut = 10000; //超时设置
|
|
||||||
private static double downloadedSize = 0; //已下载大小
|
|
||||||
private static bool disableIntegrityCheck = false; //关闭完整性检查
|
|
||||||
|
|
||||||
private string jsonFile = string.Empty;
|
private string jsonFile = string.Empty;
|
||||||
private string headers = string.Empty;
|
|
||||||
private string downDir = string.Empty;
|
|
||||||
private string downName = string.Empty;
|
|
||||||
private string muxSetJson = string.Empty;
|
|
||||||
private int threads = 1;
|
|
||||||
private int retryCount = 5;
|
|
||||||
private static int count = 0;
|
|
||||||
private static int partsCount = 0;
|
|
||||||
private int total = 0;
|
private int total = 0;
|
||||||
public static string partsPadZero = string.Empty;
|
public static string partsPadZero = string.Empty;
|
||||||
string segsPadZero = string.Empty;
|
string segsPadZero = string.Empty;
|
||||||
bool delAfterDone = false;
|
|
||||||
private bool isVTT = false;
|
private bool isVTT = false;
|
||||||
bool externalAudio = false; //额外的音轨
|
bool externalAudio = false; //额外的音轨
|
||||||
string externalAudioUrl = "";
|
string externalAudioUrl = "";
|
||||||
@@ -37,28 +24,56 @@ namespace N_m3u8DL_CLI
|
|||||||
string externalSubUrl = "";
|
string externalSubUrl = "";
|
||||||
string fflogName = "_ffreport.log";
|
string fflogName = "_ffreport.log";
|
||||||
public static bool BinaryMerge = false;
|
public static bool BinaryMerge = false;
|
||||||
private bool noMerge = false;
|
|
||||||
private bool muxFastStart = true;
|
|
||||||
private string muxFormat = "mp4";
|
|
||||||
private static bool hasSetDir = false;
|
|
||||||
|
|
||||||
public int Threads { get => threads; set => threads = value; }
|
public int Threads { get; set; } = 1;
|
||||||
public int RetryCount { get => retryCount; set => retryCount = value; }
|
public int RetryCount { get; set; } = 5;
|
||||||
public string Headers { get => headers; set => headers = value; }
|
public string Headers { 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 bool DelAfterDone { get => delAfterDone; set => delAfterDone = value; }
|
public bool DelAfterDone { get; set; } = false;
|
||||||
public string MuxFormat { get => muxFormat; set => muxFormat = value; }
|
public string MuxFormat { get; set; } = "mp4";
|
||||||
public bool MuxFastStart { get => muxFastStart; set => muxFastStart = value; }
|
public bool MuxFastStart { get; set; } = true;
|
||||||
public string MuxSetJson { get => muxSetJson; set => muxSetJson = value; }
|
public string MuxSetJson { get; set; } = string.Empty;
|
||||||
public int TimeOut { get => timeOut; set => timeOut = value; }
|
public int TimeOut { get; set; } = 10000; //超时设置
|
||||||
public static double DownloadedSize { get => downloadedSize; set => downloadedSize = value; }
|
public static double DownloadedSize { get; set; } = 0; //已下载大小
|
||||||
public static bool HasSetDir { get => hasSetDir; set => hasSetDir = value; }
|
public static bool HasSetDir { get; set; } = false;
|
||||||
public bool NoMerge { get => noMerge; set => noMerge = value; }
|
public bool NoMerge { get; set; } = false;
|
||||||
public static int CalcTime { get => calcTime; set => calcTime = value; }
|
public static int CalcTime { get; set; } = 1; //计算速度的间隔
|
||||||
public static int Count { get => count; set => count = value; }
|
public static int Count { get; set; } = 0;
|
||||||
public static int PartsCount { get => partsCount; set => partsCount = value; }
|
public static int PartsCount { get; set; } = 0;
|
||||||
public static bool DisableIntegrityCheck { get => disableIntegrityCheck; set => disableIntegrityCheck = value; }
|
public static bool DisableIntegrityCheck { get; set; } = false; //关闭完整性检查
|
||||||
|
|
||||||
|
static CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
|
//计算下载速度
|
||||||
|
static System.Timers.Timer timer = new System.Timers.Timer(1000 * CalcTime); //实例化Timer类
|
||||||
|
|
||||||
|
public DownloadManager()
|
||||||
|
{
|
||||||
|
timer.AutoReset = true;
|
||||||
|
timer.Elapsed += delegate
|
||||||
|
{
|
||||||
|
Console.SetCursorPosition(0, 1);
|
||||||
|
Console.Write("Speed: " + Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + " / s".PadRight(70));
|
||||||
|
|
||||||
|
if (Global.HadReadInfo && Global.BYTEDOWN <= Global.STOP_SPEED * 1024 * CalcTime)
|
||||||
|
{
|
||||||
|
stopCount++;
|
||||||
|
Console.SetCursorPosition(0, 1);
|
||||||
|
Console.Write("Speed: " + Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + " / s [" + stopCount + "]".PadRight(70));
|
||||||
|
|
||||||
|
if (stopCount >= 12)
|
||||||
|
{
|
||||||
|
Global.ShouldStop = true;
|
||||||
|
cts.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stopCount = 0;
|
||||||
|
Global.BYTEDOWN = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void DoDownload()
|
public void DoDownload()
|
||||||
{
|
{
|
||||||
@@ -94,7 +109,6 @@ namespace N_m3u8DL_CLI
|
|||||||
PartsCount = parts.Count;
|
PartsCount = parts.Count;
|
||||||
segsPadZero = string.Empty.PadRight(oriCount.Length, '0');
|
segsPadZero = string.Empty.PadRight(oriCount.Length, '0');
|
||||||
partsPadZero = string.Empty.PadRight(Convert.ToString(parts.Count).Length, '0');
|
partsPadZero = string.Empty.PadRight(Convert.ToString(parts.Count).Length, '0');
|
||||||
CancellationTokenSource cts = new CancellationTokenSource();
|
|
||||||
|
|
||||||
//是直播视频
|
//是直播视频
|
||||||
if (isVOD == "False")
|
if (isVOD == "False")
|
||||||
@@ -104,7 +118,6 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
Global.ShouldStop = false; //是否该停止下载
|
Global.ShouldStop = false; //是否该停止下载
|
||||||
|
|
||||||
//监控文件夹变化
|
|
||||||
if (!Directory.Exists(DownDir))
|
if (!Directory.Exists(DownDir))
|
||||||
Directory.CreateDirectory(DownDir); //新建文件夹
|
Directory.CreateDirectory(DownDir); //新建文件夹
|
||||||
Watcher watcher = new Watcher(DownDir);
|
Watcher watcher = new Watcher(DownDir);
|
||||||
@@ -112,41 +125,9 @@ namespace N_m3u8DL_CLI
|
|||||||
watcher.PartsCount = PartsCount;
|
watcher.PartsCount = PartsCount;
|
||||||
watcher.WatcherStrat();
|
watcher.WatcherStrat();
|
||||||
|
|
||||||
//监控文件夹大小变化 via https://stackoverflow.com/questions/2869561/what-is-the-fastest-way-to-calculate-a-windows-folders-size/12665904#12665904
|
//开始计算速度
|
||||||
System.Timers.Timer timer = new System.Timers.Timer(1000 * CalcTime); //实例化Timer类
|
|
||||||
timer.AutoReset = true;
|
|
||||||
timer.Enabled = true;
|
timer.Enabled = true;
|
||||||
//Scripting.FileSystemObject fso = new Scripting.FileSystemObject();
|
cts = new CancellationTokenSource();
|
||||||
//Scripting.Folder folder = fso.GetFolder(DownDir);
|
|
||||||
timer.Elapsed += delegate
|
|
||||||
{
|
|
||||||
//采用COM组件获取文件夹的大小,需要引入 "Microsoft Scripting Runtime"
|
|
||||||
//double sizeInBytes = folder.Size;
|
|
||||||
Console.SetCursorPosition(0, 1);
|
|
||||||
//Console.WriteLine("Speed: " + Global.FormatFileSize((sizeInBytes - lastSizeInBytes) / calcTime) + " / s ");
|
|
||||||
Console.Write("Speed: " + Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + " / s".PadRight(70));
|
|
||||||
|
|
||||||
if (Global.HadReadInfo && Global.BYTEDOWN <= Global.STOP_SPEED * 1024 * CalcTime)
|
|
||||||
{
|
|
||||||
stopCount++;
|
|
||||||
Console.SetCursorPosition(0, 1);
|
|
||||||
Console.Write("Speed: " + Global.FormatFileSize((Global.BYTEDOWN) / CalcTime) + " / s [" + stopCount + "]".PadRight(70));
|
|
||||||
|
|
||||||
if (stopCount >= 12)
|
|
||||||
{
|
|
||||||
Global.ShouldStop = true;
|
|
||||||
cts.Cancel();
|
|
||||||
GC.Collect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stopCount = 0;
|
|
||||||
Global.BYTEDOWN = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//开始调用下载
|
//开始调用下载
|
||||||
LOGGER.WriteLine(strings.startDownloading);
|
LOGGER.WriteLine(strings.startDownloading);
|
||||||
@@ -200,16 +181,10 @@ namespace N_m3u8DL_CLI
|
|||||||
sd.Key = firstSeg["key"].Value<string>();
|
sd.Key = firstSeg["key"].Value<string>();
|
||||||
sd.Iv = firstSeg["iv"].Value<string>();
|
sd.Iv = firstSeg["iv"].Value<string>();
|
||||||
}
|
}
|
||||||
try
|
if (firstSeg["expectByte"] != null)
|
||||||
{
|
|
||||||
sd.ExpectByte = firstSeg["expectByte"].Value<long>();
|
sd.ExpectByte = firstSeg["expectByte"].Value<long>();
|
||||||
}
|
if (firstSeg["startByte"] != null)
|
||||||
catch (Exception) { }
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sd.StartByte = firstSeg["startByte"].Value<long>();
|
sd.StartByte = firstSeg["startByte"].Value<long>();
|
||||||
}
|
|
||||||
catch (Exception) { }
|
|
||||||
sd.Headers = Headers;
|
sd.Headers = Headers;
|
||||||
sd.SavePath = DownDir + "\\Part_" + 0.ToString(partsPadZero) + "\\" + firstSeg["index"].Value<int>().ToString(segsPadZero) + ".tsdownloading";
|
sd.SavePath = DownDir + "\\Part_" + 0.ToString(partsPadZero) + "\\" + firstSeg["index"].Value<int>().ToString(segsPadZero) + ".tsdownloading";
|
||||||
if (File.Exists(sd.SavePath))
|
if (File.Exists(sd.SavePath))
|
||||||
@@ -290,16 +265,10 @@ namespace N_m3u8DL_CLI
|
|||||||
sd.Key = info["key"].Value<string>();
|
sd.Key = info["key"].Value<string>();
|
||||||
sd.Iv = info["iv"].Value<string>();
|
sd.Iv = info["iv"].Value<string>();
|
||||||
}
|
}
|
||||||
try
|
if (firstSeg["expectByte"] != null)
|
||||||
{
|
|
||||||
sd.ExpectByte = info["expectByte"].Value<long>();
|
sd.ExpectByte = info["expectByte"].Value<long>();
|
||||||
}
|
if (firstSeg["startByte"] != null)
|
||||||
catch (Exception) { }
|
|
||||||
try
|
|
||||||
{
|
|
||||||
sd.StartByte = info["startByte"].Value<long>();
|
sd.StartByte = info["startByte"].Value<long>();
|
||||||
}
|
|
||||||
catch (Exception) { }
|
|
||||||
sd.Headers = Headers;
|
sd.Headers = Headers;
|
||||||
sd.SavePath = DownDir + "\\Part_" + info["part"].Value<int>().ToString(partsPadZero) + "\\" + info["index"].Value<int>().ToString(segsPadZero) + ".tsdownloading";
|
sd.SavePath = DownDir + "\\Part_" + info["part"].Value<int>().ToString(partsPadZero) + "\\" + info["index"].Value<int>().ToString(segsPadZero) + ".tsdownloading";
|
||||||
if (File.Exists(sd.SavePath))
|
if (File.Exists(sd.SavePath))
|
||||||
@@ -327,12 +296,8 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
watcher.WatcherStop();
|
watcher.WatcherStop();
|
||||||
|
|
||||||
//监控文件夹大小变化的收尾工作
|
//停止速度监测
|
||||||
timer.Enabled = false;
|
timer.Enabled = false;
|
||||||
timer.Close();
|
|
||||||
// cleanup COM
|
|
||||||
//System.Runtime.InteropServices.Marshal.ReleaseComObject(folder);
|
|
||||||
//System.Runtime.InteropServices.Marshal.ReleaseComObject(fso);
|
|
||||||
|
|
||||||
//检测是否下完
|
//检测是否下完
|
||||||
IsComplete(Convert.ToInt32(segCount));
|
IsComplete(Convert.ToInt32(segCount));
|
||||||
@@ -363,8 +328,7 @@ namespace N_m3u8DL_CLI
|
|||||||
Count++;
|
Count++;
|
||||||
LOGGER.WriteLine(strings.retryCount + Count + " / " + RetryCount);
|
LOGGER.WriteLine(strings.retryCount + Count + " / " + RetryCount);
|
||||||
LOGGER.PrintLine(strings.retryCount + Count + " / " + RetryCount, LOGGER.Warning);
|
LOGGER.PrintLine(strings.retryCount + Count + " / " + RetryCount, LOGGER.Warning);
|
||||||
Thread.Sleep(3000);
|
Thread.Sleep(6000);
|
||||||
GC.Collect(); //垃圾回收
|
|
||||||
DoDownload();
|
DoDownload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -640,7 +604,7 @@ namespace N_m3u8DL_CLI
|
|||||||
parser.DownDir = Path.Combine(Path.GetDirectoryName(DownDir), parser.DownName);
|
parser.DownDir = Path.Combine(Path.GetDirectoryName(DownDir), parser.DownName);
|
||||||
LOGGER.WriteLine(strings.startParsing + externalAudioUrl);
|
LOGGER.WriteLine(strings.startParsing + externalAudioUrl);
|
||||||
LOGGER.WriteLine(strings.downloadingExternalAudioTrack);
|
LOGGER.WriteLine(strings.downloadingExternalAudioTrack);
|
||||||
DownName = DownName + "(Audio)";
|
DownName = parser.DownName;
|
||||||
fflogName = "_ffreport(Audio).log";
|
fflogName = "_ffreport(Audio).log";
|
||||||
DownDir = parser.DownDir;
|
DownDir = parser.DownDir;
|
||||||
parser.Parse(); //开始解析
|
parser.Parse(); //开始解析
|
||||||
|
@@ -30,8 +30,9 @@ namespace N_m3u8DL_CLI
|
|||||||
|
|
||||||
|
|
||||||
/*===============================================================================*/
|
/*===============================================================================*/
|
||||||
static string nowVer = "2.8.2";
|
static Version ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
static string nowDate = "20201122";
|
static string nowVer = $"{ver.Major}.{ver.Minor}.{ver.Build}";
|
||||||
|
static string nowDate = "20201125";
|
||||||
public static void WriteInit()
|
public static void WriteInit()
|
||||||
{
|
{
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
@@ -109,7 +110,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/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.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36";
|
||||||
webRequest.Accept = "*/*";
|
webRequest.Accept = "*/*";
|
||||||
webRequest.Headers.Add("Accept-Encoding", "gzip, deflate, br");
|
webRequest.Headers.Add("Accept-Encoding", "gzip, deflate, br");
|
||||||
webRequest.Timeout = TimeOut; //设置超时
|
webRequest.Timeout = TimeOut; //设置超时
|
||||||
@@ -119,7 +120,7 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
webRequest.UserAgent = "";
|
webRequest.UserAgent = "";
|
||||||
if (!url.Contains("/internettv/"))
|
if (!url.Contains("/internettv/"))
|
||||||
webRequest.Referer = "https://player.mgtv.com/mgtv_v6_player/PlayerCore.swf";
|
webRequest.Referer = "https://www.mgtv.com";
|
||||||
webRequest.Headers.Add("Cookie", "MQGUID");
|
webRequest.Headers.Add("Cookie", "MQGUID");
|
||||||
}
|
}
|
||||||
//添加headers
|
//添加headers
|
||||||
@@ -157,7 +158,7 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
|
|
||||||
//文件过大则认为不是m3u8
|
//文件过大则认为不是m3u8
|
||||||
if (webResponse.ContentLength != -1 && webRequest.ContentLength > 50 * 1024 * 1024) return "";
|
if (webResponse.ContentLength != -1 && webResponse.ContentLength > 50 * 1024 * 1024) return "";
|
||||||
|
|
||||||
if (webResponse.ContentEncoding != null
|
if (webResponse.ContentEncoding != null
|
||||||
&& webResponse.ContentEncoding.ToLower() == "gzip") //如果使用了GZip则先解压
|
&& webResponse.ContentEncoding.ToLower() == "gzip") //如果使用了GZip则先解压
|
||||||
@@ -426,7 +427,7 @@ namespace N_m3u8DL_CLI
|
|||||||
if (NoProxy) req.Proxy = null;
|
if (NoProxy) req.Proxy = null;
|
||||||
req.Headers.Add("Accept-Encoding", "gzip, deflate");
|
req.Headers.Add("Accept-Encoding", "gzip, deflate");
|
||||||
req.Accept = "*/*";
|
req.Accept = "*/*";
|
||||||
req.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";
|
req.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36";
|
||||||
//添加headers
|
//添加headers
|
||||||
if (headers != "")
|
if (headers != "")
|
||||||
{
|
{
|
||||||
@@ -535,11 +536,11 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
request.UserAgent = "";
|
request.UserAgent = "";
|
||||||
if (!url.Contains("/internettv/"))
|
if (!url.Contains("/internettv/"))
|
||||||
request.Referer = "https://player.mgtv.com/mgtv_v6_player/PlayerCore.swf";
|
request.Referer = "https://www.mgtv.com";
|
||||||
request.Headers.Add("Cookie", "MQGUID");
|
request.Headers.Add("Cookie", "MQGUID");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
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";
|
request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36";
|
||||||
//下载部分字节
|
//下载部分字节
|
||||||
if (expectByte != -1)
|
if (expectByte != -1)
|
||||||
request.AddRange("bytes", startByte, startByte + expectByte - 1);
|
request.AddRange("bytes", startByte, startByte + expectByte - 1);
|
||||||
|
@@ -112,9 +112,9 @@ namespace N_m3u8DL_CLI
|
|||||||
/// <param name="downDir">文件存储目录</param>
|
/// <param name="downDir">文件存储目录</param>
|
||||||
/// <param name="mpdUrl">MPD链接</param>
|
/// <param name="mpdUrl">MPD链接</param>
|
||||||
/// <param name="mpdContent">MPD内容</param>
|
/// <param name="mpdContent">MPD内容</param>
|
||||||
/// <param name="customBase">自定义BaseUrl</param>
|
/// <param name="defaultBase">BaseUrl</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string Parse(string downDir, string mpdUrl, string mpdContent, string customBase = "")
|
public static string Parse(string downDir, string mpdUrl, string mpdContent, string defaultBase = "")
|
||||||
{
|
{
|
||||||
XmlDocument mpdDoc = new XmlDocument();
|
XmlDocument mpdDoc = new XmlDocument();
|
||||||
mpdDoc.LoadXml(mpdContent);
|
mpdDoc.LoadXml(mpdContent);
|
||||||
@@ -194,7 +194,7 @@ namespace N_m3u8DL_CLI
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mpdBaseUrl = string.IsNullOrEmpty(customBase) ? GetBaseUrl(mpdUrl) : customBase;
|
var mpdBaseUrl = string.IsNullOrEmpty(defaultBase) ? GetBaseUrl(mpdUrl) : defaultBase;
|
||||||
if (!string.IsNullOrEmpty(mpdBaseUrl) && !CheckBaseUrl())
|
if (!string.IsNullOrEmpty(mpdBaseUrl) && !CheckBaseUrl())
|
||||||
{
|
{
|
||||||
if (!mpdBaseUrl.EndsWith("/") && !baseUrl.StartsWith("/"))
|
if (!mpdBaseUrl.EndsWith("/") && !baseUrl.StartsWith("/"))
|
||||||
@@ -214,7 +214,7 @@ namespace N_m3u8DL_CLI
|
|||||||
["Height"] = IntOrNull(GetAttribute("height")),
|
["Height"] = IntOrNull(GetAttribute("height")),
|
||||||
["Tbr"] = DoubleOrNull(bandwidth, 1000),
|
["Tbr"] = DoubleOrNull(bandwidth, 1000),
|
||||||
["Asr"] = IntOrNull(GetAttribute("audioSamplingRate")),
|
["Asr"] = IntOrNull(GetAttribute("audioSamplingRate")),
|
||||||
["Fps"] = IntOrNull(GetAttribute("audioSamplingRate")),
|
["Fps"] = IntOrNull(GetAttribute("frameRate")),
|
||||||
["Language"] = lang,
|
["Language"] = lang,
|
||||||
["Codecs"] = GetAttribute("codecs")
|
["Codecs"] = GetAttribute("codecs")
|
||||||
};
|
};
|
||||||
@@ -252,6 +252,7 @@ namespace N_m3u8DL_CLI
|
|||||||
var initializationUrl = "";
|
var initializationUrl = "";
|
||||||
if (initializationTemplate.Contains("{0:D"))
|
if (initializationTemplate.Contains("{0:D"))
|
||||||
{
|
{
|
||||||
|
if (initializationTemplate.Contains("{{Bandwidth}}"))
|
||||||
initializationUrl = string.Format(initializationTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
initializationUrl = string.Format(initializationTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -260,6 +261,7 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
representationMsInfo["InitializationUrl"] = CombineURL(baseUrl, initializationUrl);
|
representationMsInfo["InitializationUrl"] = CombineURL(baseUrl, initializationUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
string LocationKey(string location)
|
string LocationKey(string location)
|
||||||
{
|
{
|
||||||
return Regex.IsMatch(location, "^https?://") ? "url" : "path";
|
return Regex.IsMatch(location, "^https?://") ? "url" : "path";
|
||||||
@@ -284,13 +286,15 @@ namespace N_m3u8DL_CLI
|
|||||||
var segUrl = "";
|
var segUrl = "";
|
||||||
if (mediaTemplate.Contains("{0:D"))
|
if (mediaTemplate.Contains("{0:D"))
|
||||||
{
|
{
|
||||||
|
if (mediaTemplate.Contains("{{Bandwidth}}"))
|
||||||
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||||
|
if (mediaTemplate.Contains("{{Number}}"))
|
||||||
segUrl = string.Format(mediaTemplate, i).Replace("{{Number}}", "");
|
segUrl = string.Format(mediaTemplate, i).Replace("{{Number}}", "");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
segUrl = mediaTemplate.Replace("{{Bandwidth}}", bandwidth.ToString());
|
segUrl = mediaTemplate.Replace("{{Bandwidth}}", bandwidth.ToString());
|
||||||
segUrl = mediaTemplate.Replace("{{Number}}", i.ToString());
|
segUrl = segUrl.Replace("{{Number}}", i.ToString());
|
||||||
}
|
}
|
||||||
fragments.Add(new Dictionary<string, dynamic>()
|
fragments.Add(new Dictionary<string, dynamic>()
|
||||||
{
|
{
|
||||||
@@ -313,15 +317,18 @@ namespace N_m3u8DL_CLI
|
|||||||
var segUrl = "";
|
var segUrl = "";
|
||||||
if (mediaTemplate.Contains("{0:D"))
|
if (mediaTemplate.Contains("{0:D"))
|
||||||
{
|
{
|
||||||
|
if (mediaTemplate.Contains("{{Bandwidth}}"))
|
||||||
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||||
|
if (mediaTemplate.Contains("{{Number}}"))
|
||||||
segUrl = string.Format(mediaTemplate, segmentNumber).Replace("{{Number}}", "");
|
segUrl = string.Format(mediaTemplate, segmentNumber).Replace("{{Number}}", "");
|
||||||
|
if (mediaTemplate.Contains("{{Time}}"))
|
||||||
segUrl = string.Format(mediaTemplate, segmentTime).Replace("{{Time}}", "");
|
segUrl = string.Format(mediaTemplate, segmentTime).Replace("{{Time}}", "");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
segUrl = mediaTemplate.Replace("{{Bandwidth}}", bandwidth.ToString());
|
segUrl = mediaTemplate.Replace("{{Bandwidth}}", bandwidth.ToString());
|
||||||
segUrl = mediaTemplate.Replace("{{Number}}", segmentNumber.ToString());
|
segUrl = segUrl.Replace("{{Number}}", segmentNumber.ToString());
|
||||||
segUrl = mediaTemplate.Replace("{{Time}}", segmentTime.ToString());
|
segUrl = segUrl.Replace("{{Time}}", segmentTime.ToString());
|
||||||
}
|
}
|
||||||
fragments.Add(new Dictionary<string, dynamic>()
|
fragments.Add(new Dictionary<string, dynamic>()
|
||||||
{
|
{
|
||||||
@@ -429,9 +436,68 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//排序
|
||||||
|
formatList.Sort((a, b) =>
|
||||||
|
{
|
||||||
|
return (a["Width"] + a["Height"]) * 1000 + a["Tbr"] > (b["Width"] + b["Height"]) * 1000 + b["Tbr"] ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
//默认为最高码率的视频和音频
|
||||||
var bestVideo = SelectBestVideo(formatList);
|
var bestVideo = SelectBestVideo(formatList);
|
||||||
var bestAudio = SelectBestAudio(formatList);
|
var bestAudio = SelectBestAudio(formatList);
|
||||||
|
|
||||||
|
var audioLangList = new List<string>();
|
||||||
|
formatList.ForEach(f =>
|
||||||
|
{
|
||||||
|
if (f["Width"] == -1 && !audioLangList.Contains(f["Language"])) audioLangList.Add(f["Language"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (audioLangList.Count > 1)
|
||||||
|
{
|
||||||
|
string Stringify(Dictionary<string, dynamic> f)
|
||||||
|
{
|
||||||
|
var type = f["Width"] == -1 && f["Height"] == -1 ? "Audio" : "Video";
|
||||||
|
var res = type == "Video" ? $"[{f["Width"]}x{f["Height"]}]" : "";
|
||||||
|
var id = $"[{f["FormatId"]}] ";
|
||||||
|
var tbr = $"[{((int)f["Tbr"]).ToString().PadLeft(4)} Kbps] ";
|
||||||
|
var asr = f["Asr"] != -1 ? $"[{f["Asr"]} Hz] " : "";
|
||||||
|
var fps = f["Fps"] != -1 ? $"[{f["Fps"]} fps] " : "";
|
||||||
|
var lang = string.IsNullOrEmpty(f["Language"]) ? "" : $"[{f["Language"]}] ";
|
||||||
|
var codecs = $"[{f["Codecs"]}] ";
|
||||||
|
return $"{type} => {id}{tbr}{asr}{fps}{lang}{codecs}{res}";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < formatList.Count; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {Stringify(formatList[i])}");
|
||||||
|
LOGGER.CursorIndex++;
|
||||||
|
}
|
||||||
|
Console.CursorVisible = true;
|
||||||
|
LOGGER.PrintLine("Found Multiple Language Audio Tracks.\r\n" + "".PadRight(13) + "Please Select What You Want(Up to 1 Video and 1 Audio).");
|
||||||
|
Console.Write("".PadRight(13) + "Enter Numbers Separated By A Space: ");
|
||||||
|
var input = Console.ReadLine();
|
||||||
|
LOGGER.CursorIndex += 2;
|
||||||
|
Console.CursorVisible = false;
|
||||||
|
if (!string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
bestVideo = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };
|
||||||
|
bestAudio = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };
|
||||||
|
foreach (var index in input.Split())
|
||||||
|
{
|
||||||
|
var n = 0;
|
||||||
|
int.TryParse(index, out n);
|
||||||
|
if (formatList[n]["Width"] == -1)
|
||||||
|
{
|
||||||
|
bestAudio = formatList[n];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bestVideo = formatList[n];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (bestVideo.Keys.Count > 1 && bestAudio.Keys.Count > 1) //音视频
|
if (bestVideo.Keys.Count > 1 && bestAudio.Keys.Count > 1) //音视频
|
||||||
{
|
{
|
||||||
return GenerateMasterList(downDir, bestVideo, bestAudio);
|
return GenerateMasterList(downDir, bestVideo, bestAudio);
|
||||||
@@ -442,7 +508,7 @@ namespace N_m3u8DL_CLI
|
|||||||
}
|
}
|
||||||
else if (bestVideo.Keys.Count > 1) //仅有视频
|
else if (bestVideo.Keys.Count > 1) //仅有视频
|
||||||
{
|
{
|
||||||
return GenerateMasterList(downDir, bestAudio);
|
return GenerateMasterList(downDir, bestVideo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -472,26 +538,42 @@ namespace N_m3u8DL_CLI
|
|||||||
//Video
|
//Video
|
||||||
if (m3u8.Contains("#EXT-VIDEO-WIDTH"))
|
if (m3u8.Contains("#EXT-VIDEO-WIDTH"))
|
||||||
{
|
{
|
||||||
var _path = Path.Combine(downDir, "bestVideo.m3u8");
|
var _path = Path.Combine(downDir, "mpdVideo.m3u8");
|
||||||
File.WriteAllText(_path, m3u8);
|
File.WriteAllText(_path, m3u8);
|
||||||
videoPath = new Uri(_path).ToString();
|
videoPath = new Uri(_path).ToString();
|
||||||
res = f["Width"] + "x" + f["Height"];
|
res = f["Width"] + "x" + f["Height"];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(downDir + "(Audio)"))
|
var _path = Path.Combine(downDir, "mpdAudio.m3u8");
|
||||||
Directory.CreateDirectory(downDir + "(Audio)");
|
|
||||||
var _path = Path.Combine(downDir + "(Audio)", "bestAudio.m3u8");
|
|
||||||
File.WriteAllText(_path, m3u8);
|
File.WriteAllText(_path, m3u8);
|
||||||
audioPath = new Uri(_path).ToString();
|
audioPath = new Uri(_path).ToString();
|
||||||
}
|
}
|
||||||
codecsList.Add(f["Codecs"]);
|
codecsList.Add(f["Codecs"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var content = $"#EXTM3U\r\n" +
|
var content = "";
|
||||||
|
if (videoPath == "" && audioPath != "")
|
||||||
|
{
|
||||||
|
return audioPath;
|
||||||
|
}
|
||||||
|
else if (audioPath == "" && videoPath != "")
|
||||||
|
{
|
||||||
|
return videoPath;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(downDir + "(Audio)"))
|
||||||
|
Directory.CreateDirectory(downDir + "(Audio)");
|
||||||
|
var _path = Path.Combine(downDir + "(Audio)", "mpdAudio.m3u8");
|
||||||
|
File.Copy(new Uri(audioPath).LocalPath, _path, true);
|
||||||
|
audioPath = new Uri(_path).ToString();
|
||||||
|
content = $"#EXTM3U\r\n" +
|
||||||
$"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"{audioPath}\",GROUP-ID=\"default-audio-group\",NAME=\"stream_0\",AUTOSELECT=YES,CHANNELS=\"0\"\r\n" +
|
$"#EXT-X-MEDIA:TYPE=AUDIO,URI=\"{audioPath}\",GROUP-ID=\"default-audio-group\",NAME=\"stream_0\",AUTOSELECT=YES,CHANNELS=\"0\"\r\n" +
|
||||||
$"#EXT-X-STREAM-INF:BANDWIDTH={bandwidth},CODECS=\"{string.Join(",", codecsList)}\",RESOLUTION={res},AUDIO=\"default-audio-group\"\r\n" +
|
$"#EXT-X-STREAM-INF:BANDWIDTH={bandwidth},CODECS=\"{string.Join(",", codecsList)}\",RESOLUTION={res},AUDIO=\"default-audio-group\"\r\n" +
|
||||||
$"{videoPath}";
|
$"{videoPath}";
|
||||||
|
}
|
||||||
|
|
||||||
var _masterPath = Path.Combine(downDir, "master.m3u8");
|
var _masterPath = Path.Combine(downDir, "master.m3u8");
|
||||||
File.WriteAllText(_masterPath, content);
|
File.WriteAllText(_masterPath, content);
|
||||||
return new Uri(_masterPath).ToString();
|
return new Uri(_masterPath).ToString();
|
||||||
@@ -525,7 +607,7 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
var dur = seg.ContainsKey("duration") ? seg["duration"] : 0.0;
|
var dur = seg.ContainsKey("duration") ? seg["duration"] : 0.0;
|
||||||
var url = seg.ContainsKey("url") ? seg["url"] : seg["path"];
|
var url = seg.ContainsKey("url") ? seg["url"] : seg["path"];
|
||||||
sb.AppendLine($"#EXTINF:{dur}");
|
sb.AppendLine($"#EXTINF:{dur.ToString("0.00")}");
|
||||||
sb.AppendLine(url);
|
sb.AppendLine(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,6 +630,7 @@ namespace N_m3u8DL_CLI
|
|||||||
if (f["Tbr"] > bandwidth)
|
if (f["Tbr"] > bandwidth)
|
||||||
{
|
{
|
||||||
best = f;
|
best = f;
|
||||||
|
bandwidth = f["Tbr"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -569,6 +652,7 @@ namespace N_m3u8DL_CLI
|
|||||||
if (f["Tbr"] > bandwidth)
|
if (f["Tbr"] > bandwidth)
|
||||||
{
|
{
|
||||||
best = f;
|
best = f;
|
||||||
|
bandwidth = f["Tbr"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -864,9 +864,7 @@ namespace N_m3u8DL_CLI
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string GetBaseUrl(string m3u8url, string headers)
|
public static string GetBaseUrl(string m3u8url, string headers)
|
||||||
{
|
{
|
||||||
if (!isQiQiuYun && Global.Get302(m3u8url, headers) != m3u8url)
|
string url = Global.Get302(m3u8url, headers);
|
||||||
m3u8url = Global.Get302(m3u8url, headers);
|
|
||||||
string url = Global.Get302(m3u8url);
|
|
||||||
if (url.Contains("?"))
|
if (url.Contains("?"))
|
||||||
url = url.Remove(url.LastIndexOf('?'));
|
url = url.Remove(url.LastIndexOf('?'));
|
||||||
url = url.Substring(0, url.LastIndexOf('/') + 1);
|
url = url.Substring(0, url.LastIndexOf('/') + 1);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -287,3 +287,11 @@
|
|||||||
- 修复BUG
|
- 修复BUG
|
||||||
2020年11月22日
|
2020年11月22日
|
||||||
- 解决HTTPS协议自动重定向后,Referer丢失问题
|
- 解决HTTPS协议自动重定向后,Referer丢失问题
|
||||||
|
- 新的任务速度监控逻辑
|
||||||
|
2020年11月23日
|
||||||
|
- 将默认UA修改为 Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
|
||||||
|
- 修改芒果TV请求头
|
||||||
|
2020年11月25日
|
||||||
|
- 修正MPD判断最高清晰度的逻辑
|
||||||
|
- 在MPD输入下支持选择音轨
|
||||||
|
- 修复BUG
|
Reference in New Issue
Block a user