From 8945777e6b26a2c34db3491241d78028caa5801b Mon Sep 17 00:00:00 2001 From: nilaoda Date: Wed, 14 Jun 2023 22:07:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E4=BB=B6=E5=A4=B9?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/N_m3u8DL-RE.Parser/StreamExtractor.cs | 8 +++ src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs | 2 +- src/N_m3u8DL-RE/Config/DownloaderConfig.cs | 6 ++- .../DownloadManager/SimpleDownloadManager.cs | 19 +++++-- .../SimpleLiveRecordManager2.cs | 19 +++++-- src/N_m3u8DL-RE/Program.cs | 49 ++++++++++++++----- 6 files changed, 79 insertions(+), 24 deletions(-) diff --git a/src/N_m3u8DL-RE.Parser/StreamExtractor.cs b/src/N_m3u8DL-RE.Parser/StreamExtractor.cs index 9195991..8559442 100644 --- a/src/N_m3u8DL-RE.Parser/StreamExtractor.cs +++ b/src/N_m3u8DL-RE.Parser/StreamExtractor.cs @@ -18,6 +18,8 @@ namespace N_m3u8DL_RE.Parser private string rawText; private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1); + public Dictionary RawFiles { get; set; } = new(); //存储(文件名,文件内容) + public StreamExtractor() { @@ -55,24 +57,28 @@ namespace N_m3u8DL_RE.Parser public void LoadSourceFromText(string rawText) { + var rawType = "txt"; rawText = rawText.Trim(); this.rawText = rawText; if (rawText.StartsWith(HLSTags.ext_m3u)) { Logger.InfoMarkUp(ResString.matchHLS); extractor = new HLSExtractor(parserConfig); + rawType = "m3u8"; } else if (rawText.Contains("") && rawText.Contains("") && rawText.Contains(" diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index 73e89b8..b75479c 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -46,7 +46,7 @@ namespace N_m3u8DL_RE.CommandLine private readonly static Option DelAfterDone = new(new string[] { "--del-after-done" }, description: ResString.cmd_delAfterDone, getDefaultValue: () => true); private readonly static Option AutoSubtitleFix = new(new string[] { "--auto-subtitle-fix" }, description: ResString.cmd_subtitleFix, getDefaultValue: () => true); private readonly static Option CheckSegmentsCount = new(new string[] { "--check-segments-count" }, description: ResString.cmd_checkSegmentsCount, getDefaultValue: () => true); - private readonly static Option WriteMetaJson = new(new string[] { "--write-meta-json" }, description: ResString.cmd_writeMetaJson, getDefaultValue: () => false); + private readonly static Option WriteMetaJson = new(new string[] { "--write-meta-json" }, description: ResString.cmd_writeMetaJson, getDefaultValue: () => true); private readonly static Option AppendUrlParams = new(new string[] { "--append-url-params" }, description: ResString.cmd_appendUrlParams, getDefaultValue: () => false); private readonly static Option MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false); private readonly static Option UseShakaPackager = new (new string[] { "--use-shaka-packager" }, description: ResString.cmd_useShakaPackager, getDefaultValue: () => false); diff --git a/src/N_m3u8DL-RE/Config/DownloaderConfig.cs b/src/N_m3u8DL-RE/Config/DownloaderConfig.cs index 0f70597..c953878 100644 --- a/src/N_m3u8DL-RE/Config/DownloaderConfig.cs +++ b/src/N_m3u8DL-RE/Config/DownloaderConfig.cs @@ -11,8 +11,12 @@ namespace N_m3u8DL_RE.Config { internal class DownloaderConfig { - public required MyOption MyOptions { get; set; } + public required MyOption MyOptions { get; set; } + /// + /// 前置阶段生成的文件夹名 + /// + public required string DirPrefix { get; set; } /// /// 文件名模板 /// diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs index 05b281c..121067e 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs @@ -22,7 +22,6 @@ namespace N_m3u8DL_RE.DownloadManager DownloaderConfig DownloaderConfig; StreamExtractor StreamExtractor; List SelectedSteams; - DateTime NowDateTime; List OutputFiles = new(); public SimpleDownloadManager(DownloaderConfig downloaderConfig, List selectedSteams, StreamExtractor streamExtractor) @@ -31,7 +30,6 @@ namespace N_m3u8DL_RE.DownloadManager this.SelectedSteams = selectedSteams; this.StreamExtractor = streamExtractor; Downloader = new SimpleDownloader(DownloaderConfig); - NowDateTime = DateTime.Now; } private string? ReadInit(byte[] data) @@ -116,8 +114,8 @@ namespace N_m3u8DL_RE.DownloadManager } var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO; - var dirName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}_{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}"; - var tmpDir = Path.Combine(DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory, dirName); + var dirName = $"{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}"; + var tmpDir = Path.Combine(DownloaderConfig.DirPrefix, dirName); var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory; var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName; var headers = DownloaderConfig.Headers; @@ -670,6 +668,17 @@ namespace N_m3u8DL_RE.DownloadManager var success = Results.Values.All(v => v == true); + //删除临时文件夹 + if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && success) + { + foreach (var item in StreamExtractor.RawFiles) + { + var file = Path.Combine(DownloaderConfig.DirPrefix, item.Key); + if (File.Exists(file)) File.Delete(file); + } + OtherUtil.SafeDeleteDir(DownloaderConfig.DirPrefix); + } + //混流 if (success && DownloaderConfig.MyOptions.MuxAfterDone && OutputFiles.Count > 0) { @@ -681,7 +690,7 @@ namespace N_m3u8DL_RE.DownloadManager OutputFiles.ForEach(f => Logger.WarnMarkUp($"[grey]{Path.GetFileName(f.FilePath).EscapeMarkup()}[/]")); var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory; var ext = DownloaderConfig.MyOptions.MuxToMp4 ? ".mp4" : ".mkv"; - var outName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}.MUX"; + var outName = $"{DownloaderConfig.DirPrefix}.MUX"; var outPath = Path.Combine(saveDir, outName); Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}{ext}[/]"); var result = false; diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs index d2c107b..b214fdf 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs @@ -33,7 +33,6 @@ namespace N_m3u8DL_RE.DownloadManager List SelectedSteams; ConcurrentDictionary PipeSteamNamesDic = new(); List OutputFiles = new(); - DateTime NowDateTime; DateTime? PublishDateTime; bool STOP_FLAG = false; int WAIT_SEC = 0; //刷新间隔 @@ -52,7 +51,6 @@ namespace N_m3u8DL_RE.DownloadManager { this.DownloaderConfig = downloaderConfig; Downloader = new SimpleDownloader(DownloaderConfig); - NowDateTime = DateTime.Now; PublishDateTime = selectedSteams.FirstOrDefault()?.PublishTime; StreamExtractor = streamExtractor; SelectedSteams = selectedSteams; @@ -184,8 +182,8 @@ namespace N_m3u8DL_RE.DownloadManager var name = streamSpec.ToShortString(); var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO; - var dirName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}_{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}"; - var tmpDir = Path.Combine(DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory, dirName); + var dirName = $"{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}"; + var tmpDir = Path.Combine(DownloaderConfig.DirPrefix, dirName); var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory; var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName; var headers = DownloaderConfig.Headers; @@ -860,6 +858,17 @@ namespace N_m3u8DL_RE.DownloadManager var success = Results.Values.All(v => v == true); + //删除临时文件夹 + if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && success) + { + foreach (var item in StreamExtractor.RawFiles) + { + var file = Path.Combine(DownloaderConfig.DirPrefix, item.Key); + if (File.Exists(file)) File.Delete(file); + } + OtherUtil.SafeDeleteDir(DownloaderConfig.DirPrefix); + } + //混流 if (success && DownloaderConfig.MyOptions.MuxAfterDone && OutputFiles.Count > 0) { @@ -871,7 +880,7 @@ namespace N_m3u8DL_RE.DownloadManager OutputFiles.ForEach(f => Logger.WarnMarkUp($"[grey]{Path.GetFileName(f.FilePath).EscapeMarkup()}[/]")); var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory; var ext = DownloaderConfig.MyOptions.MuxToMp4 ? ".mp4" : ".mkv"; - var outName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}.MUX"; + var outName = $"{DownloaderConfig.DirPrefix}.MUX"; var outPath = Path.Combine(saveDir, outName); Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}{ext}[/]"); var result = false; diff --git a/src/N_m3u8DL-RE/Program.cs b/src/N_m3u8DL-RE/Program.cs index 0dd4e24..1bcebb2 100644 --- a/src/N_m3u8DL-RE/Program.cs +++ b/src/N_m3u8DL-RE/Program.cs @@ -104,6 +104,8 @@ namespace N_m3u8DL_RE throw new FileNotFoundException(ResString.ffmpegNotFound); } + Logger.Extra($"ffmpeg: {option.FFmpegBinaryPath}"); + //预先检查mkvmerge if (option.UseMkvmerge && option.MuxAfterDone) { @@ -113,6 +115,7 @@ namespace N_m3u8DL_RE { throw new FileNotFoundException("mkvmerge not found"); } + Logger.Extra($"mkvmerge: {option.MkvmergeBinaryPath}"); } //预先检查 @@ -128,12 +131,14 @@ namespace N_m3u8DL_RE var file4 = GlobalUtil.FindExecutable("packager-win-x64"); if (file == null && file2 == null && file3 == null && file4 == null) throw new FileNotFoundException("shaka-packager not found!"); option.DecryptionBinaryPath = file ?? file2 ?? file3 ?? file4; + Logger.Extra($"shaka-packager: {option.DecryptionBinaryPath}"); } else { var file = GlobalUtil.FindExecutable("mp4decrypt"); if (file == null) throw new FileNotFoundException("mp4decrypt not found!"); option.DecryptionBinaryPath = file; + Logger.Extra($"mp4decrypt: {option.DecryptionBinaryPath}"); } } else if (!File.Exists(option.DecryptionBinaryPath)) @@ -191,6 +196,7 @@ namespace N_m3u8DL_RE //解析流信息 var streams = await extractor.ExtractStreamsAsync(); + //全部媒体 var lists = streams.OrderBy(p => p.MediaType).ThenByDescending(p => p.Bandwidth).ThenByDescending(GetOrder); //基本流 @@ -200,11 +206,12 @@ namespace N_m3u8DL_RE //可选字幕轨道 var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES); - if (option.WriteMetaJson) - { - Logger.Warn(ResString.writeJson); - await File.WriteAllTextAsync("meta.json", GlobalUtil.ConvertToJson(lists), Encoding.UTF8); - } + //生成文件夹 + var tmpDir = Path.Combine(option.TmpDir ?? Environment.CurrentDirectory, $"{option.SaveName ?? DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}"); + //记录文件 + extractor.RawFiles["meta.json"] = GlobalUtil.ConvertToJson(lists); + //写出文件 + await WriteRawFilesAsync(option, extractor, tmpDir); Logger.Info(ResString.streamsInfo, lists.Count(), basicStreams.Count(), audios.Count(), subs.Count()); @@ -272,11 +279,9 @@ namespace N_m3u8DL_RE option.BinaryMerge = true; } - if (option.WriteMetaJson) - { - Logger.Warn(ResString.writeJson); - await File.WriteAllTextAsync("meta_selected.json", GlobalUtil.ConvertToJson(selectedStreams), Encoding.UTF8); - } + //记录文件 + extractor.RawFiles["meta_selected.json"] = GlobalUtil.ConvertToJson(selectedStreams); + Logger.Info(ResString.selectedStream); foreach (var item in selectedStreams) @@ -284,12 +289,16 @@ namespace N_m3u8DL_RE Logger.InfoMarkUp(item.ToString()); } + //写出文件 + await WriteRawFilesAsync(option, extractor, tmpDir); + if (option.SkipDownload) { return; } #if DEBUG + Console.WriteLine("Press any key to continue..."); Console.ReadKey(); #endif @@ -305,17 +314,18 @@ namespace N_m3u8DL_RE var downloadConfig = new DownloaderConfig() { MyOptions = option, + DirPrefix = tmpDir, Headers = parserConfig.Headers, //使用命令行解析得到的Headers }; var result = false; - + if (extractor.ExtractorType == ExtractorType.HTTP_LIVE) { var sldm = new HTTPLiveRecordManager(downloadConfig, selectedStreams, extractor); result = await sldm.StartRecordAsync(); } - else if(!livingFlag) + else if (!livingFlag) { //开始下载 var sdm = new SimpleDownloadManager(downloadConfig, selectedStreams, extractor); @@ -338,6 +348,21 @@ namespace N_m3u8DL_RE } } + private static async Task WriteRawFilesAsync(MyOption option, StreamExtractor extractor, string tmpDir) + { + //写出json文件 + if (option.WriteMetaJson) + { + if (!Directory.Exists(tmpDir)) Directory.CreateDirectory(tmpDir); + Logger.Warn(ResString.writeJson); + foreach (var item in extractor.RawFiles) + { + var file = Path.Combine(tmpDir, item.Key); + if (!File.Exists(file)) await File.WriteAllTextAsync(file, item.Value, Encoding.UTF8); + } + } + } + static async Task CheckUpdateAsync() { try