优化直播字幕录制逻辑
This commit is contained in:
parent
80b6f85578
commit
0f89ecc7cc
|
@ -334,7 +334,7 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
|
|
||||||
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
|
public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
|
||||||
{
|
{
|
||||||
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220918")
|
var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220919")
|
||||||
{
|
{
|
||||||
Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
|
||||||
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
|
||||||
|
|
|
@ -126,6 +126,8 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
|
ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
|
||||||
List<Mediainfo> mediaInfos = new();
|
List<Mediainfo> mediaInfos = new();
|
||||||
FileStream? fileOutputStream = null;
|
FileStream? fileOutputStream = null;
|
||||||
|
WebVttSub currentVtt = new(); //字幕流始终维护一个实例
|
||||||
|
bool firstSub = true;
|
||||||
task.MaxValue = 0;
|
task.MaxValue = 0;
|
||||||
task.StartTask();
|
task.StartTask();
|
||||||
|
|
||||||
|
@ -283,42 +285,27 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
||||||
&& streamSpec.Extension != null && streamSpec.Extension.Contains("vtt"))
|
&& streamSpec.Extension != null && streamSpec.Extension.Contains("vtt"))
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.fixingVTT);
|
|
||||||
//排序字幕并修正时间戳
|
//排序字幕并修正时间戳
|
||||||
bool first = true;
|
|
||||||
var finalVtt = new WebVttSub();
|
|
||||||
var keys = FileDic.Keys.OrderBy(k => k.Index);
|
var keys = FileDic.Keys.OrderBy(k => k.Index);
|
||||||
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 vtt = WebVttSub.Parse(vttContent);
|
||||||
//手动计算MPEGTS
|
//手动计算MPEGTS
|
||||||
if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
|
if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
|
||||||
{
|
{
|
||||||
vtt.MpegtsTimestamp = 90 * (long)(seg.Duration * 1000) * seg.Index;
|
vtt.MpegtsTimestamp = 90 * (long)(seg.Duration * 1000) * seg.Index;
|
||||||
}
|
}
|
||||||
if (first)
|
if (firstSub)
|
||||||
{
|
{
|
||||||
finalVtt = vtt;
|
currentVtt = vtt;
|
||||||
first = false;
|
firstSub = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
finalVtt.AddCuesFromOne(vtt);
|
currentVtt.AddCuesFromOne(vtt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//写出字幕
|
|
||||||
var files = FileDic.Values.Where(v => !Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
|
|
||||||
foreach (var item in files) File.Delete(item);
|
|
||||||
FileDic.Clear();
|
|
||||||
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(files.Last()) + ".vtt");
|
|
||||||
var subContentFixed = finalVtt.ToString();
|
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
|
|
||||||
FileDic[keys.First()] = new DownloadResult()
|
|
||||||
{
|
|
||||||
ActualContentLength = subContentFixed.Length,
|
|
||||||
ActualFilePath = path
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//自动修复VTT mp4字幕
|
//自动修复VTT mp4字幕
|
||||||
|
@ -330,21 +317,16 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
|
var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
|
||||||
if (sawVtt)
|
if (sawVtt)
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.fixingVTTmp4);
|
|
||||||
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
|
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
|
||||||
var finalVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
|
if (firstSub)
|
||||||
//写出字幕
|
|
||||||
var firstKey = FileDic.Keys.First();
|
|
||||||
foreach (var item in mp4s) File.Delete(item);
|
|
||||||
FileDic.Clear();
|
|
||||||
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(mp4s.Last()) + ".vtt");
|
|
||||||
var subContentFixed = finalVtt.ToString();
|
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
|
|
||||||
FileDic[firstKey] = new DownloadResult()
|
|
||||||
{
|
{
|
||||||
ActualContentLength = subContentFixed.Length,
|
currentVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
|
||||||
ActualFilePath = path
|
}
|
||||||
};
|
else
|
||||||
|
{
|
||||||
|
var finalVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
|
||||||
|
currentVtt.AddCuesFromOne(finalVtt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,21 +334,16 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
|
||||||
&& streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
|
&& streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.fixingTTML);
|
|
||||||
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".ttml")).OrderBy(s => s).ToArray();
|
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".ttml")).OrderBy(s => s).ToArray();
|
||||||
var finalVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
|
if (firstSub)
|
||||||
//写出字幕
|
|
||||||
var firstKey = FileDic.Keys.First();
|
|
||||||
foreach (var item in mp4s) File.Delete(item);
|
|
||||||
FileDic.Clear();
|
|
||||||
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(mp4s.Last()) + ".vtt");
|
|
||||||
var subContentFixed = finalVtt.ToString();
|
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
|
|
||||||
FileDic[firstKey] = new DownloadResult()
|
|
||||||
{
|
{
|
||||||
ActualContentLength = subContentFixed.Length,
|
currentVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
|
||||||
ActualFilePath = path
|
}
|
||||||
};
|
else
|
||||||
|
{
|
||||||
|
var finalVtt = MP4TtmlUtil.ExtractFromTTMLs(mp4s, 0);
|
||||||
|
currentVtt.AddCuesFromOne(finalVtt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//自动修复TTML mp4字幕
|
//自动修复TTML mp4字幕
|
||||||
|
@ -374,25 +351,20 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
&& streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
|
&& streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
|
||||||
&& streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
|
&& streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
|
||||||
{
|
{
|
||||||
Logger.WarnMarkUp(ResString.fixingTTMLmp4);
|
|
||||||
//sawTtml暂时不判断
|
//sawTtml暂时不判断
|
||||||
//var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
|
//var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
|
||||||
//var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
|
//var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
|
||||||
//var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
|
//var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
|
||||||
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
|
var mp4s = FileDic.Values.Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).OrderBy(s => s).ToArray();
|
||||||
var finalVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0);
|
if (firstSub)
|
||||||
//写出字幕
|
|
||||||
var firstKey = FileDic.Keys.First();
|
|
||||||
foreach (var item in mp4s) File.Delete(item);
|
|
||||||
FileDic.Clear();
|
|
||||||
var path = Path.Combine(tmpDir, Path.GetFileNameWithoutExtension(mp4s.Last()) + ".vtt");
|
|
||||||
var subContentFixed = finalVtt.ToString();
|
|
||||||
await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
|
|
||||||
FileDic[firstKey] = new DownloadResult()
|
|
||||||
{
|
{
|
||||||
ActualContentLength = subContentFixed.Length,
|
currentVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0);
|
||||||
ActualFilePath = path
|
}
|
||||||
};
|
else
|
||||||
|
{
|
||||||
|
var finalVtt = MP4TtmlUtil.ExtractFromMp4s(mp4s, 0);
|
||||||
|
currentVtt.AddCuesFromOne(finalVtt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//合并逻辑
|
//合并逻辑
|
||||||
|
@ -403,7 +375,11 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
if (streamSpec.Extension == null) outputExt = ".ts";
|
if (streamSpec.Extension == null) outputExt = ".ts";
|
||||||
else if (streamSpec.MediaType == MediaType.AUDIO && streamSpec.Extension == "m4s") outputExt = ".m4a";
|
else if (streamSpec.MediaType == MediaType.AUDIO && streamSpec.Extension == "m4s") outputExt = ".m4a";
|
||||||
else if (streamSpec.MediaType != MediaType.SUBTITLES && streamSpec.Extension == "m4s") outputExt = ".mp4";
|
else if (streamSpec.MediaType != MediaType.SUBTITLES && streamSpec.Extension == "m4s") outputExt = ".mp4";
|
||||||
else if (streamSpec.MediaType == MediaType.SUBTITLES) outputExt = ".vtt";
|
else if (streamSpec.MediaType == MediaType.SUBTITLES)
|
||||||
|
{
|
||||||
|
if (DownloaderConfig.MyOptions.SubtitleFormat == Enum.SubtitleFormat.SRT) outputExt = ".srt";
|
||||||
|
else outputExt = ".vtt";
|
||||||
|
}
|
||||||
|
|
||||||
var output = Path.Combine(saveDir, saveName + outputExt);
|
var output = Path.Combine(saveDir, saveName + outputExt);
|
||||||
|
|
||||||
|
@ -457,18 +433,28 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var initResult = streamSpec.Playlist!.MediaInit != null ? FileDic[streamSpec.Playlist!.MediaInit!]! : null;
|
||||||
var files = FileDic.Select(f => f.Value).Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
|
var files = FileDic.Select(f => f.Value).Select(v => v!.ActualFilePath).OrderBy(s => s).ToArray();
|
||||||
foreach (var inputFilePath in files)
|
foreach (var inputFilePath in files)
|
||||||
{
|
{
|
||||||
using (var inputStream = File.OpenRead(inputFilePath))
|
|
||||||
{
|
|
||||||
inputStream.CopyTo(fileOutputStream);
|
|
||||||
}
|
|
||||||
if (!DownloaderConfig.MyOptions.LiveKeepSegments && !Path.GetFileName(inputFilePath).StartsWith("_init"))
|
if (!DownloaderConfig.MyOptions.LiveKeepSegments && !Path.GetFileName(inputFilePath).StartsWith("_init"))
|
||||||
{
|
{
|
||||||
File.Delete(inputFilePath);
|
File.Delete(inputFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var subText = currentVtt.ToStringWithHeader();
|
||||||
|
if (outputExt == ".srt")
|
||||||
|
{
|
||||||
|
subText = OtherUtil.WebVtt2Other(currentVtt, Enum.SubtitleFormat.SRT);
|
||||||
|
}
|
||||||
|
var subBytes = Encoding.UTF8.GetBytes(subText);
|
||||||
|
fileOutputStream.Position = 0;
|
||||||
|
fileOutputStream.Write(subBytes);
|
||||||
|
FileDic.Clear();
|
||||||
|
if (initResult != null)
|
||||||
|
{
|
||||||
|
FileDic[streamSpec.Playlist!.MediaInit!] = initResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//刷新buffer
|
//刷新buffer
|
||||||
|
|
Loading…
Reference in New Issue