支持从音频修正VTT字幕时间轴

This commit is contained in:
nilaoda 2023-04-12 22:44:41 +08:00
parent f873cbc2dd
commit 3b7d32077e
9 changed files with 49 additions and 3 deletions

View File

@ -16,6 +16,7 @@ namespace N_m3u8DL_RE.Common
[JsonSerializable(typeof(IOrderedEnumerable<StreamSpec>))] [JsonSerializable(typeof(IOrderedEnumerable<StreamSpec>))]
[JsonSerializable(typeof(IEnumerable<MediaSegment>))] [JsonSerializable(typeof(IEnumerable<MediaSegment>))]
[JsonSerializable(typeof(List<StreamSpec>))] [JsonSerializable(typeof(List<StreamSpec>))]
[JsonSerializable(typeof(List<MediaSegment>))]
[JsonSerializable(typeof(Dictionary<string, string>))] [JsonSerializable(typeof(Dictionary<string, string>))]
internal partial class JsonContext : JsonSerializerContext { } internal partial class JsonContext : JsonSerializerContext { }
} }

View File

@ -72,6 +72,7 @@ namespace N_m3u8DL_RE.Common.Resource
public static string cmd_liveRecordLimit { get => GetText("cmd_liveRecordLimit"); } public static string cmd_liveRecordLimit { get => GetText("cmd_liveRecordLimit"); }
public static string cmd_taskStartAt { get => GetText("cmd_taskStartAt"); } public static string cmd_taskStartAt { get => GetText("cmd_taskStartAt"); }
public static string cmd_liveWaitTime { get => GetText("cmd_liveWaitTime"); } 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_liveRealTimeMerge { get => GetText("cmd_liveRealTimeMerge"); }
public static string cmd_livePerformAsVod { get => GetText("cmd_livePerformAsVod"); } public static string cmd_livePerformAsVod { get => GetText("cmd_livePerformAsVod"); }
public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); } public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }

View File

@ -154,6 +154,12 @@ namespace N_m3u8DL_RE.Common.Resource
zhTW: "mkvmerge可執行程序全路徑, 例如 C:\\Tools\\mkvmerge.exe", zhTW: "mkvmerge可執行程序全路徑, 例如 C:\\Tools\\mkvmerge.exe",
enUS: "Full path to the mkvmerge binary, like 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 ["cmd_header"] = new TextContainer
( (
zhCN: "为HTTP请求设置特定的请求头, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"", zhCN: "为HTTP请求设置特定的请求头, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"",

View File

@ -79,6 +79,7 @@ namespace N_m3u8DL_RE.CommandLine
private readonly static Option<bool> LivePipeMux = new(new string[] { "--live-pipe-mux" }, description: ResString.cmd_livePipeMux, getDefaultValue: () => false); private readonly static Option<bool> LivePipeMux = new(new string[] { "--live-pipe-mux" }, description: ResString.cmd_livePipeMux, getDefaultValue: () => false);
private readonly static Option<TimeSpan?> LiveRecordLimit = new(new string[] { "--live-record-limit" }, description: ResString.cmd_liveRecordLimit, parseArgument: ParseLiveLimit) { ArgumentHelpName = "HH:mm:ss" }; private readonly static Option<TimeSpan?> LiveRecordLimit = new(new string[] { "--live-record-limit" }, description: ResString.cmd_liveRecordLimit, parseArgument: ParseLiveLimit) { ArgumentHelpName = "HH:mm:ss" };
private readonly static Option<int?> LiveWaitTime = new(new string[] { "--live-wait-time" }, description: ResString.cmd_liveWaitTime) { ArgumentHelpName = "SEC" }; private readonly static Option<int?> LiveWaitTime = new(new string[] { "--live-wait-time" }, description: ResString.cmd_liveWaitTime) { ArgumentHelpName = "SEC" };
private readonly static Option<bool> 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), TaskStartAt = bindingContext.ParseResult.GetValueForOption(TaskStartAt),
LivePerformAsVod = bindingContext.ParseResult.GetValueForOption(LivePerformAsVod), LivePerformAsVod = bindingContext.ParseResult.GetValueForOption(LivePerformAsVod),
LivePipeMux = bindingContext.ParseResult.GetValueForOption(LivePipeMux), LivePipeMux = bindingContext.ParseResult.GetValueForOption(LivePipeMux),
LiveFixVttByAudio = bindingContext.ParseResult.GetValueForOption(LiveFixVttByAudio),
UseSystemProxy = bindingContext.ParseResult.GetValueForOption(UseSystemProxy), UseSystemProxy = bindingContext.ParseResult.GetValueForOption(UseSystemProxy),
CustomProxy = bindingContext.ParseResult.GetValueForOption(CustomProxy), CustomProxy = bindingContext.ParseResult.GetValueForOption(CustomProxy),
LiveWaitTime = bindingContext.ParseResult.GetValueForOption(LiveWaitTime), LiveWaitTime = bindingContext.ParseResult.GetValueForOption(LiveWaitTime),
@ -487,7 +489,7 @@ namespace N_m3u8DL_RE.CommandLine
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption, LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
MuxAfterDone, MuxAfterDone,
CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, TaskStartAt, 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 MuxImports, VideoFilter, AudioFilter, SubtitleFilter, DropVideoFilter, DropAudioFilter, DropSubtitleFilter, MoreHelp
}; };

