diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs index 5d0da33..fb7e44d 100644 --- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs +++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs @@ -34,6 +34,7 @@ namespace N_m3u8DL_RE.Common.Resource public static string cmd_ffmpegBinaryPath { get => GetText("cmd_ffmpegBinaryPath"); } public static string cmd_mkvmergeBinaryPath { get => GetText("cmd_mkvmergeBinaryPath"); } public static string cmd_baseUrl { get => GetText("cmd_baseUrl"); } + public static string cmd_maxSpeed { get => GetText("cmd_maxSpeed"); } public static string cmd_adKeyword { get => GetText("cmd_adKeyword"); } public static string cmd_moreHelp { get => GetText("cmd_moreHelp"); } public static string cmd_header { get => GetText("cmd_header"); } diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs index af9d31c..5e12a52 100644 --- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs +++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs @@ -136,6 +136,12 @@ namespace N_m3u8DL_RE.Common.Resource zhTW: "設置BaseURL", enUS: "Set BaseURL" ), + ["cmd_maxSpeed"] = new TextContainer + ( + zhCN: "设置限速,单位支持 Mbps 或 Kbps,如:15M 100K", + zhTW: "設置限速,單位支持 Mbps 或 Kbps,如:15M 100K", + enUS: "Set speed limit, Mbps or Kbps, for example: 15M 100K." + ), ["cmd_noDateInfo"] = new TextContainer ( zhCN: "混流时不写入日期信息", diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index a035ef0..154839a 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -18,7 +18,7 @@ namespace N_m3u8DL_RE.CommandLine { internal partial class CommandInvoker { - public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20230728"; + public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20230730"; [GeneratedRegex("((best|worst)\\d*|all)")] private static partial Regex ForStrRegex(); @@ -58,6 +58,7 @@ namespace N_m3u8DL_RE.CommandLine private readonly static Option ConcurrentDownload = new(new string[] { "-mt", "--concurrent-download" }, description: ResString.cmd_concurrentDownload, getDefaultValue: () => false); private readonly static Option NoLog = new(new string[] { "--no-log" }, description: ResString.cmd_noLog, getDefaultValue: () => false); private readonly static Option AdKeywords = new(new string[] { "--ad-keyword" }, description: ResString.cmd_adKeyword) { ArgumentHelpName = "REG" }; + private readonly static Option MaxSpeed = new(new string[] { "-R", "--max-speed" }, description: ResString.cmd_maxSpeed, parseArgument: ParseSpeedLimit) { ArgumentHelpName = "SPEED" }; //代理选项 @@ -102,6 +103,32 @@ namespace N_m3u8DL_RE.CommandLine private readonly static Option DropAudioFilter = new(new string[] { "-da", "--drop-audio" }, description: ResString.cmd_dropAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" }; private readonly static Option DropSubtitleFilter = new(new string[] { "-ds", "--drop-subtitle" }, description: ResString.cmd_dropSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" }; + /// + /// 解析录制直播时长限制 + /// + /// + /// + private static long? ParseSpeedLimit(ArgumentResult result) + { + var input = result.Tokens.First().Value.ToUpper(); + try + { + var reg = new Regex("([\\d\\\\.]+)(M|K)"); + if (!reg.IsMatch(input)) throw new ArgumentException(); + + var number = double.Parse(reg.Match(input).Groups[1].Value); + if (reg.Match(input).Groups[2].Value == "M") + return (long)(number * 1024 * 1024); + else + return (long)(number * 1024); + } + catch (Exception) + { + result.ErrorMessage = "error in parse SpeedLimit: " + input; + return null; + } + } + /// /// 解析用户定义的下载范围 /// @@ -502,6 +529,7 @@ namespace N_m3u8DL_RE.CommandLine NoDateInfo = bindingContext.ParseResult.GetValueForOption(NoDateInfo), NoLog = bindingContext.ParseResult.GetValueForOption(NoLog), AdKeywords = bindingContext.ParseResult.GetValueForOption(AdKeywords), + MaxSpeed = bindingContext.ParseResult.GetValueForOption(MaxSpeed), }; if (bindingContext.ParseResult.HasOption(CustomHLSMethod)) option.CustomHLSMethod = bindingContext.ParseResult.GetValueForOption(CustomHLSMethod); @@ -564,6 +592,7 @@ namespace N_m3u8DL_RE.CommandLine BinaryMerge, DelAfterDone, NoDateInfo, NoLog, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix, FFmpegBinaryPath, LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption, + MaxSpeed, MuxAfterDone, CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, CustomRange, TaskStartAt, LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LivePipeMux, LiveFixVttByAudio, LiveRecordLimit, LiveWaitTime, LiveTakeCount, diff --git a/src/N_m3u8DL-RE/CommandLine/MyOption.cs b/src/N_m3u8DL-RE/CommandLine/MyOption.cs index e4f19c4..3303fd8 100644 --- a/src/N_m3u8DL-RE/CommandLine/MyOption.cs +++ b/src/N_m3u8DL-RE/CommandLine/MyOption.cs @@ -21,6 +21,10 @@ namespace N_m3u8DL_RE.CommandLine /// public string[]? AdKeywords { get; set; } /// + /// See: . + /// + public long? MaxSpeed { get; set; } + /// /// See: . /// public string[]? Keys { get; set; } diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs index 21f272b..33c8a85 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs @@ -662,6 +662,11 @@ namespace N_m3u8DL_RE.DownloadManager var description = item.ToShortShortString(); var task = ctx.AddTask(description, autoStart: false); SpeedContainerDic[task.Id] = new SpeedContainer(); //速度计算 + //限速设置 + if (DownloaderConfig.MyOptions.MaxSpeed != null) + { + SpeedContainerDic[task.Id].SpeedLimit = DownloaderConfig.MyOptions.MaxSpeed.Value; + } return (item, task); }).ToDictionary(item => item.item, item => item.task); diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs index ed051d2..7701642 100644 --- a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs +++ b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs @@ -840,6 +840,11 @@ namespace N_m3u8DL_RE.DownloadManager { var task = ctx.AddTask(item.ToShortShortString(), autoStart: false, maxValue: 0); SpeedContainerDic[task.Id] = new SpeedContainer(); //速度计算 + //限速设置 + if (DownloaderConfig.MyOptions.MaxSpeed != null) + { + SpeedContainerDic[task.Id].SpeedLimit = DownloaderConfig.MyOptions.MaxSpeed.Value; + } LastFileNameDic[task.Id] = ""; RecordLimitReachedDic[task.Id] = false; DateTimeDic[task.Id] = 0L; diff --git a/src/N_m3u8DL-RE/Entity/SpeedContainer.cs b/src/N_m3u8DL-RE/Entity/SpeedContainer.cs index 2445f4f..93ef2d5 100644 --- a/src/N_m3u8DL-RE/Entity/SpeedContainer.cs +++ b/src/N_m3u8DL-RE/Entity/SpeedContainer.cs @@ -13,6 +13,7 @@ namespace N_m3u8DL_RE.Entity { public bool SingleSegment { get; set; } = false; public long NowSpeed { get; set; } = 0L; //当前每秒速度 + public long SpeedLimit { get; set; } = long.MaxValue; //限速设置 public long? ResponseLength { get; set; } public long RDownloaded { get => _Rdownloaded; } private int _zeroSpeedCount = 0; diff --git a/src/N_m3u8DL-RE/Util/DownloadUtil.cs b/src/N_m3u8DL-RE/Util/DownloadUtil.cs index afe6b1d..b0604de 100644 --- a/src/N_m3u8DL-RE/Util/DownloadUtil.cs +++ b/src/N_m3u8DL-RE/Util/DownloadUtil.cs @@ -120,6 +120,11 @@ namespace N_m3u8DL_RE.Util { speedContainer.Add(size); await stream.WriteAsync(buffer, 0, size); + //限速策略 + while (speedContainer.Downloaded > speedContainer.SpeedLimit) + { + await Task.Delay(1); + } } return new DownloadResult()