Skip to content

Commit

Permalink
Merge pull request #295 from Diazole/286-add-dash-role-scheme
Browse files Browse the repository at this point in the history
Add support for the dash role scheme. #286
  • Loading branch information
nilaoda authored Oct 19, 2023
2 parents a7bba3d + 7875b1b commit 75b270b
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 31 deletions.
21 changes: 12 additions & 9 deletions src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ public class StreamSpec
public string? Channels { get; set; }
public string? Extension { get; set; }

//Dash
public RoleType? Role { get; set; }

//补充信息-色域
public string? VideoRange { get; set; }
//补充信息-特征
Expand Down Expand Up @@ -74,19 +77,19 @@ public string ToShortString()
if (MediaType == Enum.MediaType.AUDIO)
{
prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")}";
var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {Role}";
returnStr = d.EscapeMarkup();
}
else if (MediaType == Enum.MediaType.SUBTITLES)
{
prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
var d = $"{GroupId} | {Language} | {Name} | {Codecs}";
var d = $"{GroupId} | {Language} | {Name} | {Codecs} | {Role}";
returnStr = d.EscapeMarkup();
}
else
{
prefixStr = $"[aqua]Vid[/] {encStr}";
var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange}";
var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange} | {Role}";
returnStr = d.EscapeMarkup();
}

Expand All @@ -108,19 +111,19 @@ public string ToShortShortString()
if (MediaType == Enum.MediaType.AUDIO)
{
prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
var d = $"{(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Language} | {(Channels != null ? Channels + "CH" : "")}";
var d = $"{(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {Role}";
returnStr = d.EscapeMarkup();
}
else if (MediaType == Enum.MediaType.SUBTITLES)
{
prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
var d = $"{Language} | {Name} | {Codecs}";
var d = $"{Language} | {Name} | {Codecs} | {Role}";
returnStr = d.EscapeMarkup();
}
else
{
prefixStr = $"[aqua]Vid[/] {encStr}";
var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {FrameRate} | {VideoRange}";
var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {FrameRate} | {VideoRange} | {Role}";
returnStr = d.EscapeMarkup();
}

Expand Down Expand Up @@ -150,19 +153,19 @@ public override string ToString()
if (MediaType == Enum.MediaType.AUDIO)
{
prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {segmentsCountStr}";
var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {segmentsCountStr} | {Role}";
returnStr = d.EscapeMarkup();
}
else if (MediaType == Enum.MediaType.SUBTITLES)
{
prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
var d = $"{GroupId} | {Language} | {Name} | {Codecs} | {Characteristics} | {segmentsCountStr}";
var d = $"{GroupId} | {Language} | {Name} | {Codecs} | {Characteristics} | {segmentsCountStr} | {Role}";
returnStr = d.EscapeMarkup();
}
else
{
prefixStr = $"[aqua]Vid[/] {encStr}";
var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange} | {segmentsCountStr}";
var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange} | {segmentsCountStr} | {Role}";
returnStr = d.EscapeMarkup();
}

