You've already forked N_m3u8DL-CLI
mirror of
https://github.com/nilaoda/N_m3u8DL-CLI
synced 2025-10-09 00:22:13 +02:00
Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
82f2111522 | ||
![]() |
4c3207586f | ||
![]() |
69b411e37c | ||
![]() |
1e8525041f | ||
![]() |
65ae72d4a4 |
@@ -368,6 +368,8 @@ namespace N_m3u8DL_CLI
|
|||||||
//有MAP文件,一般为mp4,采取默认动作
|
//有MAP文件,一般为mp4,采取默认动作
|
||||||
if(File.Exists(DownDir + "\\Part_0\\!MAP.ts"))
|
if(File.Exists(DownDir + "\\Part_0\\!MAP.ts"))
|
||||||
MuxFormat = "mp4";
|
MuxFormat = "mp4";
|
||||||
|
if (isVTT)
|
||||||
|
MuxFormat = "vtt";
|
||||||
|
|
||||||
if (Global.AUDIO_TYPE != "")
|
if (Global.AUDIO_TYPE != "")
|
||||||
MuxFormat = Global.AUDIO_TYPE;
|
MuxFormat = Global.AUDIO_TYPE;
|
||||||
@@ -528,6 +530,8 @@ namespace N_m3u8DL_CLI
|
|||||||
//有MAP文件,一般为mp4,采取默认动作
|
//有MAP文件,一般为mp4,采取默认动作
|
||||||
if (File.Exists(DownDir + "\\!MAP.ts"))
|
if (File.Exists(DownDir + "\\!MAP.ts"))
|
||||||
MuxFormat = "mp4";
|
MuxFormat = "mp4";
|
||||||
|
if (isVTT)
|
||||||
|
MuxFormat = "vtt";
|
||||||
Global.CombineMultipleFilesIntoSingleFile(Global.GetFiles(DownDir, ".ts"), FFmpeg.OutPutPath + $".{MuxFormat}");
|
Global.CombineMultipleFilesIntoSingleFile(Global.GetFiles(DownDir, ".ts"), FFmpeg.OutPutPath + $".{MuxFormat}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@@ -32,7 +32,7 @@ namespace N_m3u8DL_CLI
|
|||||||
/*===============================================================================*/
|
/*===============================================================================*/
|
||||||
static Version ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
static Version ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
static string nowVer = $"{ver.Major}.{ver.Minor}.{ver.Build}";
|
static string nowVer = $"{ver.Major}.{ver.Minor}.{ver.Build}";
|
||||||
static string nowDate = "20210118";
|
static string nowDate = "20210124";
|
||||||
public static void WriteInit()
|
public static void WriteInit()
|
||||||
{
|
{
|
||||||
Console.Clear();
|
Console.Clear();
|
||||||
|
@@ -520,6 +520,7 @@ namespace N_m3u8DL_CLI
|
|||||||
return $"{type} => {id}{tbr}{asr}{fps}{lang}{codecs}{res}";
|
return $"{type} => {id}{tbr}{asr}{fps}{lang}{codecs}{res}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var startCursorIndex = LOGGER.CursorIndex;
|
||||||
for (int i = 0; i < formatList.Count; i++)
|
for (int i = 0; i < formatList.Count; i++)
|
||||||
{
|
{
|
||||||
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {Stringify(formatList[i])}");
|
Console.WriteLine("".PadRight(13) + $"[{i.ToString().PadLeft(2)}]. {Stringify(formatList[i])}");
|
||||||
@@ -531,6 +532,12 @@ namespace N_m3u8DL_CLI
|
|||||||
var input = Console.ReadLine();
|
var input = Console.ReadLine();
|
||||||
LOGGER.CursorIndex += 2;
|
LOGGER.CursorIndex += 2;
|
||||||
Console.CursorVisible = false;
|
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))
|
if (!string.IsNullOrEmpty(input))
|
||||||
{
|
{
|
||||||
bestVideo = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };
|
bestVideo = new Dictionary<string, dynamic>() { ["Tbr"] = 0 };
|
||||||
@@ -658,7 +665,7 @@ namespace N_m3u8DL_CLI
|
|||||||
string rangeStr = match.Value;
|
string rangeStr = match.Value;
|
||||||
long start = Convert.ToInt64(match.Groups[1].Value);
|
long start = Convert.ToInt64(match.Groups[1].Value);
|
||||||
long end = Convert.ToInt64(match.Groups[2].Value);
|
long end = Convert.ToInt64(match.Groups[2].Value);
|
||||||
sb.AppendLine($"#EXT-X-MAP:URI=\"{initUrl.Replace(rangeStr, "")}\",BYTERANGE=\"{end}@{start}\"");
|
sb.AppendLine($"#EXT-X-MAP:URI=\"{initUrl.Replace(rangeStr, "")}\",BYTERANGE=\"{end + 1 - start}@{start}\"");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -11,6 +11,29 @@ namespace N_m3u8DL_CLI
|
|||||||
{
|
{
|
||||||
class Parser
|
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这一步
|
//存储上一行key的信息,如果一样,就跳过下载key这一步
|
||||||
private string lastKeyLine = string.Empty;
|
private string lastKeyLine = string.Empty;
|
||||||
//METHOD, KEY, IV
|
//METHOD, KEY, IV
|
||||||
@@ -21,9 +44,9 @@ namespace N_m3u8DL_CLI
|
|||||||
private string bestUrl = string.Empty;
|
private string bestUrl = string.Empty;
|
||||||
private string bestUrlAudio = string.Empty;
|
private string bestUrlAudio = string.Empty;
|
||||||
private string bestUrlSub = 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; //音轨地址
|
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 string subUrl = string.Empty; //字幕地址
|
||||||
//存放多轨道的信息
|
//存放多轨道的信息
|
||||||
private ArrayList extLists = new ArrayList();
|
private ArrayList extLists = new ArrayList();
|
||||||
@@ -65,6 +88,8 @@ namespace N_m3u8DL_CLI
|
|||||||
JArray segments = new JArray();
|
JArray segments = new JArray();
|
||||||
JObject segInfo = new JObject();
|
JObject segInfo = new JObject();
|
||||||
extLists.Clear();
|
extLists.Clear();
|
||||||
|
MEDIA_AUDIO_GROUP.Clear();
|
||||||
|
MEDIA_SUB_GROUP.Clear();
|
||||||
string m3u8Content = string.Empty;
|
string m3u8Content = string.Empty;
|
||||||
string m3u8Method = string.Empty;
|
string m3u8Method = string.Empty;
|
||||||
string[] extMAP = { "", "" };
|
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为空则截取字符串充当
|
//如果BaseUrl为空则截取字符串充当
|
||||||
if (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_i_frame_stream_inf)) ;
|
||||||
else if (line.StartsWith(HLSTags.ext_x_media))
|
else if (line.StartsWith(HLSTags.ext_x_media))
|
||||||
{
|
{
|
||||||
if (Global.GetTagAttribute(line, "TYPE") == "AUDIO" && !MEDIA_AUDIO.ContainsKey(Global.GetTagAttribute(line, "GROUP-ID")))
|
var groupId = Global.GetTagAttribute(line, "GROUP-ID");
|
||||||
MEDIA_AUDIO.Add(Global.GetTagAttribute(line, "GROUP-ID"), CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI")));
|
if (Global.GetTagAttribute(line, "TYPE") == "AUDIO")
|
||||||
if (Global.GetTagAttribute(line, "TYPE") == "SUBTITLES")
|
|
||||||
{
|
{
|
||||||
if (!MEDIA_SUB.ContainsKey(Global.GetTagAttribute(line, "GROUP-ID")))
|
var audio = new Audio();
|
||||||
MEDIA_SUB.Add(Global.GetTagAttribute(line, "GROUP-ID"), CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI")));
|
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)) ;
|
else if (line.StartsWith(HLSTags.ext_x_playlist_type)) ;
|
||||||
@@ -504,13 +564,71 @@ namespace N_m3u8DL_CLI
|
|||||||
jsonM3u8Info.Add("vod", isEndlist);
|
jsonM3u8Info.Add("vod", isEndlist);
|
||||||
jsonM3u8Info.Add("targetDuration", targetDuration);
|
jsonM3u8Info.Add("targetDuration", targetDuration);
|
||||||
jsonM3u8Info.Add("totalDuration", totalDuration);
|
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] != "")
|
if (extMAP[0] != "")
|
||||||
{
|
{
|
||||||
@@ -807,13 +925,24 @@ namespace N_m3u8DL_CLI
|
|||||||
File.Copy(m3u8SavePath, Path.GetDirectoryName(m3u8SavePath) + "\\master.m3u8", true);
|
File.Copy(m3u8SavePath, Path.GetDirectoryName(m3u8SavePath) + "\\master.m3u8", true);
|
||||||
LOGGER.WriteLine("Master List Found");
|
LOGGER.WriteLine("Master List Found");
|
||||||
LOGGER.PrintLine(strings.masterListFound, LOGGER.Warning);
|
LOGGER.PrintLine(strings.masterListFound, LOGGER.Warning);
|
||||||
string t = "{" + "\"masterUri\":\"" + M3u8Url + "\","
|
var json = new JObject();
|
||||||
+ "\"updateTime\":\"" + DateTime.Now.ToString("o") + "\","
|
json.Add("masterUri", M3u8Url);
|
||||||
+ "\"playLists:\":[" + string.Join(",", extLists.ToArray()) + "]" + "}";
|
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文件
|
//输出json文件
|
||||||
LOGGER.WriteLine(strings.wrtingMasterMeta);
|
LOGGER.WriteLine(strings.wrtingMasterMeta);
|
||||||
LOGGER.PrintLine(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.WriteLine(strings.selectPlaylist + ": " + bestUrl);
|
||||||
LOGGER.PrintLine(strings.selectPlaylist);
|
LOGGER.PrintLine(strings.selectPlaylist);
|
||||||
LOGGER.WriteLine(strings.startReParsing);
|
LOGGER.WriteLine(strings.startReParsing);
|
||||||
|
@@ -309,3 +309,7 @@
|
|||||||
2021年1月18日
|
2021年1月18日
|
||||||
- 完善MPD下载相关
|
- 完善MPD下载相关
|
||||||
- 重新打包多语言资源
|
- 重新打包多语言资源
|
||||||
|
2021年1月24日
|
||||||
|
- 适配Disney+资源
|
||||||
|
- MPD选择流行为优化
|
||||||
|
- 修复二进制合并时vtt字幕被合并为ts后缀问题
|
Reference in New Issue
Block a user