1
mirror of https://github.com/nilaoda/N_m3u8DL-CLI synced 2025-09-13 22:40:51 +02:00

Compare commits

...

15 Commits
2.8.9 ... 2.9.4

Author SHA1 Message Date
nilaoda
82f2111522 update 2021-01-24 16:26:32 +08:00
nilaoda
4c3207586f Update MPDParser.cs 2021-01-24 16:24:03 +08:00
nilaoda
69b411e37c fix sub merge bug 2021-01-24 16:22:13 +08:00
nilaoda
1e8525041f Download from DSNP 2021-01-24 16:21:32 +08:00
nilaoda
65ae72d4a4 Update MPDParser.cs 2021-01-18 20:36:43 +08:00
nilaoda
4a4bfae5ab 优化MPD下载行为 2021-01-18 02:00:19 +08:00
nilaoda
d586dddfcd Update changelog.txt 2021-01-18 01:59:31 +08:00
nilaoda
fca6b3ff6c Update changelog.txt 2020-12-29 23:19:39 +08:00
nilaoda
5d75626a36 Update Global.cs 2020-12-29 23:18:16 +08:00
nilaoda
a94271c244 mpd - xigua 2020-12-20 18:53:40 +08:00
nilaoda
c51118dce7 解密huke88 2020-12-20 18:53:02 +08:00
nilaoda
81b2e87bf7 处理同一ID分散在不同Period的情况 2020-12-12 02:07:41 +08:00
nilaoda
71a9878aaa Update changelog.txt 2020-12-06 21:32:48 +08:00
nilaoda
769fe4e926 Update Global.cs 2020-12-06 21:32:38 +08:00
nilaoda
1f57ba7c09 Update Parser.cs 2020-12-06 21:32:29 +08:00
8 changed files with 328 additions and 43 deletions

View File

@@ -0,0 +1,53 @@
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace N_m3u8DL_CLI
{
//https://js.huke88.com/assets/revision/js/plugins/tcplayer/tcplayer.v4.1.min.js?v=930
//https://js.huke88.com/assets/revision/js/plugins/tcplayer/libs/hls.min.0.13.2m.js?v=930
class DecodeHuke88Key
{
private static string[] GetOverlayInfo(string url)
{
var enc = new Regex("eyJ\\w{100,}").Match(url).Value;
var json = Encoding.UTF8.GetString(Convert.FromBase64String(enc));
JObject jObject = JObject.Parse(json);
var key = jObject["overlayKey"].ToString();
var iv = jObject["overlayIv"].ToString();
return new string[] { key, iv };
}
public static string DecodeKey(string url, byte[] data)
{
var info = GetOverlayInfo(url);
var overlayKey = info[0];
var overlayIv = info[1];
var l = new List<byte>();
var c = new List<byte>();
for (int h = 0; h < 16; h++)
{
var f = overlayKey.Substring(2 * h, 2);
var g = overlayIv.Substring(2 * h, 2);
l.Add(Convert.ToByte(f, 16));
c.Add(Convert.ToByte(g, 16));
}
var _lastCipherblock = c.ToArray();
var t = new byte[data.Length];
var r = data;
r = Decrypter.AES128Decrypt(data, l.ToArray(), Decrypter.HexStringToBytes("00000000000000000000000000000000"), CipherMode.CBC, PaddingMode.Zeros);
for (var o = 0; o < 16; o++)
t[o] = (byte)(r[o] ^ _lastCipherblock[o]);
var key = Convert.ToBase64String(t);
return key;
}
}
}

View File

