新增支持并发下载
This commit is contained in:
parent
5f179e4694
commit
8eee867cff
|
@ -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"); }
|
||||
|
|
|
@ -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" +
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue