From 8eee867cff5672468fcfab3d5cf5c3d57b02ed87 Mon Sep 17 00:00:00 2001
From: nilaoda <nilaoda@live.com>
Date: Fri, 26 Aug 2022 22:19:12 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E5=B9=B6?=
 =?UTF-8?q?=E5=8F=91=E4=B8=8B=E8=BD=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/N_m3u8DL-RE.Common/Resource/ResString.cs  |  1 +
 src/N_m3u8DL-RE.Common/Resource/StaticText.cs |  6 ++++
 src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs | 30 +++++++++++--------
 src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs |  6 ++--
 src/N_m3u8DL-RE/CommandLine/MyOption.cs       |  4 +++
 .../DownloadManager/SimpleDownloadManager.cs  | 30 ++++++++++++++-----
 src/N_m3u8DL-RE/Util/MergeUtil.cs             |  4 +++
 7 files changed, 59 insertions(+), 22 deletions(-)

diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs
index 7fe79eb..ae1a685 100644
--- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs
@@ -48,6 +48,7 @@ namespace N_m3u8DL_RE.Common.Resource
         public static string cmd_uiLanguage { get => GetText("cmd_uiLanguage"); }
         public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
         public static string cmd_useShakaPackager { get => GetText("cmd_useShakaPackager"); }
+        public static string cmd_concurrentDownload { get => GetText("cmd_concurrentDownload"); }
         public static string cmd_useMkvmerge { get => GetText("cmd_useMkvmerge"); }
         public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
         public static string cmd_muxToMp4 { get => GetText("cmd_muxToMp4"); }
diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
index 6cb03e9..6cd0eb6 100644
--- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
@@ -244,6 +244,12 @@ namespace N_m3u8DL_RE.Common.Resource
                 zhTW: "混流时使用mkvmerge替代ffmpeg",
                 enUS: "Use mkvmerge instead of ffmpeg to mux"
             ),
+            ["cmd_concurrentDownload"] = new TextContainer
+            (
+                zhCN: "并发下载已选择的音频、视频和字幕",
+                zhTW: "並發下載已選擇的音訊、影片和字幕",
+                enUS: "Concurrently download the selected audio, video and subtitles"
+            ),
             ["cmd_muxAfterDone"] = new TextContainer
             (
                 zhCN: "所有工作完成时尝试混流分离的音视频. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
diff --git a/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs b/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs
index 0fa0903..c446732 100644
--- a/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs
+++ b/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs
@@ -3,6 +3,7 @@ using N_m3u8DL_RE.Entity;
 using Spectre.Console;
 using Spectre.Console.Rendering;
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -12,37 +13,40 @@ namespace N_m3u8DL_RE.Column
 {
     internal sealed class DownloadSpeedColumn : ProgressColumn
     {
-        private string DateTimeString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-        private string Speed = "0Bps";
+        private ConcurrentDictionary<int, string> DateTimeStringDic = new();
+        private ConcurrentDictionary<int, string> SpeedDic = new();
         protected override bool NoWrap => true;
-        public SpeedContainer SpeedContainer { get; set; }
+        public ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic { get; set; }
 
-        public DownloadSpeedColumn(SpeedContainer SpeedContainer)
+        public DownloadSpeedColumn(ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic)
         {
-            this.SpeedContainer = SpeedContainer;
+            this.SpeedContainerDic = SpeedContainerDic;
         }
 
         public Style MyStyle { get; set; } = new Style(foreground: Color.Green);
 
         public override IRenderable Render(RenderContext context, ProgressTask task, TimeSpan deltaTime)
         {
+            var taskId = task.Id;
+            var speedContainer = SpeedContainerDic[taskId];
             var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
             var flag = task.IsFinished || !task.IsStarted;
             //单文件下载汇报进度
-            if (!flag && SpeedContainer.SingleSegment && SpeedContainer.ResponseLength != null)
+            if (!flag && speedContainer.SingleSegment && speedContainer.ResponseLength != null)
             {
-                task.MaxValue = (double)SpeedContainer.ResponseLength;
-                task.Value = SpeedContainer.RDownloaded;
+                task.MaxValue = (double)speedContainer.ResponseLength;
+                task.Value = speedContainer.RDownloaded;
             }
             //一秒汇报一次即可
-            if (DateTimeString != now)
+            if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now) 
             {
-                Speed = FormatFileSize(SpeedContainer.Downloaded);
-                SpeedContainer.Reset();
-                DateTimeString = now;
+                SpeedDic[taskId] = FormatFileSize(speedContainer.Downloaded);
+                speedContainer.Reset();
             }
+            DateTimeStringDic[taskId] = now;
             var style = flag ? Style.Plain : MyStyle;
-            return flag ? new Text("-", style).Centered() : new Text(Speed, style).Centered();
+            SpeedDic.TryGetValue(taskId, out var speed);
+            return flag ? new Text("-", style).Centered() : new Text(speed ?? "0Bps", style).Centered();
         }
 
         private static string FormatFileSize(double fileSize)
diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
index c176804..1925972 100644
--- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
+++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
@@ -42,6 +42,7 @@ namespace N_m3u8DL_RE.CommandLine
         private readonly static Option<string?> DecryptionBinaryPath = new(new string[] { "--decryption-binary-path" }, description: ResString.cmd_decryptionBinaryPath);
         private readonly static Option<string?> FFmpegBinaryPath = new(new string[] { "--ffmpeg-binary-path" }, description: ResString.cmd_ffmpegBinaryPath);
         private readonly static Option<string?> BaseUrl = new(new string[] { "--base-url" }, description: ResString.cmd_baseUrl);
+        private readonly static Option<bool> ConcurrentDownload = new(new string[] { "--concurrent-download" }, description: ResString.cmd_concurrentDownload, getDefaultValue: () => false);
 
         //复杂命令行如下
         private readonly static Option<MuxOptions?> MuxAfterDone = new(new string[] { "-M", "--mux-after-done" }, description: ResString.cmd_muxAfterDone, parseArgument: ParseMuxAfterDone) { ArgumentHelpName = "OPTIONS" };
@@ -187,6 +188,7 @@ namespace N_m3u8DL_RE.CommandLine
                     DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount),
                     BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
                     MuxImports = bindingContext.ParseResult.GetValueForOption(MuxImports),
+                    ConcurrentDownload = bindingContext.ParseResult.GetValueForOption(ConcurrentDownload),
                 };
 
 
@@ -218,10 +220,10 @@ namespace N_m3u8DL_RE.CommandLine
 
         public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
         {
-            var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220825")
+            var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220826")
             {
                 Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
-                BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
+                BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
                 FFmpegBinaryPath,
                 LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
                 MuxAfterDone, MuxImports
diff --git a/src/N_m3u8DL-RE/CommandLine/MyOption.cs b/src/N_m3u8DL-RE/CommandLine/MyOption.cs
index 13ef95f..1403f3b 100644
--- a/src/N_m3u8DL-RE/CommandLine/MyOption.cs
+++ b/src/N_m3u8DL-RE/CommandLine/MyOption.cs
@@ -103,6 +103,10 @@ namespace N_m3u8DL_RE.CommandLine
         /// </summary>
         public bool UseMkvmerge { get; set; }
         /// <summary>
+        /// See: <see cref="CommandInvoker.ConcurrentDownload"/>.
+        /// </summary>
+        public bool ConcurrentDownload { get; set; }
+        /// <summary>
         /// See: <see cref="CommandInvoker.SubtitleFormat"/>.
         /// </summary>
         public SubtitleFormat SubtitleFormat { get; set; }
diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
index 6b42ad5..957e22c 100644
--- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
+++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
@@ -110,6 +110,7 @@ namespace N_m3u8DL_RE.DownloadManager
             }
         }
 
+
         private async Task<bool> DownloadStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer)
         {
             speedContainer.ResetVars();
@@ -549,7 +550,7 @@ namespace N_m3u8DL_RE.DownloadManager
 
         public async Task<bool> StartDownloadAsync(IEnumerable<StreamSpec> streamSpecs)
         {
-            SpeedContainer speedContainer = new SpeedContainer(); //速度计算
+            ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); //速度计算
             ConcurrentDictionary<StreamSpec, bool?> Results = new();
 
             var progress = AnsiConsole.Progress().AutoClear(true);
@@ -560,7 +561,7 @@ namespace N_m3u8DL_RE.DownloadManager
                 new TaskDescriptionColumn() { Alignment = Justify.Left },
                 new ProgressBarColumn(),
                 new PercentageColumn(),
-                new DownloadSpeedColumn(speedContainer), //速度计算
+                new DownloadSpeedColumn(SpeedContainerDic), //速度计算
                 new RemainingTimeColumn(),
                 new SpinnerColumn(),
             });
@@ -571,14 +572,29 @@ namespace N_m3u8DL_RE.DownloadManager
                 var dic = streamSpecs.Select(item =>
                 {
                     var task = ctx.AddTask(item.ToShortString(), autoStart: false);
+                    SpeedContainerDic[task.Id] = new SpeedContainer(); //速度计算
                     return (item, task);
                 }).ToDictionary(item => item.item, item => item.task);
-                //遍历,顺序下载
-                foreach (var kp in dic)
+
+                if (!DownloaderConfig.MyOptions.ConcurrentDownload)
                 {
-                    var task = kp.Value;
-                    var result = await DownloadStreamAsync(kp.Key, task, speedContainer);
-                    Results[kp.Key] = result;
+                    //遍历,顺序下载
+                    foreach (var kp in dic)
+                    {
+                        var task = kp.Value;
+                        var result = await DownloadStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
+                        Results[kp.Key] = result;
+                    }
+                }
+                else
+                {
+                    //并发下载
+                    await Parallel.ForEachAsync(dic, async (kp, _) =>
+                    {
+                        var task = kp.Value;
+                        var result = await DownloadStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
+                        Results[kp.Key] = result;
+                    });
                 }
             });
 
diff --git a/src/N_m3u8DL-RE/Util/MergeUtil.cs b/src/N_m3u8DL-RE/Util/MergeUtil.cs
index 1dcf9e5..53ff963 100644
--- a/src/N_m3u8DL-RE/Util/MergeUtil.cs
+++ b/src/N_m3u8DL-RE/Util/MergeUtil.cs
@@ -238,6 +238,10 @@ namespace N_m3u8DL_RE.Util
                     break;
                 }
             }
+            //有的播放器不识别zho,统一转为chi
+            if (outputFile.LangCode == "zho") outputFile.LangCode = "chi";
+            if (outputFile.LangCode == "cmn") outputFile.LangCode = "chi";
+            if (outputFile.LangCode == "yue") outputFile.LangCode = "chi";
         }
     }
 }