@@ -1,16 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace N_m3u8DL_CLI
{
class Decrypter
{
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC)
public static byte[] AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
FileStream fs = new FileStream(filePath, FileMode.Open);
//获取文件大小
@@ -25,14 +21,14 @@ namespace N_m3u8DL_CLI
dcpt.Key = keyByte;
dcpt.IV = ivByte;
dcpt.Mode = mode;
dcpt.Padding = PaddingMode.PKCS7;
dcpt.Padding = padding;
ICryptoTransform cTransform = dcpt.CreateDecryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
return resultArray;
}
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC)
public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
{
byte[] inBuff = encryptedBuff;
@@ -42,7 +38,7 @@ namespace N_m3u8DL_CLI
dcpt.Key = keyByte;
dcpt.IV = ivByte;
dcpt.Mode = mode;
dcpt.Padding = PaddingMode.PKCS7;
dcpt.Padding = padding;
ICryptoTransform cTransform = dcpt.CreateDecryptor();
Byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
@@ -56,7 +52,7 @@ namespace N_m3u8DL_CLI
return new byte[0];
}
if (hexStr.StartsWith("0x") || hexStr.StartsWith("0X"))
if (hexStr.StartsWith("0x") || hexStr.StartsWith("0X"))
{
hexStr = hexStr.Remove(0, 2);
}

View File

@@ -368,6 +368,8 @@ namespace N_m3u8DL_CLI
//有MAP文件一般为mp4采取默认动作
if(File.Exists(DownDir + "\\Part_0\\!MAP.ts"))
MuxFormat = "mp4";
if (isVTT)
MuxFormat = "vtt";
if (Global.AUDIO_TYPE != "")
MuxFormat = Global.AUDIO_TYPE;
@@ -528,6 +530,8 @@ namespace N_m3u8DL_CLI
//有MAP文件一般为mp4采取默认动作
if (File.Exists(DownDir + "\\!MAP.ts"))
MuxFormat = "mp4";
if (isVTT)
MuxFormat = "vtt";
Global.CombineMultipleFilesIntoSingleFile(Global.GetFiles(DownDir, ".ts"), FFmpeg.OutPutPath + $".{MuxFormat}");
}
else

View File

