From 508f39b649a99c42f97ce86976c9ef7184048d43 Mon Sep 17 00:00:00 2001 From: AdiEcho <30563671+AdiEcho@users.noreply.github.com> Date: Thu, 25 Jul 2024 01:05:33 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=9ADRM=E6=96=87?= =?UTF-8?q?=E4=BB=B6KID=E8=AF=BB=E5=8F=96=E5=BC=82=E5=B8=B8=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#422)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Fix] fix reading the kid in a multi-DRM file * [Fix] fix decrypting multi-drm files * [Fix] fix glibc error in building * [Feat] 删除ReadInit方法 * [Fix] 修复ReadMe和more-help中显示参数不准确的问题 --- .github/workflows/build_latest.yml | 1 + README.md | 4 ++-- src/N_m3u8DL-RE.Common/Resource/StaticText.cs | 12 +++++----- src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs | 10 +++++++-- .../DownloadManager/SimpleDownloadManager.cs | 22 +++++++++++-------- .../SimpleLiveRecordManager2.cs | 4 ++-- src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs | 16 +++++++++----- 7 files changed, 43 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build_latest.yml b/.github/workflows/build_latest.yml index b5e4910..d67398c 100644 --- a/.github/workflows/build_latest.yml +++ b/.github/workflows/build_latest.yml @@ -20,6 +20,7 @@ on: env: DOTNET_SDK_VERSION: "8.0.*" + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true jobs: build-win-x64-arm64: diff --git a/README.md b/README.md index 8261edd..dddc509 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ More Help: 通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数: -id=REGEX:lang=REGEX:name=REGEX:codec=REGEX:res=REGEX:frame=REGEX +id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX plistDurMin=hms:plistDurMax=hms:for=FOR @@ -151,7 +151,7 @@ plistDurMin=hms:plistDurMax=hms:for=FOR # 选择最佳视频 -sv best # 选择4K+HEVC视频 --sv res="3840*":codec=hvc1:for=best +-sv res="3840*":codecs=hvc1:for=best # 选择长度大于1小时20分钟30秒的视频 -sv plistDurMin="1h20m30s":for=best ``` diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index 5a8e3a8..aaa4615 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -475,7 +475,7 @@ namespace N_m3u8DL_RE.Common.Resource ["cmd_selectVideo_more"] = new TextContainer ( zhCN: "通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数:\r\n\r\n" + - "id=REGEX:lang=REGEX:name=REGEX:codec=REGEX:res=REGEX:frame=REGEX\r\n" + + "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\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" + @@ -483,12 +483,12 @@ namespace N_m3u8DL_RE.Common.Resource "# 选择最佳视频\r\n" + "-sv best\r\n" + "# 选择4K+HEVC视频\r\n" + - "-sv res=\"3840*\":codec=hvc1:for=best\r\n" + + "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" + "# 选择长度大于1小时20分钟30秒的视频\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" + + "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\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" + @@ -496,12 +496,12 @@ namespace N_m3u8DL_RE.Common.Resource "# 選擇最佳影片\r\n" + "-sv best\r\n" + "# 選擇4K+HEVC影片\r\n" + - "-sv res=\"3840*\":codec=hvc1:for=best\r\n" + + "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" + "# 選擇長度大於1小時20分鐘30秒的影片\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" + + "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\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" + @@ -509,7 +509,7 @@ namespace N_m3u8DL_RE.Common.Resource "# select best video\r\n" + "-sv best\r\n" + "# select 4K+HEVC video\r\n" + - "-sv res=\"3840*\":codec=hvc1:for=best\r\n" + + "-sv res=\"3840*\":codecs=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 role=\"main\":for:best\r\n" diff --git a/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs b/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs index 0482a0f..3e5ea62 100644 --- a/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs +++ b/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs @@ -8,6 +8,7 @@ namespace Mp4SubtitleParser public string? PSSH; public string? KID; public string? Scheme; + public bool isMultiDRM; } public class MP4InitUtil @@ -19,7 +20,6 @@ namespace Mp4SubtitleParser { var info = new ParsedMP4Info(); - //parse init new MP4Parser() .Box("moov", MP4Parser.Children) @@ -36,7 +36,13 @@ namespace Mp4SubtitleParser if (SYSTEM_ID_WIDEVINE.SequenceEqual(systemId)) { var dataSize = box.Reader.ReadUInt32(); - info.PSSH = Convert.ToBase64String(box.Reader.ReadBytes((int)dataSize)); + var psshData = box.Reader.ReadBytes((int)dataSize); + info.PSSH = Convert.ToBase64String(psshData); + if (info.KID == "00000000000000000000000000000000") + { + info.KID = HexUtil.BytesToHex(psshData[2..18]).ToLower(); + info.isMultiDRM = true; + } } }) .FullBox("encv", MP4Parser.AllData(data => ReadBox(data, info))) diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs index 8ad42f4..5893cc5 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs @@ -76,7 +76,6 @@ namespace N_m3u8DL_RE.DownloadManager } } - private async Task DownloadStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer) { speedContainer.ResetVars(); @@ -115,6 +114,7 @@ namespace N_m3u8DL_RE.DownloadManager var mp4InitFile = ""; var currentKID = ""; var readInfo = false; //是否读取过 + var mp4Info = new ParsedMP4Info(); //用户自定义范围导致被跳过的时长 计算字幕偏移使用 var skippedDur = streamSpec.SkippedDuration ?? 0d; @@ -167,7 +167,8 @@ namespace N_m3u8DL_RE.DownloadManager //读取mp4信息 if (result != null && result.Success) { - currentKID = MP4DecryptUtil.ReadInit(result.ActualFilePath); + mp4Info = MP4DecryptUtil.GetMP4Info(result.ActualFilePath); + currentKID = mp4Info.KID; // try shaka packager, which can handle WebM if (string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.UseShakaPackager) { currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, mp4decrypt); @@ -179,7 +180,7 @@ namespace N_m3u8DL_RE.DownloadManager { var enc = result.ActualFilePath; var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc)); - var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID); + var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM); if (dResult) { FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec; @@ -238,7 +239,7 @@ namespace N_m3u8DL_RE.DownloadManager //读取init信息 if (string.IsNullOrEmpty(currentKID)) { - currentKID = MP4DecryptUtil.ReadInit(result.ActualFilePath); + currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID; } // try shaka packager, which can handle WebM if (string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.UseShakaPackager) { @@ -251,7 +252,8 @@ namespace N_m3u8DL_RE.DownloadManager { var enc = result.ActualFilePath; var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc)); - var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile); + mp4Info = MP4DecryptUtil.GetMP4Info(enc); + var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM); if (dResult) { File.Delete(enc); @@ -288,7 +290,8 @@ namespace N_m3u8DL_RE.DownloadManager { var enc = result.ActualFilePath; var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc)); - var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile); + mp4Info = MP4DecryptUtil.GetMP4Info(enc); + var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM); if (dResult) { File.Delete(enc); @@ -584,7 +587,7 @@ namespace N_m3u8DL_RE.DownloadManager //重新读取init信息 if (mergeSuccess && totalCount >= 1 && string.IsNullOrEmpty(currentKID) && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method != Common.Enum.EncryptMethod.NONE) { - currentKID = MP4DecryptUtil.ReadInit(output); + currentKID = MP4DecryptUtil.GetMP4Info(output).KID; // try shaka packager, which can handle WebM if (string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.UseShakaPackager) { currentKID = MP4DecryptUtil.ReadInitShaka(output, mp4decrypt); @@ -594,12 +597,13 @@ namespace N_m3u8DL_RE.DownloadManager } //调用mp4decrypt解密 - if (mergeSuccess && File.Exists(output) && !string.IsNullOrEmpty(currentKID) && !DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0) + if (mergeSuccess && File.Exists(output) && !string.IsNullOrEmpty(currentKID) && !DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0) { var enc = output; var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc)); + mp4Info = MP4DecryptUtil.GetMP4Info(enc); Logger.InfoMarkUp($"[grey]Decrypting...[/]"); - var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID); + var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM); if (result) { File.Delete(enc); diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs index 4ab0b66..92674dc 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs @@ -212,7 +212,7 @@ namespace N_m3u8DL_RE.DownloadManager //读取mp4信息 if (result != null && result.Success) { - currentKID = MP4DecryptUtil.ReadInit(result.ActualFilePath); + currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID; //从文件读取KEY await SearchKeyAsync(currentKID); //实时解密 @@ -290,7 +290,7 @@ namespace N_m3u8DL_RE.DownloadManager //读取init信息 if (string.IsNullOrEmpty(currentKID)) { - currentKID = MP4DecryptUtil.ReadInit(result.ActualFilePath); + currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID; } //从文件读取KEY await SearchKeyAsync(currentKID); diff --git a/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs b/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs index 41f9be6..a04291e 100644 --- a/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs +++ b/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs @@ -10,12 +10,18 @@ namespace N_m3u8DL_RE.Util internal class MP4DecryptUtil { private static string ZeroKid = "00000000000000000000000000000000"; - public static async Task DecryptAsync(bool shakaPackager, string bin, string[]? keys, string source, string dest, string? kid, string init = "") + public static async Task DecryptAsync(bool shakaPackager, string bin, string[]? keys, string source, string dest, string? kid, string init = "", bool isMultiDRM=false) { if (keys == null || keys.Length == 0) return false; string? keyPair = null; string? trackId = null; + + if (isMultiDRM) + { + trackId = "1"; + } + if (!string.IsNullOrEmpty(kid)) { var test = keys.Where(k => k.StartsWith(kid)); @@ -127,22 +133,22 @@ namespace N_m3u8DL_RE.Util return null; } - public static string? ReadInit(byte[] data) + public static ParsedMP4Info GetMP4Info(byte[] data) { var info = MP4InitUtil.ReadInit(data); if (info.Scheme != null) Logger.WarnMarkUp($"[grey]Type: {info.Scheme}[/]"); if (info.PSSH != null) Logger.WarnMarkUp($"[grey]PSSH(WV): {info.PSSH}[/]"); if (info.KID != null) Logger.WarnMarkUp($"[grey]KID: {info.KID}[/]"); - return info.KID; + return info; } - public static string? ReadInit(string output) + public static ParsedMP4Info GetMP4Info(string output) { using (var fs = File.OpenRead(output)) { var header = new byte[1 * 1024 * 1024]; //1MB fs.Read(header); - return ReadInit(header); + return GetMP4Info(header); } } From 6cc09fa373d1330cf1796349c68f7b1c0894641b Mon Sep 17 00:00:00 2001 From: Shingo Date: Wed, 7 Aug 2024 22:47:03 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=A0=81=E7=8E=87?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E5=99=A8=E4=BB=A5=E5=8F=8A=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?2=E5=A4=84bug=20(#427)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prevent the ffmpeg-binary-path option from being accidentally overridden by the mux-after-done option. * Add bandwidth filter * Add bandwidth filter --- src/N_m3u8DL-RE.Common/Resource/StaticText.cs | 24 ++++++++++++------- src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs | 10 +++++++- src/N_m3u8DL-RE/Entity/StreamFilter.cs | 4 ++++ src/N_m3u8DL-RE/Util/FilterUtil.cs | 4 ++++ 4 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index aaa4615..1265525 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -477,7 +477,7 @@ namespace N_m3u8DL_RE.Common.Resource zhCN: "通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数:\r\n\r\n" + "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" + - "plistDurMin=hms:plistDurMax=hms:role=string:for=FOR\r\n\r\n" + + "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" + "* for=FOR: 选择方式. best[number], worst[number], all (默认: best)\r\n\r\n" + "例如: \r\n" + "# 选择最佳视频\r\n" + @@ -486,11 +486,13 @@ namespace N_m3u8DL_RE.Common.Resource "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" + "# 选择长度大于1小时20分钟30秒的视频\r\n" + "-sv plistDurMin=\"1h20m30s\":for=best\r\n" + - "-sv role=\"main\":for:best\r\n", + "-sv role=\"main\":for=best\r\n" + + "# 选择码率在800Kbps至1Mbps之间的视频\r\n" + + "-sv bwMin=800:bwMax=1000\r\n", zhTW: "通過正則表達式選擇符合要求的影片軌. 你能夠以:分隔形式指定如下參數:\r\n\r\n" + "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" + - "plistDurMin=hms:plistDurMax=hms:role=string:for=FOR\r\n\r\n" + + "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" + "* for=FOR: 選擇方式. best[number], worst[number], all (默認: best)\r\n\r\n" + "例如: \r\n" + "# 選擇最佳影片\r\n" + @@ -499,11 +501,13 @@ namespace N_m3u8DL_RE.Common.Resource "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" + "# 選擇長度大於1小時20分鐘30秒的影片\r\n" + "-sv plistDurMin=\"1h20m30s\":for=best\r\n" + - "-sv role=\"main\":for:best\r\n", + "-sv role=\"main\":for=best\r\n" + + "# 選擇碼率在800Kbps至1Mbps之間的影片\r\n" + + "-sv bwMin=800:bwMax=1000\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:codecs=REGEX:res=REGEX:frame=REGEX\r\n" + "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" + - "plistDurMin=hms:plistDurMax=hms:role=string:for=FOR\r\n\r\n" + + "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int: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" + @@ -512,7 +516,9 @@ namespace N_m3u8DL_RE.Common.Resource "-sv res=\"3840*\":codecs=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 role=\"main\":for:best\r\n" + "-sv role=\"main\":for=best\r\n" + + "# Select video with bandwidth between 800Kbps and 1Mbps\r\n" + + "-sv bwMin=800:bwMax=1000\r\n" ), ["cmd_selectAudio"] = new TextContainer ( @@ -536,7 +542,7 @@ namespace N_m3u8DL_RE.Common.Resource "-sa lang=en:for=best\r\n" + "# 选择最佳的2条英语(或日语)音轨\r\n" + "-sa lang=\"ja|en\":for=best2\r\n" + - "-sa role=\"main\":for:best\r\n", + "-sa role=\"main\":for=best\r\n", zhTW: "通過正則表達式選擇符合要求的音軌. 參考 --select-video\r\n\r\n" + "例如: \r\n" + "# 選擇所有音訊\r\n" + @@ -545,7 +551,7 @@ namespace N_m3u8DL_RE.Common.Resource "-sa lang=en:for=best\r\n" + "# 選擇最佳的2條英語(或日語)音軌\r\n" + "-sa lang=\"ja|en\":for=best2\r\n" + - "-sa role=\"main\":for:best\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" + @@ -554,7 +560,7 @@ namespace N_m3u8DL_RE.Common.Resource "-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 role=\"main\":for:best\r\n" + "-sa role=\"main\":for=best\r\n" ), ["cmd_selectSubtitle"] = new TextContainer ( diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index ceb9777..e9311c4 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -369,6 +369,14 @@ namespace N_m3u8DL_RE.CommandLine if (!string.IsNullOrEmpty(plistDurMax)) streamFilter.PlaylistMaxDur = OtherUtil.ParseSeconds(plistDurMax); + var bwMin = p.GetValue("bwMin"); + if (!string.IsNullOrEmpty(bwMin)) + streamFilter.BandwidthMin = int.Parse(bwMin) * 1000; + + var bwMax = p.GetValue("bwMax"); + if (!string.IsNullOrEmpty(bwMax)) + streamFilter.BandwidthMax = int.Parse(bwMax) * 1000; + var role = p.GetValue("role"); if (System.Enum.TryParse(role, true, out RoleType roleType)) streamFilter.Role = roleType; @@ -566,7 +574,7 @@ namespace N_m3u8DL_RE.CommandLine option.MuxAfterDone = true; option.MuxOptions = muxAfterDoneValue; if (muxAfterDoneValue.UseMkvmerge) option.MkvmergeBinaryPath = muxAfterDoneValue.BinPath; - else option.FFmpegBinaryPath = muxAfterDoneValue.BinPath; + else option.FFmpegBinaryPath ??= muxAfterDoneValue.BinPath; } diff --git a/src/N_m3u8DL-RE/Entity/StreamFilter.cs b/src/N_m3u8DL-RE/Entity/StreamFilter.cs index 41191b3..d76d862 100644 --- a/src/N_m3u8DL-RE/Entity/StreamFilter.cs +++ b/src/N_m3u8DL-RE/Entity/StreamFilter.cs @@ -23,6 +23,8 @@ namespace N_m3u8DL_RE.Entity public long? SegmentsMaxCount { get; set; } public double? PlaylistMinDur { get; set; } public double? PlaylistMaxDur { get; set; } + public int? BandwidthMin { get; set; } + public int? BandwidthMax { get; set; } public RoleType? Role { get; set; } public string For { get; set; } = "best"; @@ -44,6 +46,8 @@ namespace N_m3u8DL_RE.Entity if (SegmentsMaxCount != null) sb.Append($"SegmentsMaxCount: {SegmentsMaxCount} "); if (PlaylistMinDur != null) sb.Append($"PlaylistMinDur: {PlaylistMinDur} "); if (PlaylistMaxDur != null) sb.Append($"PlaylistMaxDur: {PlaylistMaxDur} "); + if (BandwidthMin != null) sb.Append($"{nameof(BandwidthMin)}: {BandwidthMin} "); + if (BandwidthMax != null) sb.Append($"{nameof(BandwidthMax)}: {BandwidthMax} "); if (Role.HasValue) sb.Append($"Role: {Role} "); return sb.ToString() + $"For: {For}"; diff --git a/src/N_m3u8DL-RE/Util/FilterUtil.cs b/src/N_m3u8DL-RE/Util/FilterUtil.cs index 80a1a6b..ec06e64 100644 --- a/src/N_m3u8DL-RE/Util/FilterUtil.cs +++ b/src/N_m3u8DL-RE/Util/FilterUtil.cs @@ -46,6 +46,10 @@ namespace N_m3u8DL_RE.Util inputs = inputs.Where(i => i.Playlist?.TotalDuration > filter.PlaylistMinDur); if (filter.PlaylistMaxDur != null) inputs = inputs.Where(i => i.Playlist?.TotalDuration < filter.PlaylistMaxDur); + if (filter.BandwidthMin != null) + inputs = inputs.Where(i => i.Bandwidth >= filter.BandwidthMin); + if (filter.BandwidthMax != null) + inputs = inputs.Where(i => i.Bandwidth <= filter.BandwidthMax); if (filter.Role.HasValue) inputs = inputs.Where(i => i.Role == filter.Role); From 584d3c892b5875b6bdac9172cc8ac16274d61bf0 Mon Sep 17 00:00:00 2001 From: nilaoda Date: Wed, 28 Aug 2024 22:33:33 +0800 Subject: [PATCH 3/3] 0.2.1 sync mainline --- README.md | 14 ++++++++++---- src/N_m3u8DL-RE/N_m3u8DL-RE.csproj | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dddc509..ba26825 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ Arch Linux 可以从 AUR 获取:[n-m3u8dl-re-bin](https://aur.archlinux.org/packages/n-m3u8dl-re-bin)、[n-m3u8dl-re-git](https://aur.archlinux.org/packages/n-m3u8dl-re-git) ```bash -# Arch Linux 及其衍生版安装 N_m3u8DL-RE 发行版 +# Arch Linux 及其衍生版安装 N_m3u8DL-RE 发行版 (该源非本人维护) yay -Syu n-m3u8dl-re-bin -# Arch Linux 及其衍生版安装 N_m3u8DL-RE 开发版 +# Arch Linux 及其衍生版安装 N_m3u8DL-RE 开发版 (该源非本人维护) yay -Syu n-m3u8dl-re-git ``` --- @@ -25,7 +25,7 @@ yay -Syu n-m3u8dl-re-git # 命令行参数 ``` Description: - N_m3u8DL-RE (Beta version) 20230628 + N_m3u8DL-RE (Beta version) 20240630 Usage: N_m3u8DL-RE [options] @@ -38,13 +38,16 @@ Options: --save-dir 设置输出目录 --save-name 设置保存文件名 --base-url 设置BaseURL - --thread-count 设置下载线程数 [default: 16] + --thread-count 设置下载线程数 [default: 本机CPU线程数] --download-retry-count 每个分片下载异常时的重试次数 [default: 3] + --force-ansi-console 强制认定终端为支持ANSI且可交互的终端 + --no-ansi-color 去除ANSI颜色 --auto-select 自动选择所有类型的最佳轨道 [default: False] --skip-merge 跳过合并分片 [default: False] --skip-download 跳过下载 [default: False] --check-segments-count 检测实际下载的分片数量和预期数量是否匹配 [default: True] --binary-merge 二进制合并 [default: False] + --use-ffmpeg-concat-demuxer 使用 ffmpeg 合并时,使用 concat 分离器而非 concat 协议 [default: False] --del-after-done 完成后删除临时文件 [default: True] --no-date-info 混流时不写入日期信息 [default: False] --no-log 关闭日志文件输出 [default: False] @@ -66,6 +69,7 @@ Options: --decryption-binary-path MP4解密所用工具的全路径, 例如 C:\Tools\mp4decrypt.exe --use-shaka-packager 解密时使用shaka-packager替代mp4decrypt [default: False] --mp4-real-time-decryption 实时解密MP4分片 [default: False] + -R, --max-speed 设置限速,单位支持 Mbps 或 Kbps,如:15M 100K -M, --mux-after-done 所有工作完成时尝试混流分离的音视频. 输入 "--morehelp mux-after-done" 以查看详细信息 --custom-hls-method 指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN) --custom-hls-key 指定HLS解密KEY. 可以是文件, HEX或Base64 @@ -81,6 +85,7 @@ Options: --live-fix-vtt-by-audio 通过读取音频文件的起始时间修正VTT字幕 [default: False] --live-record-limit 录制直播时的录制时长限制 --live-wait-time 手动设置直播列表刷新间隔 + --live-take-count 手动设置录制直播时首次获取分片的数量 [default: 16] --mux-import 混流时引入外部媒体文件. 输入 "--morehelp mux-import" 以查看详细信息 -sv, --select-video 通过正则表达式选择符合要求的视频流. 输入 "--morehelp select-video" 以查看详细信息 -sa, --select-audio 通过正则表达式选择符合要求的音频流. 输入 "--morehelp select-audio" 以查看详细信息 @@ -88,6 +93,7 @@ Options: -dv, --drop-video 通过正则表达式去除符合要求的视频流. -da, --drop-audio 通过正则表达式去除符合要求的音频流. -ds, --drop-subtitle 通过正则表达式去除符合要求的字幕流. + --ad-keyword 设置广告分片的URL关键字(正则表达式) --morehelp