Expand Down
15 changes: 15 additions & 0 deletions src/N_m3u8DL-RE.Common/Enum/RoleType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace N_m3u8DL_RE.Common.Enum
{
public enum RoleType
{
Subtitle = 0,
Main = 1,
Alternate = 2,
Supplementary = 3,
Commentary = 4,
Dub = 5,
Description = 6,
Sign = 7,
Metadata = 8,
}
}
24 changes: 15 additions & 9 deletions src/N_m3u8DL-RE.Common/Resource/StaticText.cs
Original file line number Diff line number Diff line change
Expand Up @@ -459,39 +459,42 @@ internal class StaticText
zhCN: "通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
"id=REGEX:lang=REGEX:name=REGEX:codec=REGEX:res=REGEX:frame=REGEX\r\n" +
"segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
"plistDurMin=hms:plistDurMax=hms:for=FOR\r\n\r\n" +
"plistDurMin=hms:plistDurMax=hms:role=string:for=FOR\r\n\r\n" +
"* for=FOR: 选择方式. best[number], worst[number], all (默认: best)\r\n\r\n" +
"例如: \r\n" +
"# 选择最佳视频\r\n" +
"-sv best\r\n" +
"# 选择4K+HEVC视频\r\n" +
"-sv res=\"3840*\":codec=hvc1:for=best\r\n" +
"# 选择长度大于1小时20分钟30秒的视频\r\n" +
"-sv plistDurMin=\"1h20m30s\":for=best\r\n",
"-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
"-sv role=\"main\":for:best\r\n",
zhTW: "通過正則表達式選擇符合要求的影片軌. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
"id=REGEX:lang=REGEX:name=REGEX:codec=REGEX:res=REGEX:frame=REGEX\r\n" +
"segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
"plistDurMin=hms:plistDurMax=hms:for=FOR\r\n\r\n" +
"plistDurMin=hms:plistDurMax=hms:role=string:for=FOR\r\n\r\n" +
"* for=FOR: 選擇方式. best[number], worst[number], all (默認: best)\r\n\r\n" +
"例如: \r\n" +
"# 選擇最佳影片\r\n" +
"-sv best\r\n" +
"# 選擇4K+HEVC影片\r\n" +
"-sv res=\"3840*\":codec=hvc1:for=best\r\n" +
"# 選擇長度大於1小時20分鐘30秒的影片\r\n" +
"-sv plistDurMin=\"1h20m30s\":for=best\r\n",
"-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
"-sv role=\"main\":for:best\r\n",
enUS: "Select video streams by regular expressions. OPTIONS is a colon separated list of:\r\n\r\n" +
"id=REGEX:lang=REGEX:name=REGEX:codec=REGEX:res=REGEX:frame=REGEX\r\n" +
"segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
"plistDurMin=hms:plistDurMax=hms:for=FOR\r\n\r\n" +
"plistDurMin=hms:plistDurMax=hms:role=string:for=FOR\r\n\r\n" +
"* for=FOR: Select type. best[number], worst[number], all (Default: best)\r\n\r\n" +
"Examples: \r\n" +
"# select best video\r\n" +
"-sv best\r\n" +
"# select 4K+HEVC video\r\n" +
"-sv res=\"3840*\":codec=hvc1:for=best\r\n" +
"# Select best video with duration longer than 1 hour 20 minutes 30 seconds\r\n" +
"-sv plistDurMin=\"1h20m30s\":for=best\r\n"
"-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
"-sv role=\"main\":for:best\r\n"
),
["cmd_selectAudio"] = new TextContainer
(
Expand All @@ -514,23 +517,26 @@ internal class StaticText
"# 选择最佳英语音轨\r\n" +
"-sa lang=en:for=best\r\n" +
"# 选择最佳的2条英语(或日语)音轨\r\n" +
"-sa lang=\"ja|en\":for=best2\r\n",
"-sa lang=\"ja|en\":for=best2\r\n" +
"-sa role=\"main\":for:best\r\n",
zhTW: "通過正則表達式選擇符合要求的音軌. 參考 --select-video\r\n\r\n" +
"例如: \r\n" +
"# 選擇所有音訊\r\n" +
"-sa all\r\n" +
"# 選擇最佳英語音軌\r\n" +
"-sa lang=en:for=best\r\n" +
"# 選擇最佳的2條英語(或日語)音軌\r\n" +
"-sa lang=\"ja|en\":for=best2\r\n",
"-sa lang=\"ja|en\":for=best2\r\n" +
"-sa role=\"main\":for:best\r\n",
enUS: "Select audio streams by regular expressions. ref --select-video\r\n\r\n" +
"Examples: \r\n" +
"# select all\r\n" +
"-sa all\r\n" +
"# select best eng audio\r\n" +
"-sa lang=en:for=best\r\n" +
"# select best 2, and language is ja or en\r\n" +
"-sa lang=\"ja|en\":for=best2\r\n"
"-sa lang=\"ja|en\":for=best2\r\n" +
"-sa role=\"main\":for:best\r\n"
),
["cmd_selectSubtitle"] = new TextContainer
(
Expand Down
23 changes: 15 additions & 8 deletions src/N_m3u8DL-RE.Parser/Extractor/DASHExtractor2.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using N_m3u8DL_RE.Common.Entity;
using N_m3u8DL_RE.Common.Entity;
using N_m3u8DL_RE.Common.Enum;
using N_m3u8DL_RE.Common.Util;
using N_m3u8DL_RE.Parser.Config;
Expand Down Expand Up @@ -189,10 +189,17 @@ public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
if (role != null)
{
var v = role.Attribute("value")?.Value;
if (v == "subtitle")
streamSpec.MediaType = MediaType.SUBTITLES;
if (mType != null && mType.Contains("ttml"))
streamSpec.Extension = "ttml";
if (Enum.TryParse(v, true, out RoleType roleType))
{
streamSpec.Role = roleType;

if (roleType == RoleType.Subtitle)
{
streamSpec.MediaType = MediaType.SUBTITLES;
if (mType != null && mType.Contains("ttml"))
streamSpec.Extension = "ttml";
}
}
}
streamSpec.Playlist.IsLive = isLive;
//设置刷新间隔 timeShiftBufferDepth / 2
Expand All @@ -213,7 +220,7 @@ public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
{
streamSpec.PublishTime = DateTime.Parse(publishTime);
}


//第一种形式 SegmentBase
var segmentBaseElement = representation.Elements().Where(e => e.Name.LocalName == "SegmentBase").FirstOrDefault();
Expand Down Expand Up @@ -439,7 +446,7 @@ public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
}

