diff --git a/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs b/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs index 294e8a0..617bc8d 100644 --- a/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs +++ b/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs @@ -16,6 +16,7 @@ namespace N_m3u8DL_RE.Common [JsonSerializable(typeof(IOrderedEnumerable))] [JsonSerializable(typeof(IEnumerable))] [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Dictionary))] internal partial class JsonContext : JsonSerializerContext { } } diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs index e5b5b09..5e76723 100644 --- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs +++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs @@ -72,6 +72,7 @@ namespace N_m3u8DL_RE.Common.Resource public static string cmd_liveRecordLimit { get => GetText("cmd_liveRecordLimit"); } public static string cmd_taskStartAt { get => GetText("cmd_taskStartAt"); } public static string cmd_liveWaitTime { get => GetText("cmd_liveWaitTime"); } + public static string cmd_liveFixVttByAudio { get => GetText("cmd_liveFixVttByAudio"); } public static string cmd_liveRealTimeMerge { get => GetText("cmd_liveRealTimeMerge"); } public static string cmd_livePerformAsVod { get => GetText("cmd_livePerformAsVod"); } public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); } diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index 1aed7bb..5395eee 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -154,6 +154,12 @@ namespace N_m3u8DL_RE.Common.Resource zhTW: "mkvmerge可執行程序全路徑, 例如 C:\\Tools\\mkvmerge.exe", enUS: "Full path to the mkvmerge binary, like C:\\Tools\\mkvmerge.exe" ), + ["cmd_liveFixVttByAudio"] = new TextContainer + ( + zhCN: "通过读取音频文件的起始时间修正VTT字幕", + zhTW: "透過讀取音訊檔案的起始時間修正VTT字幕", + enUS: "Correct VTT sub by reading the start time of the audio file" + ), ["cmd_header"] = new TextContainer ( zhCN: "为HTTP请求设置特定的请求头, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"", diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index ed4b480..b466068 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -79,6 +79,7 @@ namespace N_m3u8DL_RE.CommandLine private readonly static Option LivePipeMux = new(new string[] { "--live-pipe-mux" }, description: ResString.cmd_livePipeMux, getDefaultValue: () => false); private readonly static Option LiveRecordLimit = new(new string[] { "--live-record-limit" }, description: ResString.cmd_liveRecordLimit, parseArgument: ParseLiveLimit) { ArgumentHelpName = "HH:mm:ss" }; private readonly static Option LiveWaitTime = new(new string[] { "--live-wait-time" }, description: ResString.cmd_liveWaitTime) { ArgumentHelpName = "SEC" }; + private readonly static Option LiveFixVttByAudio = new(new string[] { "--live-fix-vtt-by-audio" }, description: ResString.cmd_liveFixVttByAudio, getDefaultValue: () => false); //复杂命令行如下 @@ -418,6 +419,7 @@ namespace N_m3u8DL_RE.CommandLine TaskStartAt = bindingContext.ParseResult.GetValueForOption(TaskStartAt), LivePerformAsVod = bindingContext.ParseResult.GetValueForOption(LivePerformAsVod), LivePipeMux = bindingContext.ParseResult.GetValueForOption(LivePipeMux), + LiveFixVttByAudio = bindingContext.ParseResult.GetValueForOption(LiveFixVttByAudio), UseSystemProxy = bindingContext.ParseResult.GetValueForOption(UseSystemProxy), CustomProxy = bindingContext.ParseResult.GetValueForOption(CustomProxy), LiveWaitTime = bindingContext.ParseResult.GetValueForOption(LiveWaitTime), @@ -487,7 +489,7 @@ namespace N_m3u8DL_RE.CommandLine LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption, MuxAfterDone, CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, TaskStartAt, - LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LivePipeMux, LiveRecordLimit, LiveWaitTime, + LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LivePipeMux, LiveFixVttByAudio, LiveRecordLimit, LiveWaitTime, MuxImports, VideoFilter, AudioFilter, SubtitleFilter, DropVideoFilter, DropAudioFilter, DropSubtitleFilter, MoreHelp }; diff --git a/src/N_m3u8DL-RE/CommandLine/MyOption.cs b/src/N_m3u8DL-RE/CommandLine/MyOption.cs index 1bd62f4..5242fec 100644 --- a/src/N_m3u8DL-RE/CommandLine/MyOption.cs +++ b/src/N_m3u8DL-RE/CommandLine/MyOption.cs @@ -226,5 +226,9 @@ namespace N_m3u8DL_RE.CommandLine /// See: . /// public bool LivePipeMux { get; set; } + /// + /// See: . + /// + public bool LiveFixVttByAudio { get; set; } } } \ No newline at end of file diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs index da646e8..d2c107b 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs @@ -45,6 +45,9 @@ namespace N_m3u8DL_RE.DownloadManager ConcurrentDictionary DateTimeDic = new(); //上次下载的dateTime CancellationTokenSource CancellationTokenSource = new(); //取消Wait + private readonly object lockObj = new object(); + TimeSpan? audioStart = null; + public SimpleLiveRecordManager2(DownloaderConfig downloaderConfig, List selectedSteams, StreamExtractor streamExtractor) { this.DownloaderConfig = downloaderConfig; @@ -248,6 +251,10 @@ namespace N_m3u8DL_RE.DownloadManager Logger.WarnMarkUp(ResString.readingInfo); mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath); mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp())); + lock (lockObj) + { + if (audioStart == null) audioStart = mediaInfos.FirstOrDefault(x => x.Type == "Audio")?.StartTime; + } ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter); readInfo = true; } @@ -324,6 +331,10 @@ namespace N_m3u8DL_RE.DownloadManager Logger.WarnMarkUp(ResString.readingInfo); mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath); mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp())); + lock (lockObj) + { + if (audioStart == null) audioStart = mediaInfos.FirstOrDefault(x => x.Type == "Audio")?.StartTime; + } ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter); readInfo = true; } @@ -367,7 +378,13 @@ namespace N_m3u8DL_RE.DownloadManager foreach (var seg in keys) { var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath); - var vtt = WebVttSub.Parse(vttContent); + var waitCount = 0; + while (DownloaderConfig.MyOptions.LiveFixVttByAudio && audioStart == null && waitCount++ < 5) + { + await Task.Delay(1000); + } + var subOffset = audioStart != null ? (long)audioStart.Value.TotalMilliseconds : 0L; + var vtt = WebVttSub.Parse(vttContent, subOffset); //手动计算MPEGTS if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0) { @@ -773,6 +790,11 @@ namespace N_m3u8DL_RE.DownloadManager if (WAIT_SEC <= 0) WAIT_SEC = 1; Logger.WarnMarkUp($"set refresh interval to {WAIT_SEC} seconds"); } + //如果没有选中音频 取消通过音频修复vtt时间轴 + if (!SelectedSteams.Any(x => x.MediaType == MediaType.AUDIO)) + { + DownloaderConfig.MyOptions.LiveFixVttByAudio = false; + } /*//写出master if (DownloaderConfig.MyOptions.LiveWriteHLS) diff --git a/src/N_m3u8DL-RE/Entity/Mediainfo.cs b/src/N_m3u8DL-RE/Entity/Mediainfo.cs index ae019b5..5bf440f 100644 --- a/src/N_m3u8DL-RE/Entity/Mediainfo.cs +++ b/src/N_m3u8DL-RE/Entity/Mediainfo.cs @@ -16,6 +16,7 @@ namespace N_m3u8DL_RE.Entity public string? Resolution { get; set; } public string? Fps { get; set; } public string? Type { get; set; } + public TimeSpan? StartTime { get; set; } public bool DolbyVison { get; set; } public bool HDR { get; set; } diff --git a/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj b/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj index c6ff558..fa29ac2 100644 --- a/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj +++ b/src/N_m3u8DL-RE/N_m3u8DL-RE.csproj @@ -7,7 +7,7 @@ enable preview enable - 0.1.5.3 + 0.1.5.4 AnyCPU;x64 diff --git a/src/N_m3u8DL-RE/Util/MediainfoUtil.cs b/src/N_m3u8DL-RE/Util/MediainfoUtil.cs index 6e8ccc0..2235be7 100644 --- a/src/N_m3u8DL-RE/Util/MediainfoUtil.cs +++ b/src/N_m3u8DL-RE/Util/MediainfoUtil.cs @@ -30,6 +30,8 @@ namespace N_m3u8DL_RE.Util private static partial Regex FpsRegex(); [GeneratedRegex("DOVI configuration record.*profile: (\\d).*compatibility id: (\\d)")] private static partial Regex DoViRegex(); + [GeneratedRegex("Duration.*?start: (\\d+\\.?\\d{0,3})")] + private static partial Regex StartRegex(); public static async Task> ReadInfoAsync(string binary, string file) { @@ -73,6 +75,13 @@ namespace N_m3u8DL_RE.Util ) info.DolbyVison = true; + if (StartRegex().IsMatch(output)) + { + var f = StartRegex().Match(output).Groups[1].Value; + if (double.TryParse(f, out var d)) + info.StartTime = TimeSpan.FromSeconds(d); + } + result.Add(info); }