新增支持并发下载

This commit is contained in:
nilaoda 2022-08-26 22:19:12 +08:00
parent 5f179e4694
commit 8eee867cff
7 changed files with 59 additions and 22 deletions

View File

@ -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"); }

View File

@ -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" +

View File

@ -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)

View File

@ -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

View File

@ -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; }

View File

@ -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,15 +572,30 @@ 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);
if (!DownloaderConfig.MyOptions.ConcurrentDownload)
{
//遍历,顺序下载
foreach (var kp in dic)
{
var task = kp.Value;
var result = await DownloadStreamAsync(kp.Key, task, speedContainer);
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;
});
}
});
var success = Results.Values.All(v => v == true);

View File

@ -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";
}
}
}