View File

@ -226,5 +226,9 @@ namespace N_m3u8DL_RE.CommandLine
/// See: <see cref="CommandInvoker.LivePipeMux"/>. /// See: <see cref="CommandInvoker.LivePipeMux"/>.
/// </summary> /// </summary>
public bool LivePipeMux { get; set; } public bool LivePipeMux { get; set; }
/// <summary>
/// See: <see cref="CommandInvoker.LiveFixVttByAudio"/>.
/// </summary>
public bool LiveFixVttByAudio { get; set; }
} }
} }

View File

@ -45,6 +45,9 @@ namespace N_m3u8DL_RE.DownloadManager
ConcurrentDictionary<int, long> DateTimeDic = new(); //上次下载的dateTime ConcurrentDictionary<int, long> DateTimeDic = new(); //上次下载的dateTime
CancellationTokenSource CancellationTokenSource = new(); //取消Wait CancellationTokenSource CancellationTokenSource = new(); //取消Wait
private readonly object lockObj = new object();
TimeSpan? audioStart = null;
public SimpleLiveRecordManager2(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor) public SimpleLiveRecordManager2(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor)
{ {
this.DownloaderConfig = downloaderConfig; this.DownloaderConfig = downloaderConfig;
@ -248,6 +251,10 @@ namespace N_m3u8DL_RE.DownloadManager
Logger.WarnMarkUp(ResString.readingInfo); Logger.WarnMarkUp(ResString.readingInfo);
mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath); mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath);
mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp())); 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); ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
readInfo = true; readInfo = true;
} }
@ -324,6 +331,10 @@ namespace N_m3u8DL_RE.DownloadManager
Logger.WarnMarkUp(ResString.readingInfo); Logger.WarnMarkUp(ResString.readingInfo);
mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath); mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath);
mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp())); 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); ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
readInfo = true; readInfo = true;
} }
@ -367,7 +378,13 @@ namespace N_m3u8DL_RE.DownloadManager
foreach (var seg in keys) foreach (var seg in keys)
{ {
var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath); 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 //手动计算MPEGTS
if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0) if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
{ {
@ -773,6 +790,11 @@ namespace N_m3u8DL_RE.DownloadManager
if (WAIT_SEC <= 0) WAIT_SEC = 1; if (WAIT_SEC <= 0) WAIT_SEC = 1;
Logger.WarnMarkUp($"set refresh interval to {WAIT_SEC} seconds"); Logger.WarnMarkUp($"set refresh interval to {WAIT_SEC} seconds");
} }
//如果没有选中音频 取消通过音频修复vtt时间轴
if (!SelectedSteams.Any(x => x.MediaType == MediaType.AUDIO))
{
DownloaderConfig.MyOptions.LiveFixVttByAudio = false;
}
/*//写出master /*//写出master
if (DownloaderConfig.MyOptions.LiveWriteHLS) if (DownloaderConfig.MyOptions.LiveWriteHLS)

View File

@ -16,6 +16,7 @@ namespace N_m3u8DL_RE.Entity
public string? Resolution { get; set; } public string? Resolution { get; set; }
public string? Fps { get; set; } public string? Fps { get; set; }
public string? Type { get; set; } public string? Type { get; set; }
public TimeSpan? StartTime { get; set; }
public bool DolbyVison { get; set; } public bool DolbyVison { get; set; }
public bool HDR { get; set; } public bool HDR { get; set; }

View File

@ -7,7 +7,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<Version>0.1.5.3</Version> <Version>0.1.5.4</Version>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
</PropertyGroup> </PropertyGroup>

View File

@ -30,6 +30,8 @@ namespace N_m3u8DL_RE.Util
private static partial Regex FpsRegex(); private static partial Regex FpsRegex();
[GeneratedRegex("DOVI configuration record.*profile: (\\d).*compatibility id: (\\d)")] [GeneratedRegex("DOVI configuration record.*profile: (\\d).*compatibility id: (\\d)")]
private static partial Regex DoViRegex(); private static partial Regex DoViRegex();
[GeneratedRegex("Duration.*?start: (\\d+\\.?\\d{0,3})")]
private static partial Regex StartRegex();
public static async Task<List<Mediainfo>> ReadInfoAsync(string binary, string file) public static async Task<List<Mediainfo>> ReadInfoAsync(string binary, string file)
{ {
@ -73,6 +75,13 @@ namespace N_m3u8DL_RE.Util
) )
info.DolbyVison = true; 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); result.Add(info);
} }