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
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d657b455cd | ||
![]() |
cda6575605 | ||
![]() |
0d6377d41b | ||
![]() |
9c76bdcbce | ||
![]() |
83915ff606 | ||
![]() |
9cd4746f33 | ||
![]() |
33a5b917ac |
@@ -32,7 +32,7 @@ namespace N_m3u8DL_CLI
|
||||
/*===============================================================================*/
|
||||
static Version ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||
static string nowVer = $"{ver.Major}.{ver.Minor}.{ver.Build}";
|
||||
static string nowDate = "20201123";
|
||||
static string nowDate = "20201125";
|
||||
public static void WriteInit()
|
||||
{
|
||||
Console.Clear();
|
||||
|
@@ -112,9 +112,9 @@ namespace N_m3u8DL_CLI
|
||||
/// <param name="downDir">文件存储目录</param>
|
||||
/// <param name="mpdUrl">MPD链接</param>
|
||||
/// <param name="mpdContent">MPD内容</param>
|
||||
/// <param name="customBase">自定义BaseUrl</param>
|
||||
/// <param name="defaultBase">BaseUrl</param>
|
||||
/// <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();
|
||||
mpdDoc.LoadXml(mpdContent);
|
||||
@@ -194,7 +194,7 @@ namespace N_m3u8DL_CLI
|
||||
return url;
|
||||
}
|
||||
|
||||
var mpdBaseUrl = string.IsNullOrEmpty(customBase) ? GetBaseUrl(mpdUrl) : customBase;
|
||||
var mpdBaseUrl = string.IsNullOrEmpty(defaultBase) ? GetBaseUrl(mpdUrl) : defaultBase;
|
||||
if (!string.IsNullOrEmpty(mpdBaseUrl) && !CheckBaseUrl())
|
||||
{
|
||||
if (!mpdBaseUrl.EndsWith("/") && !baseUrl.StartsWith("/"))
|
||||
@@ -214,7 +214,7 @@ namespace N_m3u8DL_CLI
|
||||
["Height"] = IntOrNull(GetAttribute("height")),
|
||||
["Tbr"] = DoubleOrNull(bandwidth, 1000),
|
||||
["Asr"] = IntOrNull(GetAttribute("audioSamplingRate")),
|
||||
["Fps"] = IntOrNull(GetAttribute("audioSamplingRate")),
|
||||
["Fps"] = IntOrNull(GetAttribute("frameRate")),
|
||||
["Language"] = lang,
|
||||
["Codecs"] = GetAttribute("codecs")
|
||||
};
|
||||
@@ -252,7 +252,8 @@ namespace N_m3u8DL_CLI
|
||||
var initializationUrl = "";
|
||||
if (initializationTemplate.Contains("{0:D"))
|
||||
{
|
||||
initializationUrl = string.Format(initializationTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||
if (initializationTemplate.Contains("{{Bandwidth}}"))
|
||||
initializationUrl = string.Format(initializationTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -260,6 +261,7 @@ namespace N_m3u8DL_CLI
|
||||
}
|
||||
representationMsInfo["InitializationUrl"] = CombineURL(baseUrl, initializationUrl);
|
||||
}
|
||||
|
||||
string LocationKey(string location)
|
||||
{
|
||||
return Regex.IsMatch(location, "^https?://") ? "url" : "path";
|
||||
@@ -284,13 +286,15 @@ namespace N_m3u8DL_CLI
|
||||
var segUrl = "";
|
||||
if (mediaTemplate.Contains("{0:D"))
|
||||
{
|
||||
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||
segUrl = string.Format(mediaTemplate, i).Replace("{{Number}}", "");
|
||||
if (mediaTemplate.Contains("{{Bandwidth}}"))
|
||||
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||
if (mediaTemplate.Contains("{{Number}}"))
|
||||
segUrl = string.Format(mediaTemplate, i).Replace("{{Number}}", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
segUrl = mediaTemplate.Replace("{{Bandwidth}}", bandwidth.ToString());
|
||||
segUrl = mediaTemplate.Replace("{{Number}}", i.ToString());
|
||||
segUrl = segUrl.Replace("{{Number}}", i.ToString());
|
||||
}
|
||||
fragments.Add(new Dictionary<string, dynamic>()
|
||||
{
|
||||
@@ -313,15 +317,18 @@ namespace N_m3u8DL_CLI
|
||||
var segUrl = "";
|
||||
if (mediaTemplate.Contains("{0:D"))
|
||||
{
|
||||
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||
segUrl = string.Format(mediaTemplate, segmentNumber).Replace("{{Number}}", "");
|
||||
segUrl = string.Format(mediaTemplate, segmentTime).Replace("{{Time}}", "");
|
||||
if (mediaTemplate.Contains("{{Bandwidth}}"))
|
||||
segUrl = string.Format(mediaTemplate, bandwidth).Replace("{{Bandwidth}}", "");
|
||||
if (mediaTemplate.Contains("{{Number}}"))
|
||||
segUrl = string.Format(mediaTemplate, segmentNumber).Replace("{{Number}}", "");
|
||||
if (mediaTemplate.Contains("{{Time}}"))
|
||||
segUrl = string.Format(mediaTemplate, segmentTime).Replace("{{Time}}", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
segUrl = mediaTemplate.Replace("{{Bandwidth}}", bandwidth.ToString());
|
||||
segUrl = mediaTemplate.Replace("{{Number}}", segmentNumber.ToString());
|
||||
segUrl = mediaTemplate.Replace("{{Time}}", segmentTime.ToString());
|
||||
segUrl = segUrl.Replace("{{Number}}", segmentNumber.ToString());
|
||||
segUrl = segUrl.Replace("{{Time}}", segmentTime.ToString());
|
||||
}
|
||||
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 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) //音视频
|
||||
{
|
||||
return GenerateMasterList(downDir, bestVideo, bestAudio);
|
||||
@@ -442,7 +508,7 @@ namespace N_m3u8DL_CLI
|
||||
}
|
||||
else if (bestVideo.Keys.Count > 1) //仅有视频
|
||||
{
|
||||
return GenerateMasterList(downDir, bestAudio);
|
||||
return GenerateMasterList(downDir, bestVideo);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -472,26 +538,42 @@ namespace N_m3u8DL_CLI
|
||||
//Video
|
||||
if (m3u8.Contains("#EXT-VIDEO-WIDTH"))
|
||||
{
|
||||
var _path = Path.Combine(downDir, "bestVideo.m3u8");
|
||||
var _path = Path.Combine(downDir, "mpdVideo.m3u8");
|
||||
File.WriteAllText(_path, m3u8);
|
||||
videoPath = new Uri(_path).ToString();
|
||||
res = f["Width"] + "x" + f["Height"];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Directory.Exists(downDir + "(Audio)"))
|
||||
Directory.CreateDirectory(downDir + "(Audio)");
|
||||
var _path = Path.Combine(downDir + "(Audio)", "bestAudio.m3u8");
|
||||
var _path = Path.Combine(downDir, "mpdAudio.m3u8");
|
||||
File.WriteAllText(_path, m3u8);
|
||||
audioPath = new Uri(_path).ToString();
|
||||
}
|
||||
codecsList.Add(f["Codecs"]);
|
||||
}
|
||||
|
||||
var 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-STREAM-INF:BANDWIDTH={bandwidth},CODECS=\"{string.Join(",", codecsList)}\",RESOLUTION={res},AUDIO=\"default-audio-group\"\r\n" +
|
||||
$"{videoPath}";
|
||||
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-STREAM-INF:BANDWIDTH={bandwidth},CODECS=\"{string.Join(",", codecsList)}\",RESOLUTION={res},AUDIO=\"default-audio-group\"\r\n" +
|
||||
$"{videoPath}";
|
||||
}
|
||||
|
||||
var _masterPath = Path.Combine(downDir, "master.m3u8");
|
||||
File.WriteAllText(_masterPath, content);
|
||||
return new Uri(_masterPath).ToString();
|
||||
@@ -525,7 +607,7 @@ namespace N_m3u8DL_CLI
|
||||
{
|
||||
var dur = seg.ContainsKey("duration") ? seg["duration"] : 0.0;
|
||||
var url = seg.ContainsKey("url") ? seg["url"] : seg["path"];
|
||||
sb.AppendLine($"#EXTINF:{dur}");
|
||||
sb.AppendLine($"#EXTINF:{dur.ToString("0.00")}");
|
||||
sb.AppendLine(url);
|
||||
}
|
||||
|
||||
@@ -548,6 +630,7 @@ namespace N_m3u8DL_CLI
|
||||
if (f["Tbr"] > bandwidth)
|
||||
{
|
||||
best = f;
|
||||
bandwidth = f["Tbr"];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -569,6 +652,7 @@ namespace N_m3u8DL_CLI
|
||||
if (f["Tbr"] > bandwidth)
|
||||
{
|
||||
best = f;
|
||||
bandwidth = f["Tbr"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -290,4 +290,8 @@
|
||||
- 新的任务速度监控逻辑
|
||||
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请求头
|
||||
- 修改芒果TV请求头
|
||||
2020年11月25日
|
||||
- 修正MPD判断最高清晰度的逻辑
|
||||
- 在MPD输入下支持选择音轨
|
||||
- 修复BUG
|
Reference in New Issue
Block a user