From c99ef71f1046961fa7b77388ba37bcaf30526465 Mon Sep 17 00:00:00 2001 From: Ayou <31991515+Ayouuuu@users.noreply.github.com> Date: Wed, 13 Dec 2023 19:06:25 +0800 Subject: [PATCH 1/5] feat: ffmpeg custom metadata --- BBDown/BBDownMuxer.cs | 34 +++++++++++++++++++++++++++++++++- BBDown/CommandLineInvoker.cs | 5 ++++- BBDown/MyOption.cs | 1 + BBDown/Program.cs | 4 ++-- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/BBDown/BBDownMuxer.cs b/BBDown/BBDownMuxer.cs index a47d7c083..9d0b292da 100644 --- a/BBDown/BBDownMuxer.cs +++ b/BBDown/BBDownMuxer.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using static BBDown.Core.Entity.Entity; using static BBDown.BBDownUtil; using static BBDown.Core.Util.SubUtil; @@ -95,7 +96,7 @@ private static int MuxByMp4box(string videoPath, string audioPath, string outPat return RunExe(MP4BOX, arguments, MP4BOX != "mp4box"); } - public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List? subs = null, bool audioOnly = false, bool videoOnly = false, List? points = null, long pubTime = 0, bool simplyMux = false) + public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List? subs = null, bool audioOnly = false, bool videoOnly = false, List? points = null, long pubTime = 0, bool simplyMux = false, MyOption myOption = null) { if (audioOnly && audioPath != "") videoPath = ""; @@ -185,6 +186,22 @@ public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List if (episodeId != "") argsBuilder.Append($"-metadata album=\"{title}\" "); if (pubTime != 0) argsBuilder.Append($"-metadata creation_time=\"{(DateTimeOffset.FromUnixTimeSeconds(pubTime).ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ"))}\" "); } + if (myOption.FfmpegMetadata != "") { + Regex regex = new Regex(@"-metadata (\w+)=(.*?)(?=-metadata|$)", RegexOptions.Singleline); + MatchCollection matches = regex.Matches(argsBuilder.ToString()); + foreach (Match match in matches) + { + if (match.Success) + { + string key = match.Groups[1].Value; + string value = GetValueForReplacement(key, myOption.FfmpegMetadata); + if (!string.IsNullOrEmpty(value)) + { + argsBuilder.Replace($"{key}={match.Groups[2].Value}", $"{key}={value} "); + } + } + } + } argsBuilder.Append("-c copy "); if (audioOnly && audioPath == "") argsBuilder.Append("-vn "); if (subs != null) argsBuilder.Append("-c:s mov_text "); @@ -217,5 +234,20 @@ public static void MergeFLV(string[] files, string outPath) foreach (var s in f) File.Delete(s); } } + + private static string GetValueForReplacement(string key, string input) + { + Regex regex = new Regex($"{key}=([^,]+)"); + Match match = regex.Match(input); + + if (match.Success) + { + return match.Groups[1].Value; + } + else + { + return string.Empty; + } + } } } diff --git a/BBDown/CommandLineInvoker.cs b/BBDown/CommandLineInvoker.cs index 7f0a74afb..efef8835f 100644 --- a/BBDown/CommandLineInvoker.cs +++ b/BBDown/CommandLineInvoker.cs @@ -53,6 +53,7 @@ internal class CommandLineInvoker private readonly static Option UposHost = new(new string[] { "--upos-host" }, "自定义upos服务器"); private readonly static Option ForceReplaceHost = new(new string[] { "--force-replace-host" }, "强制替换下载服务器host(默认开启)"); private readonly static Option SaveArchivesToFile = new(new string[] { "--save-archives-to-file" }, "将下载过的视频记录到本地文件中, 用于后续跳过下载同个视频"); + private readonly static Option FfmpegMetadata = new(new string[] { "--ffmpeg-metadata","-fdata" }, "自定义ffmepg当中的metadata数据: (-fdata title=标题,album=专辑,description=简介)"); private readonly static Option DelayPerPage = new(new string[] { "--delay-per-page" }, "设置下载合集分P之间的下载间隔时间(单位: 秒, 默认无间隔)"); private readonly static Option FilePattern = new(new string[] { "--file-pattern", "-F" }, $"使用内置变量自定义单P存储文件名:\r\n\r\n" + @@ -154,6 +155,7 @@ protected override MyOption GetBoundValue(BindingContext bindingContext) if (bindingContext.ParseResult.HasOption(AddDfnSubfix)) option.AddDfnSubfix = bindingContext.ParseResult.GetValueForOption(AddDfnSubfix)!; if (bindingContext.ParseResult.HasOption(NoPaddingPageNum)) option.NoPaddingPageNum = bindingContext.ParseResult.GetValueForOption(NoPaddingPageNum)!; if (bindingContext.ParseResult.HasOption(BandwithAscending)) option.BandwithAscending = bindingContext.ParseResult.GetValueForOption(BandwithAscending)!; + if (bindingContext.ParseResult.HasOption(FfmpegMetadata)) option.FfmpegMetadata = bindingContext.ParseResult.GetValueForOption(FfmpegMetadata)!; return option; } } @@ -216,7 +218,8 @@ public static RootCommand GetRootCommand(Func action) OnlyAv1, AddDfnSubfix, NoPaddingPageNum, - BandwithAscending + BandwithAscending, + FfmpegMetadata }; rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder()); diff --git a/BBDown/MyOption.cs b/BBDown/MyOption.cs index d581c5865..d0adb89d1 100644 --- a/BBDown/MyOption.cs +++ b/BBDown/MyOption.cs @@ -52,6 +52,7 @@ internal class MyOption public string Mp4boxPath { get; set; } = ""; public string Aria2cPath { get; set; } = ""; public string UposHost { get; set; } = ""; + public string FfmpegMetadata { get; set; } = ""; public string DelayPerPage { get; set; } = "0"; public string Host { get; set; } = "api.bilibili.com"; public string EpHost { get; set; } = "api.bilibili.com"; diff --git a/BBDown/Program.cs b/BBDown/Program.cs index ed331e29d..00aa38708 100644 --- a/BBDown/Program.cs +++ b/BBDown/Program.cs @@ -626,7 +626,7 @@ private static async Task DownloadPageAsync(Page p, MyOption myOption, VInfo vIn (pagesCount > 1 || (bangumi && !vInfo.IsBangumiEnd)) ? p.title : "", File.Exists(coverPath) ? coverPath : "", lang, - subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux); + subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux, myOption); if (code != 0 || !File.Exists(savePath) || new FileInfo(savePath).Length == 0) { LogError("合并失败"); return; @@ -715,7 +715,7 @@ private static async Task DownloadPageAsync(Page p, MyOption myOption, VInfo vIn (pagesCount > 1 || (bangumi && !vInfo.IsBangumiEnd)) ? p.title : "", File.Exists(coverPath) ? coverPath : "", lang, - subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux); + subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux, myOption); if (code != 0 || !File.Exists(savePath) || new FileInfo(savePath).Length == 0) { LogError("合并失败"); return; From c1a1c4899f680c758bafbd43eb76ec59b5eca49a Mon Sep 17 00:00:00 2001 From: Ayou <31991515+Ayouuuu@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:55:20 +0800 Subject: [PATCH 2/5] fix: space error --- BBDown/BBDownMuxer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BBDown/BBDownMuxer.cs b/BBDown/BBDownMuxer.cs index 9d0b292da..7ec9c27d9 100644 --- a/BBDown/BBDownMuxer.cs +++ b/BBDown/BBDownMuxer.cs @@ -197,7 +197,7 @@ public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List string value = GetValueForReplacement(key, myOption.FfmpegMetadata); if (!string.IsNullOrEmpty(value)) { - argsBuilder.Replace($"{key}={match.Groups[2].Value}", $"{key}={value} "); + argsBuilder.Replace($"{key}={match.Groups[2].Value}", $"{key}=\"{value}\" "); } } } From c7773b8e5a07e359fd1a01457f3c785afd2bad75 Mon Sep 17 00:00:00 2001 From: Ayou <31991515+Ayouuuu@users.noreply.github.com> Date: Wed, 13 Dec 2023 23:04:58 +0800 Subject: [PATCH 3/5] Update CommandLineInvoker.cs --- BBDown/CommandLineInvoker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BBDown/CommandLineInvoker.cs b/BBDown/CommandLineInvoker.cs index efef8835f..d201044d1 100644 --- a/BBDown/CommandLineInvoker.cs +++ b/BBDown/CommandLineInvoker.cs @@ -53,7 +53,7 @@ internal class CommandLineInvoker private readonly static Option UposHost = new(new string[] { "--upos-host" }, "自定义upos服务器"); private readonly static Option ForceReplaceHost = new(new string[] { "--force-replace-host" }, "强制替换下载服务器host(默认开启)"); private readonly static Option SaveArchivesToFile = new(new string[] { "--save-archives-to-file" }, "将下载过的视频记录到本地文件中, 用于后续跳过下载同个视频"); - private readonly static Option FfmpegMetadata = new(new string[] { "--ffmpeg-metadata","-fdata" }, "自定义ffmepg当中的metadata数据: (-fdata title=标题,album=专辑,description=简介)"); + private readonly static Option FfmpegMetadata = new(new string[] { "--ffmpeg-metadata","-fdata" }, "自定义ffmepg当中的metadata数据: (-fdata artist=\"作者\",title=\"标题\",album=\"专辑\",description=\"简介\")"); private readonly static Option DelayPerPage = new(new string[] { "--delay-per-page" }, "设置下载合集分P之间的下载间隔时间(单位: 秒, 默认无间隔)"); private readonly static Option FilePattern = new(new string[] { "--file-pattern", "-F" }, $"使用内置变量自定义单P存储文件名:\r\n\r\n" + From 523d5979aaa54a18fc3f94d6968e07c536982d1e Mon Sep 17 00:00:00 2001 From: Ayou <31991515+Ayouuuu@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:07:17 +0800 Subject: [PATCH 4/5] add: argsBuilder Append --- BBDown/BBDownMuxer.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/BBDown/BBDownMuxer.cs b/BBDown/BBDownMuxer.cs index 7ec9c27d9..969b9baca 100644 --- a/BBDown/BBDownMuxer.cs +++ b/BBDown/BBDownMuxer.cs @@ -189,18 +189,31 @@ public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List if (myOption.FfmpegMetadata != "") { Regex regex = new Regex(@"-metadata (\w+)=(.*?)(?=-metadata|$)", RegexOptions.Singleline); MatchCollection matches = regex.Matches(argsBuilder.ToString()); + string metadata = myOption.FfmpegMetadata; foreach (Match match in matches) { if (match.Success) { string key = match.Groups[1].Value; - string value = GetValueForReplacement(key, myOption.FfmpegMetadata); + string value = GetValueForReplacement(key, metadata); if (!string.IsNullOrEmpty(value)) { argsBuilder.Replace($"{key}={match.Groups[2].Value}", $"{key}=\"{value}\" "); + metadata = metadata.Replace($"{key}={value}",""); } } } + string[] keyValuePairs = metadata.Split(','); + foreach (var keyValuePair in keyValuePairs) + { + string[] parts = keyValuePair.Split('='); + if (parts.Length == 2) + { + string key = parts[0]; + string value = parts[1]; + argsBuilder.Append($"-metadata {key}=\"{value}\" "); + } + } } argsBuilder.Append("-c copy "); if (audioOnly && audioPath == "") argsBuilder.Append("-vn "); From f67803c71f3334f19cba61c2cf3e2fbd7366f616 Mon Sep 17 00:00:00 2001 From: Ayou <31991515+Ayouuuu@users.noreply.github.com> Date: Thu, 14 Dec 2023 22:47:21 +0800 Subject: [PATCH 5/5] add: support placeholder - - - - - - - - - --- BBDown/BBDownMuxer.cs | 34 ++++++++++++++++++++++++++++++---- BBDown/Program.cs | 8 +++++--- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/BBDown/BBDownMuxer.cs b/BBDown/BBDownMuxer.cs index 969b9baca..1dfb6da7f 100644 --- a/BBDown/BBDownMuxer.cs +++ b/BBDown/BBDownMuxer.cs @@ -96,7 +96,10 @@ private static int MuxByMp4box(string videoPath, string audioPath, string outPat return RunExe(MP4BOX, arguments, MP4BOX != "mp4box"); } - public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List audioMaterial, string outPath, string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List? subs = null, bool audioOnly = false, bool videoOnly = false, List? points = null, long pubTime = 0, bool simplyMux = false, MyOption myOption = null) + public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List audioMaterial, string outPath, + string desc = "", string title = "", string author = "", string episodeId = "", string pic = "", string lang = "", List? subs = null, + bool audioOnly = false, bool videoOnly = false, List? points = null, + long pubTime = 0, bool simplyMux = false, MyOption myOption = null,Page p = null,int pagesCount = 0) { if (audioOnly && audioPath != "") videoPath = ""; @@ -195,7 +198,7 @@ public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List if (match.Success) { string key = match.Groups[1].Value; - string value = GetValueForReplacement(key, metadata); + string value = FormatValue(GetValueForReplacement(key, metadata),title,p,pagesCount); if (!string.IsNullOrEmpty(value)) { argsBuilder.Replace($"{key}={match.Groups[2].Value}", $"{key}=\"{value}\" "); @@ -207,10 +210,10 @@ public static int MuxAV(bool useMp4box, string videoPath, string audioPath, List foreach (var keyValuePair in keyValuePairs) { string[] parts = keyValuePair.Split('='); - if (parts.Length == 2) + if (parts.Length == 2) { string key = parts[0]; - string value = parts[1]; + string value = FormatValue(parts[1], title, p, pagesCount); argsBuilder.Append($"-metadata {key}=\"{value}\" "); } } @@ -262,5 +265,28 @@ private static string GetValueForReplacement(string key, string input) return string.Empty; } } + + private static string FormatValue(string value,string title,Page p,int pagesCount) { + var regex = Program.InfoRegex(); + foreach (Match m in regex.Matches(value).Cast()) + { + var key = m.Groups[1].Value; + var v = key switch + { + "videoTitle" => BBDownUtil.GetValidFileName(title, filterSlash: true).Trim().TrimEnd('.').Trim(), + "pageNumber" => p.index.ToString(), + "pageNumberWithZero" => p.index.ToString().PadLeft(pagesCount.ToString().Length, '0'), + "pageTitle" => BBDownUtil.GetValidFileName(p.title, filterSlash: true).Trim().TrimEnd('.').Trim(), + "bvid" => p.bvid, + "aid" => p.aid, + "cid" => p.cid, + "ownerName" => p.ownerName == null ? "" : BBDownUtil.GetValidFileName(p.ownerName, filterSlash: true).Trim().TrimEnd('.').Trim(), + "ownerMid" => p.ownerMid ?? "", + _ => $"<{key}>" + }; + value = value.Replace(m.Value, v); + } + return value; + } } } diff --git a/BBDown/Program.cs b/BBDown/Program.cs index 00aa38708..41312f976 100644 --- a/BBDown/Program.cs +++ b/BBDown/Program.cs @@ -626,7 +626,8 @@ private static async Task DownloadPageAsync(Page p, MyOption myOption, VInfo vIn (pagesCount > 1 || (bangumi && !vInfo.IsBangumiEnd)) ? p.title : "", File.Exists(coverPath) ? coverPath : "", lang, - subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux, myOption); + subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, + p.pubTime, myOption.SimplyMux, myOption, p, pagesCount); if (code != 0 || !File.Exists(savePath) || new FileInfo(savePath).Length == 0) { LogError("合并失败"); return; @@ -715,7 +716,8 @@ private static async Task DownloadPageAsync(Page p, MyOption myOption, VInfo vIn (pagesCount > 1 || (bangumi && !vInfo.IsBangumiEnd)) ? p.title : "", File.Exists(coverPath) ? coverPath : "", lang, - subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, myOption.SimplyMux, myOption); + subtitleInfo, myOption.AudioOnly, myOption.VideoOnly, p.points, p.pubTime, + myOption.SimplyMux, myOption, p, pagesCount); if (code != 0 || !File.Exists(savePath) || new FileInfo(savePath).Length == 0) { LogError("合并失败"); return; @@ -843,6 +845,6 @@ private static string FormatSavePath(string savePathFormat, string title, Video? } [GeneratedRegex("<([\\w:]+?)>")] - private static partial Regex InfoRegex(); + public static partial Regex InfoRegex(); } }