新增支持并发下载
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_uiLanguage { get => GetText("cmd_uiLanguage"); }
|
||||||
public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
|
public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
|
||||||
public static string cmd_useShakaPackager { get => GetText("cmd_useShakaPackager"); }
|
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_useMkvmerge { get => GetText("cmd_useMkvmerge"); }
|
||||||
public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
|
public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
|
||||||
public static string cmd_muxToMp4 { get => GetText("cmd_muxToMp4"); }
|
public static string cmd_muxToMp4 { get => GetText("cmd_muxToMp4"); }
|
||||||
|
|
|
@ -244,6 +244,12 @@ namespace N_m3u8DL_RE.Common.Resource
|
||||||
zhTW: "混流时使用mkvmerge替代ffmpeg",
|
zhTW: "混流时使用mkvmerge替代ffmpeg",
|
||||||
enUS: "Use mkvmerge instead of ffmpeg to mux"
|
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
|
["cmd_muxAfterDone"] = new TextContainer
|
||||||
(
|
(
|
||||||
zhCN: "所有工作完成时尝试混流分离的音视频. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
|
zhCN: "所有工作完成时尝试混流分离的音视频. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
|
||||||
|
|
|
@ -3,6 +3,7 @@ using N_m3u8DL_RE.Entity;
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using Spectre.Console.Rendering;
|
using Spectre.Console.Rendering;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -12,37 +13,40 @@ namespace N_m3u8DL_RE.Column
|
||||||
{
|
{
|
||||||
internal sealed class DownloadSpeedColumn : ProgressColumn
|
internal sealed class DownloadSpeedColumn : ProgressColumn
|
||||||
{
|
{
|
||||||
private string DateTimeString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
private ConcurrentDictionary<int, string> DateTimeStringDic = new();
|
||||||
private string Speed = "0Bps";
|
private ConcurrentDictionary<int, string> SpeedDic = new();
|
||||||
protected override bool NoWrap => true;
|
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 Style MyStyle { get; set; } = new Style(foreground: Color.Green);
|
||||||
|
|
||||||
public override IRenderable Render(RenderContext context, ProgressTask task, TimeSpan deltaTime)
|
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 now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
var flag = task.IsFinished || !task.IsStarted;
|
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.MaxValue = (double)speedContainer.ResponseLength;
|
||||||
task.Value = SpeedContainer.RDownloaded;
|
task.Value = speedContainer.RDownloaded;
|
||||||
}
|
}
|
||||||
//一秒汇报一次即可
|
//一秒汇报一次即可
|
||||||
if (DateTimeString != now)
|
if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now)
|
||||||
{
|
{
|
||||||
Speed = FormatFileSize(SpeedContainer.Downloaded);
|
SpeedDic[taskId] = FormatFileSize(speedContainer.Downloaded);
|
||||||
SpeedContainer.Reset();
|
speedContainer.Reset();
|
||||||
DateTimeString = now;
|
|
||||||
}
|
}
|
||||||
|
DateTimeStringDic[taskId] = now;
|
||||||
var style = flag ? Style.Plain : MyStyle;
|
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)
|
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?> 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?> 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<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" };
|
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),
|
DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount),
|
||||||
BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
|
BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
|
||||||
MuxImports = bindingContext.ParseResult.GetValueForOption(MuxImports),
|
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)
|
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,
|
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,
|
FFmpegBinaryPath,
|
||||||
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
|
LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
|
||||||
MuxAfterDone, MuxImports
|
MuxAfterDone, MuxImports
|
||||||
|
|
|
@ -103,6 +103,10 @@ namespace N_m3u8DL_RE.CommandLine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseMkvmerge { get; set; }
|
public bool UseMkvmerge { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// See: <see cref="CommandInvoker.ConcurrentDownload"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool ConcurrentDownload { get; set; }
|
||||||
|
/// <summary>
|
||||||
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
|
/// See: <see cref="CommandInvoker.SubtitleFormat"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SubtitleFormat SubtitleFormat { get; set; }
|
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)
|
private async Task<bool> DownloadStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer)
|
||||||
{
|
{
|
||||||
speedContainer.ResetVars();
|
speedContainer.ResetVars();
|
||||||
|
@ -549,7 +550,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
|
|
||||||
public async Task<bool> StartDownloadAsync(IEnumerable<StreamSpec> streamSpecs)
|
public async Task<bool> StartDownloadAsync(IEnumerable<StreamSpec> streamSpecs)
|
||||||
{
|
{
|
||||||
SpeedContainer speedContainer = new SpeedContainer(); //速度计算
|
ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); //速度计算
|
||||||
ConcurrentDictionary<StreamSpec, bool?> Results = new();
|
ConcurrentDictionary<StreamSpec, bool?> Results = new();
|
||||||
|
|
||||||
var progress = AnsiConsole.Progress().AutoClear(true);
|
var progress = AnsiConsole.Progress().AutoClear(true);
|
||||||
|
@ -560,7 +561,7 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
new TaskDescriptionColumn() { Alignment = Justify.Left },
|
new TaskDescriptionColumn() { Alignment = Justify.Left },
|
||||||
new ProgressBarColumn(),
|
new ProgressBarColumn(),
|
||||||
new PercentageColumn(),
|
new PercentageColumn(),
|
||||||
new DownloadSpeedColumn(speedContainer), //速度计算
|
new DownloadSpeedColumn(SpeedContainerDic), //速度计算
|
||||||
new RemainingTimeColumn(),
|
new RemainingTimeColumn(),
|
||||||
new SpinnerColumn(),
|
new SpinnerColumn(),
|
||||||
});
|
});
|
||||||
|
@ -571,15 +572,30 @@ namespace N_m3u8DL_RE.DownloadManager
|
||||||
var dic = streamSpecs.Select(item =>
|
var dic = streamSpecs.Select(item =>
|
||||||
{
|
{
|
||||||
var task = ctx.AddTask(item.ToShortString(), autoStart: false);
|
var task = ctx.AddTask(item.ToShortString(), autoStart: false);
|
||||||
|
SpeedContainerDic[task.Id] = new SpeedContainer(); //速度计算
|
||||||
return (item, task);
|
return (item, task);
|
||||||
}).ToDictionary(item => item.item, item => item.task);
|
}).ToDictionary(item => item.item, item => item.task);
|
||||||
|
|
||||||
|
if (!DownloaderConfig.MyOptions.ConcurrentDownload)
|
||||||
|
{
|
||||||
//遍历,顺序下载
|
//遍历,顺序下载
|
||||||
foreach (var kp in dic)
|
foreach (var kp in dic)
|
||||||
{
|
{
|
||||||
var task = kp.Value;
|
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;
|
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);
|
var success = Results.Values.All(v => v == true);
|
||||||
|
|
|
@ -238,6 +238,10 @@ namespace N_m3u8DL_RE.Util
|
||||||
break;
|
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