@@ -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 = "20201202";
static string nowDate = "20210124";
public static void WriteInit()
{
Console.Clear();

View File

@@ -61,14 +61,18 @@ namespace N_m3u8DL_CLI
void ExtractInitialization(XmlNode source)
{
var initialization = source.SelectSingleNode("//ns:Initialization", nsMgr);
var initialization = source.SelectSingleNode("ns:Initialization", nsMgr);
if (initialization != null)
{
MultisegmentInfo["InitializationUrl"] = ((XmlElement)initialization).GetAttribute("sourceURL");
if (((XmlElement)initialization).HasAttribute("range"))
{
MultisegmentInfo["InitializationUrl"] += "$$Range=" + ((XmlElement)initialization).GetAttribute("range");
}
}
}
var segmentList = Period.SelectSingleNode("//ns:SegmentList", nsMgr);
var segmentList = Period.SelectSingleNode("ns:SegmentList", nsMgr);
if (segmentList != null)
{
ExtractCommon(segmentList);
@@ -77,7 +81,14 @@ namespace N_m3u8DL_CLI
MultisegmentInfo["SegmentUrls"] = new List<string>();
foreach (XmlElement segment in segmentUrlsE)
{
MultisegmentInfo["SegmentUrls"].Add(segment.GetAttribute("media"));
if (segment.HasAttribute("mediaRange"))
{
MultisegmentInfo["SegmentUrls"].Add("$$Range=" + segment.GetAttribute("mediaRange"));
}
else
{
MultisegmentInfo["SegmentUrls"].Add(segment.GetAttribute("media"));
}
}
}
else
@@ -116,6 +127,10 @@ namespace N_m3u8DL_CLI
/// <returns></returns>
public static string Parse(string downDir, string mpdUrl, string mpdContent, string defaultBase = "")
{
//XiGua
if (mpdContent.Contains("<mas:") && !mpdContent.Contains("xmlns:mas"))
mpdContent = mpdContent.Replace("<MPD ", "<MPD xmlns:mas=\"urn:marlin:mas:1-0:services:schemas:mpd\" ");
XmlDocument mpdDoc = new XmlDocument();
mpdDoc.LoadXml(mpdContent);
@@ -208,6 +223,7 @@ namespace N_m3u8DL_CLI
var bandwidth = IntOrNull(GetAttribute("bandwidth"));
var f = new Dictionary<string, dynamic>
{
["ContentType"] = contentType,
["FormatId"] = representationId,
["ManifestUrl"] = mpdUrl,
["Width"] = IntOrNull(GetAttribute("width")),
@@ -432,6 +448,10 @@ namespace N_m3u8DL_CLI
if (representationMsInfo.ContainsKey("InitializationUrl"))
{
f["InitializationUrl"] = representationMsInfo["InitializationUrl"];
if (f["InitializationUrl"].StartsWith("$$Range"))
{
f["InitializationUrl"] = CombineURL(baseUrl, f["InitializationUrl"]);
}
f["Fragments"] = representationMsInfo["Fragments"];
}
}
@@ -447,7 +467,23 @@ namespace N_m3u8DL_CLI
};
}
formatList.Add(f);
//处理同一ID分散在不同Period的情况
if (formatList.Any(_f => _f["FormatId"] == f["FormatId"] && _f["Width"] == f["Width"] && _f["ContentType"] == f["ContentType"]))
{
for (int i = 0; i < formatList.Count; i++)
{
if (formatList[i]["FormatId"] == f["FormatId"] && formatList[i]["Width"] == f["Width"] && formatList[i]["ContentType"] == f["ContentType"])
{
formatList[i]["Fragments"].AddRange(f["Fragments"]);
break;
}
}
}
else
{
formatList.Add(f);
}
}
}
}
@@ -466,14 +502,14 @@ namespace N_m3u8DL_CLI
var audioLangList = new List<string>();
formatList.ForEach(f =>
{
if (f["Width"] == -1 && !audioLangList.Contains(f["Language"])) audioLangList.Add(f["Language"]);
if (f["ContentType"] == "audio" && !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 type = f["ContentType"] == "aduio" ? "Audio" : "Video";
var res = type == "Video" ? $"[{f["Width"]}x{f["Height"]}]" : "";
var id = $"[{f["FormatId"]}] ";
var tbr = $"[{((int)f["Tbr"]).ToString().PadLeft(4)} Kbps] ";
@@ -484,6 +520,7 @@ namespace N_m3u8DL_CLI
return $"{type} => {id}{tbr}{asr}{fps}{lang}{codecs}{res}";
}
var startCursorIndex = LOGGER.CursorIndex;
for (int i = 0; i < formatList.Count; i++)
{
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {Stringify(formatList[i])}");
@@ -495,6 +532,12 @@ namespace N_m3u8DL_CLI
var input = Console.ReadLine();
LOGGER.CursorIndex += 2;
Console.CursorVisible = false;
for (int i = startCursorIndex; i < LOGGER.CursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
LOGGER.CursorIndex = startCursorIndex;
if (!string.IsNullOrEmpty(input))
{
bestVideo = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };
@@ -503,7 +546,7 @@ namespace N_m3u8DL_CLI
{
var n = 0;
int.TryParse(index, out n);
if (formatList[n]["Width"] == -1)
if (formatList[n]["ContentType"] == "audio")
{
bestAudio = formatList[n];
}
@@ -605,7 +648,7 @@ namespace N_m3u8DL_CLI
sb.AppendLine("#CREATED-BY:N_m3u8DL-CLI");
//Video
if (f["Width"] != -1 && f["Height"] != -1)
if (f["ContentType"] != "audio")
{
sb.AppendLine($"#EXT-VIDEO-WIDTH:{f["Width"]}");
sb.AppendLine($"#EXT-VIDEO-HEIGHT:{f["Height"]}");
@@ -615,7 +658,19 @@ namespace N_m3u8DL_CLI
sb.AppendLine($"#EXT-TBR:{f["Tbr"]}");
if (f.ContainsKey("InitializationUrl"))
{
sb.AppendLine($"#EXT-X-MAP:URI=\"{f["InitializationUrl"]}\"");
string initUrl = f["InitializationUrl"];
if (Regex.IsMatch(initUrl, "\\$\\$Range=(\\d+)-(\\d+)"))
{
var match = Regex.Match(initUrl, "\\$\\$Range=(\\d+)-(\\d+)");
string rangeStr = match.Value;
long start = Convert.ToInt64(match.Groups[1].Value);
long end = Convert.ToInt64(match.Groups[2].Value);
sb.AppendLine($"#EXT-X-MAP:URI=\"{initUrl.Replace(rangeStr, "")}\",BYTERANGE=\"{end + 1 - start}@{start}\"");
}
else
{
sb.AppendLine($"#EXT-X-MAP:URI=\"{initUrl}\"");
}
}
sb.AppendLine("#EXT-X-KEY:METHOD=PLZ-KEEP-RAW,URI=\"None\""); //使下载器使用二进制合并
@@ -625,7 +680,19 @@ 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.ToString("0.00")}");
sb.AppendLine(url);
if (Regex.IsMatch(url, "\\$\\$Range=(\\d+)-(\\d+)"))
{
var match = Regex.Match(url, "\\$\\$Range=(\\d+)-(\\d+)");
string rangeStr = match.Value;
long start = Convert.ToInt64(match.Groups[1].Value);
long end = Convert.ToInt64(match.Groups[2].Value);
sb.AppendLine($"#EXT-X-BYTERANGE:{end + 1 - start}@{start}");
sb.AppendLine(url.Replace(rangeStr, ""));
}
else
{
sb.AppendLine(url);
}
}
sb.AppendLine("#EXT-X-ENDLIST");
@@ -637,17 +704,19 @@ namespace N_m3u8DL_CLI
{
var best = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };
var bandwidth = best["Tbr"];
var width = 0;
foreach (var f in fs)
{
var w = f["Width"];
var h = f["Height"];
if (w != -1 && h != -1)
if (f["ContentType"] == "video")
{
if (f["Tbr"] > bandwidth)
if (f["Tbr"] > bandwidth && w > width)
{
best = f;
bandwidth = f["Tbr"];
width = w;
}
}
}
@@ -662,9 +731,7 @@ namespace N_m3u8DL_CLI
foreach (var f in fs)
{
var w = f["Width"];
var h = f["Height"];
if (w == -1 && h == -1)
if (f["ContentType"] == "audio")
{
if (f["Tbr"] > bandwidth)
{
@@ -709,6 +776,10 @@ namespace N_m3u8DL_CLI
/// <returns></returns>
static string CombineURL(string baseurl, string url)
{
if (url.StartsWith("$$Range"))
{
return baseurl + url;
}
Uri uri1 = new Uri(baseurl);
Uri uri2 = new Uri(uri1, url);
url = uri2.ToString();

View File

@@ -70,6 +70,7 @@
<Compile Include="CommandLineArgumentParser.cs" />
<Compile Include="Decode51CtoKey.cs" />
<Compile Include="DecodeDdyun.cs" />
<Compile Include="DecodeHuke88Key.cs" />
<Compile Include="DecodeImooc.cs" />
<Compile Include="DecodeNfmovies.cs" />
<Compile Include="Decrypter.cs" />

View File

@@ -11,6 +11,29 @@ namespace N_m3u8DL_CLI
{
class Parser
{
struct Audio
{
public string Name;
public string Language;
public string Uri;
public string Channels;
public override string ToString()
{
return $"[{Name}] [{Language}] [{(string.IsNullOrEmpty(Channels) ? "" : $"{Channels}ch")}]".Replace("[]", "");
}
}
struct Subtitle
{
public string Name;
public string Language;
public string Uri;
public override string ToString()
{
return $"[{Name}] [{Language}]";
}
}
//存储上一行key的信息如果一样就跳过下载key这一步
private string lastKeyLine = string.Empty;
//METHOD, KEY, IV
@@ -21,9 +44,9 @@ namespace N_m3u8DL_CLI
private string bestUrl = string.Empty;
private string bestUrlAudio = string.Empty;
private string bestUrlSub = string.Empty;
Dictionary<string, string> MEDIA_AUDIO = new Dictionary<string, string>();
Dictionary<string, List<Audio>> MEDIA_AUDIO_GROUP = new Dictionary<string, List<Audio>>(); //外挂音频所有分组信息
private string audioUrl = string.Empty; //音轨地址
Dictionary<string, string> MEDIA_SUB = new Dictionary<string, string>();
Dictionary<string, List<Subtitle>> MEDIA_SUB_GROUP = new Dictionary<string, List<Subtitle>>(); //外挂字幕所有分组信息
private string subUrl = string.Empty; //字幕地址
//存放多轨道的信息
private ArrayList extLists = new ArrayList();
@@ -65,6 +88,8 @@ namespace N_m3u8DL_CLI
JArray segments = new JArray();
JObject segInfo = new JObject();
extLists.Clear();
MEDIA_AUDIO_GROUP.Clear();
MEDIA_SUB_GROUP.Clear();
string m3u8Content = string.Empty;
string m3u8Method = string.Empty;
string[] extMAP = { "", "" };
@@ -143,6 +168,16 @@ namespace N_m3u8DL_CLI
}
}
//正对Disney+修正
if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && M3u8Url.Contains("media.dssott.com/"))
{
Regex ykmap = new Regex("#EXT-X-MAP:URI=\\\".*?BUMPER/[\\s\\S]+?#EXT-X-DISCONTINUITY");
if (ykmap.IsMatch(m3u8Content))
{
m3u8Content = m3u8Content.Replace(ykmap.Match(m3u8Content).Value, "#XXX");
}
}
//如果BaseUrl为空则截取字符串充当
if (BaseUrl == "")
{
@@ -332,12 +367,37 @@ 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" && !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")
var groupId = Global.GetTagAttribute(line, "GROUP-ID");
if (Global.GetTagAttribute(line, "TYPE") == "AUDIO")
{
if (!MEDIA_SUB.ContainsKey(Global.GetTagAttribute(line, "GROUP-ID")))
MEDIA_SUB.Add(Global.GetTagAttribute(line, "GROUP-ID"), CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI")));
var audio = new Audio();
audio.Channels = Global.GetTagAttribute(line, "CHANNELS");
audio.Language = Global.GetTagAttribute(line, "LANGUAGE");
audio.Name = Global.GetTagAttribute(line, "NAME");
audio.Uri = CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI"));
if (!MEDIA_AUDIO_GROUP.ContainsKey(groupId))
{
MEDIA_AUDIO_GROUP.Add(groupId, new List<Audio>() { audio });
}
else
{
MEDIA_AUDIO_GROUP[groupId].Add(audio);
}
}
else if (Global.GetTagAttribute(line, "TYPE") == "SUBTITLES")
{
var sub = new Subtitle();
sub.Language = Global.GetTagAttribute(line, "LANGUAGE");
sub.Name = Global.GetTagAttribute(line, "NAME");
sub.Uri = CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI"));
if (!MEDIA_SUB_GROUP.ContainsKey(groupId))
{
MEDIA_SUB_GROUP.Add(groupId, new List<Subtitle>() { sub });
}
else
{
MEDIA_SUB_GROUP[groupId].Add(sub);
}
}
}
else if (line.StartsWith(HLSTags.ext_x_playlist_type)) ;
@@ -504,13 +564,71 @@ namespace N_m3u8DL_CLI
jsonM3u8Info.Add("vod", isEndlist);
jsonM3u8Info.Add("targetDuration", targetDuration);
jsonM3u8Info.Add("totalDuration", totalDuration);
if (bestUrlAudio != "" && MEDIA_AUDIO.ContainsKey(bestUrlAudio))
if (audioUrl != "")
jsonM3u8Info.Add("audio", audioUrl);
if (subUrl != "")
jsonM3u8Info.Add("sub", subUrl);
if (bestUrlAudio != "" && MEDIA_AUDIO_GROUP.ContainsKey(bestUrlAudio))
{
jsonM3u8Info.Add("audio", MEDIA_AUDIO[bestUrlAudio]);
if (MEDIA_AUDIO_GROUP[bestUrlAudio].Count == 1)
{
audioUrl = MEDIA_AUDIO_GROUP[bestUrlAudio][0].Uri;
}
//多种音频语言 让用户选择
else
{
var startCursorIndex = LOGGER.CursorIndex;
LOGGER.PrintLine("Found Multiple Language Audio Tracks.", LOGGER.Warning);
for (int i = 0; i < MEDIA_AUDIO_GROUP[bestUrlAudio].Count; i++)
{
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {bestUrlAudio} => {MEDIA_AUDIO_GROUP[bestUrlAudio][i]}");
LOGGER.CursorIndex++;
}
Console.CursorVisible = true;
LOGGER.PrintLine("Please Select What You Want.(Up To 1 Track)");
Console.Write("".PadRight(13) + "Enter Number: ");
var input = Console.ReadLine();
LOGGER.CursorIndex += 2;
Console.CursorVisible = false;
for (int i = startCursorIndex; i < LOGGER.CursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
LOGGER.CursorIndex = startCursorIndex;
audioUrl = MEDIA_AUDIO_GROUP[bestUrlAudio][int.Parse(input)].Uri;
}
}
if (bestUrlSub != "" && MEDIA_SUB.ContainsKey(bestUrlSub))
if (bestUrlSub != "" && MEDIA_SUB_GROUP.ContainsKey(bestUrlSub))
{
jsonM3u8Info.Add("sub", MEDIA_SUB[bestUrlSub]);
if (MEDIA_SUB_GROUP[bestUrlSub].Count == 1)
{
subUrl = MEDIA_SUB_GROUP[bestUrlSub][0].Uri;
}
//多种字幕语言 让用户选择
else
{
var startCursorIndex = LOGGER.CursorIndex;
LOGGER.PrintLine("Found Multiple Language Subtitle Tracks.", LOGGER.Warning);
for (int i = 0; i < MEDIA_SUB_GROUP[bestUrlSub].Count; i++)
{
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {bestUrlSub} => {MEDIA_SUB_GROUP[bestUrlSub][i]}");
LOGGER.CursorIndex++;
}
Console.CursorVisible = true;
LOGGER.PrintLine("Please Select What You Want.(Up To 1 Track)");
Console.Write("".PadRight(13) + "Enter Number: ");
var input = Console.ReadLine();
LOGGER.CursorIndex += 2;
Console.CursorVisible = false;
for (int i = startCursorIndex; i < LOGGER.CursorIndex; i++)
{
Console.SetCursorPosition(0, i);
Console.Write("".PadRight(300));
}
LOGGER.CursorIndex = startCursorIndex;
subUrl = MEDIA_SUB_GROUP[bestUrlSub][int.Parse(input)].Uri;
}
}
if (extMAP[0] != "")
{
@@ -660,7 +778,7 @@ namespace N_m3u8DL_CLI
string keyUrl = key[1];
if (isQiQiuYun)
{
string encKey = Encoding.Default.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers));
/*string encKey = Encoding.Default.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers));
var indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-');
if (encKey.Length == 20)
{
@@ -740,12 +858,30 @@ namespace N_m3u8DL_CLI
{
decKey += encKey[Convert.ToInt32(_i)];
}
key[1] = Convert.ToBase64String(Encoding.Default.GetBytes(decKey));
key[1] = Convert.ToBase64String(Encoding.Default.GetBytes(decKey));*/
key[1] = Convert.ToBase64String(Global.HttpDownloadFileToBytes(keyUrl, "User-Agent: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"));
} //气球云
else if (key[1].Contains("imooc.com/"))
{
key[1] = DecodeImooc.DecodeKey(Global.GetWebSource(key[1], Headers));
}
else if (key[1] == "https://hls.ventunotech.com/m3u8/pc_videosecurevtnkey.key")
{
string temp = Global.GetWebSource(keyUrl, Headers);
LOGGER.PrintLine(temp);
byte[] tempKey = new byte[16];
for (int d = 0; d < 16; d++)
{
tempKey[d] = Convert.ToByte(temp.Substring(2 * d, 2), 16);
}
key[1] = Convert.ToBase64String(tempKey);
}
else if (key[1].Contains("drm.vod2.myqcloud.com/getlicense"))
{
var temp = Global.HttpDownloadFileToBytes(keyUrl, Headers);
key[1] = DecodeHuke88Key.DecodeKey(key[1], temp);
}
else
{
if (keyUrl.Contains("https://keydeliver.linetv.tw/jurassicPark")) //linetv
@@ -789,13 +925,24 @@ namespace N_m3u8DL_CLI
File.Copy(m3u8SavePath, Path.GetDirectoryName(m3u8SavePath) + "\\master.m3u8", true);
LOGGER.WriteLine("Master List Found");
LOGGER.PrintLine(strings.masterListFound, LOGGER.Warning);
string t = "{" + "\"masterUri\":\"" + M3u8Url + "\","
+ "\"updateTime\":\"" + DateTime.Now.ToString("o") + "\","
+ "\"playLists:\":[" + string.Join(",", extLists.ToArray()) + "]" + "}";
var json = new JObject();
json.Add("masterUri", M3u8Url);
json.Add("updateTime", DateTime.Now.ToString("o"));
json.Add("playLists", JArray.Parse("[" + string.Join(",", extLists.ToArray()) + "]"));
if (MEDIA_AUDIO_GROUP.Keys.Count > 0)
{
var audioGroup = JObject.FromObject(MEDIA_AUDIO_GROUP);
json.Add("audioTracks", audioGroup);
}
if (MEDIA_SUB_GROUP.Keys.Count > 0)
{
var subGroup = JObject.FromObject(MEDIA_SUB_GROUP);
json.Add("subtitleTracks", subGroup);
}
//输出json文件
LOGGER.WriteLine(strings.wrtingMasterMeta);
LOGGER.PrintLine(strings.wrtingMasterMeta);
File.WriteAllText(Path.GetDirectoryName(jsonSavePath) + "\\playLists.json", Global.ConvertJsonString(t));
File.WriteAllText(Path.GetDirectoryName(jsonSavePath) + "\\playLists.json", json.ToString());
LOGGER.WriteLine(strings.selectPlaylist + ": " + bestUrl);
LOGGER.PrintLine(strings.selectPlaylist);
LOGGER.WriteLine(strings.startReParsing);

View File

@@ -299,4 +299,17 @@
- 优化MPD识别方案
- 修复MPD情况下时间戳溢出问题
2020年12月2日
- FIX Language Bug
- FIX Language Bug
2020年12月6日
- 使用手机UA请求气球云密钥服务器
2020年12月12日
- 修复MPD下同一个ID分散在不同Period导致下载不完全问题
2020年12月20日
- 支持解密虎课网
2021年1月18日
- 完善MPD下载相关
- 重新打包多语言资源
2021年1月24日
- 适配Disney+资源
- MPD选择流行为优化
- 修复二进制合并时vtt字幕被合并为ts后缀问题