//判断加密情况
if (adaptationSet.Elements().Concat(representation.Elements()).Any(e => e.Name.LocalName == "ContentProtection"))
if (adaptationSet.Elements().Concat(representation.Elements()).Any(e => e.Name.LocalName == "ContentProtection"))
{
if (streamSpec.Playlist.MediaInit != null)
{
Expand All @@ -453,7 +460,7 @@ public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)

//处理同一ID分散在不同Period的情况
var _index = streamList.FindIndex(_f => _f.PeriodId != streamSpec.PeriodId && _f.GroupId == streamSpec.GroupId && _f.Resolution == streamSpec.Resolution && _f.MediaType == streamSpec.MediaType);
if (_index > -1)
if (_index > -1)
{
if (isLive)
{
Expand Down
14 changes: 9 additions & 5 deletions src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using N_m3u8DL_RE.Common.Enum;
using N_m3u8DL_RE.Common.Enum;
using N_m3u8DL_RE.Common.Log;
using N_m3u8DL_RE.Common.Resource;
using N_m3u8DL_RE.Common.Util;
Expand Down Expand Up @@ -99,7 +99,7 @@ internal partial class CommandInvoker
private readonly static Option<StreamFilter?> VideoFilter = new(new string[] { "-sv", "--select-video" }, description: ResString.cmd_selectVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
private readonly static Option<StreamFilter?> AudioFilter = new(new string[] { "-sa", "--select-audio" }, description: ResString.cmd_selectAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
private readonly static Option<StreamFilter?> SubtitleFilter = new(new string[] { "-ss", "--select-subtitle" }, description: ResString.cmd_selectSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };

private readonly static Option<StreamFilter?> DropVideoFilter = new(new string[] { "-dv", "--drop-video" }, description: ResString.cmd_dropVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
private readonly static Option<StreamFilter?> DropAudioFilter = new(new string[] { "-da", "--drop-audio" }, description: ResString.cmd_dropAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
private readonly static Option<StreamFilter?> DropSubtitleFilter = new(new string[] { "-ds", "--drop-subtitle" }, description: ResString.cmd_dropSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
Expand Down Expand Up @@ -178,7 +178,7 @@ internal partial class CommandInvoker
return null;
}
}

/// <summary>
/// 解析用户代理
/// </summary>
Expand Down Expand Up @@ -367,6 +367,10 @@ internal partial class CommandInvoker
if (!string.IsNullOrEmpty(plistDurMax))
streamFilter.PlaylistMaxDur = OtherUtil.ParseSeconds(plistDurMax);

var role = p.GetValue("role");
if (System.Enum.TryParse(role, true, out RoleType roleType))
streamFilter.Role = roleType;

return streamFilter;
}

Expand Down Expand Up @@ -553,7 +557,7 @@ protected override MyOption GetBoundValue(BindingContext bindingContext)

//混流设置
var muxAfterDoneValue = bindingContext.ParseResult.GetValueForOption(MuxAfterDone);
if (muxAfterDoneValue != null)
if (muxAfterDoneValue != null)
{
option.MuxAfterDone = true;
option.MuxOptions = muxAfterDoneValue;
Expand All @@ -571,7 +575,7 @@ public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> act
{
var argList = new List<string>(args);
var index = -1;
if ((index = argList.IndexOf("--morehelp")) >= 0 && argList.Count > index + 1)
if ((index = argList.IndexOf("--morehelp")) >= 0 && argList.Count > index + 1)
{
var option = argList[index + 1];
var msg = option switch
Expand Down
2 changes: 2 additions & 0 deletions src/N_m3u8DL-RE/Entity/StreamFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class StreamFilter
public long? SegmentsMaxCount { get; set; }
public double? PlaylistMinDur { get; set; }
public double? PlaylistMaxDur { get; set; }
public RoleType? Role { get; set; }

public string For { get; set; } = "best";

Expand All @@ -43,6 +44,7 @@ public class StreamFilter
if (SegmentsMaxCount != null) sb.Append($"SegmentsMaxCount: {SegmentsMaxCount} ");
if (PlaylistMinDur != null) sb.Append($"PlaylistMinDur: {PlaylistMinDur} ");
if (PlaylistMaxDur != null) sb.Append($"PlaylistMaxDur: {PlaylistMaxDur} ");
if (Role.HasValue) sb.Append($"Role: {Role} ");

return sb.ToString() + $"For: {For}";
}
Expand Down
2 changes: 2 additions & 0 deletions src/N_m3u8DL-RE/Util/FilterUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public static List<StreamSpec> DoFilterKeep(IEnumerable<StreamSpec> lists, Strea
inputs = inputs.Where(i => i.Playlist?.TotalDuration > filter.PlaylistMinDur);
if (filter.PlaylistMaxDur != null)
inputs = inputs.Where(i => i.Playlist?.TotalDuration < filter.PlaylistMaxDur);
if (filter.Role.HasValue)
inputs = inputs.Where(i => i.Role == filter.Role);

var bestNumberStr = filter.For.Replace("best", "");
var worstNumberStr = filter.For.Replace("worst", "");
Expand Down

0 comments on commit 75b270b

Please sign in to comment.