diff --git a/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs b/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs index 70098db..fa56e2d 100644 --- a/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs +++ b/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs @@ -32,7 +32,7 @@ namespace N_m3u8DL_RE.Common.Util public static readonly HttpClient AppHttpClient = new(HttpClientHandler) { - Timeout = TimeSpan.FromMinutes(2) + Timeout = TimeSpan.FromSeconds(100) }; private static async Task DoGetAsync(string url, Dictionary? headers = null) diff --git a/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs b/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs index c446732..a01edda 100644 --- a/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs +++ b/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs @@ -13,10 +13,11 @@ namespace N_m3u8DL_RE.Column { internal sealed class DownloadSpeedColumn : ProgressColumn { + private long _stopSpeed = 0; private ConcurrentDictionary DateTimeStringDic = new(); private ConcurrentDictionary SpeedDic = new(); protected override bool NoWrap => true; - public ConcurrentDictionary SpeedContainerDic { get; set; } + private ConcurrentDictionary SpeedContainerDic { get; set; } public DownloadSpeedColumn(ConcurrentDictionary SpeedContainerDic) { @@ -38,9 +39,12 @@ namespace N_m3u8DL_RE.Column task.Value = speedContainer.RDownloaded; } //一秒汇报一次即可 - if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now) + if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now) { SpeedDic[taskId] = FormatFileSize(speedContainer.Downloaded); + //速度为0,计数增加 + if (speedContainer.Downloaded <= _stopSpeed) { speedContainer.AddLowSpeedCount(); SpeedDic[taskId] += $"({speedContainer.LowSpeedCount})"; } + else speedContainer.ResetLowSpeedCount(); speedContainer.Reset(); } DateTimeStringDic[taskId] = now; diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs index 9521425..5de0b16 100644 --- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs +++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs @@ -406,7 +406,7 @@ namespace N_m3u8DL_RE.CommandLine Environment.Exit(0); } - var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220922") + var rootCommand = new RootCommand("N_m3u8DL-RE (Beta version) 20220925") { Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount, BinaryMerge, DelAfterDone, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix, diff --git a/src/N_m3u8DL-RE/Entity/SpeedContainer.cs b/src/N_m3u8DL-RE/Entity/SpeedContainer.cs index 66aaa41..bd5a949 100644 --- a/src/N_m3u8DL-RE/Entity/SpeedContainer.cs +++ b/src/N_m3u8DL-RE/Entity/SpeedContainer.cs @@ -14,12 +14,25 @@ namespace N_m3u8DL_RE.Entity public bool SingleSegment { get; set; } = false; public long? ResponseLength { get; set; } public long RDownloaded { get; set; } = 0L; + private int _zeroSpeedCount = 0; + public int LowSpeedCount { get => _zeroSpeedCount; } + public bool ShouldStop { get => LowSpeedCount >= 20; } /////////////////////////////////////////////////// private long _downloaded = 0; public long Downloaded { get => _downloaded; } + public int AddLowSpeedCount() + { + return Interlocked.Add(ref _zeroSpeedCount, 1); + } + + public int ResetLowSpeedCount() + { + return Interlocked.Exchange(ref _zeroSpeedCount, 0); + } + public long Add(long size) { if (SingleSegment) RDownloaded += size; @@ -34,6 +47,7 @@ namespace N_m3u8DL_RE.Entity public void ResetVars() { Reset(); + ResetLowSpeedCount(); SingleSegment = false; ResponseLength = null; RDownloaded = 0L; diff --git a/src/N_m3u8DL-RE/Util/DownloadUtil.cs b/src/N_m3u8DL-RE/Util/DownloadUtil.cs index 70b2dae..9697eb7 100644 --- a/src/N_m3u8DL-RE/Util/DownloadUtil.cs +++ b/src/N_m3u8DL-RE/Util/DownloadUtil.cs @@ -31,37 +31,60 @@ namespace N_m3u8DL_RE.Util } } Logger.Debug(request.Headers.ToString()); - using var response = await AppHttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - if (response.StatusCode == HttpStatusCode.Found || response.StatusCode == HttpStatusCode.Moved) + CancellationTokenSource cancellationTokenSource = new(); //取消下载 + using var watcher = Task.Factory.StartNew(async () => { - HttpResponseHeaders respHeaders = response.Headers; - Logger.Debug(respHeaders.ToString()); - if (respHeaders != null && respHeaders.Location != null) + while (true) { - var redirectedUrl = respHeaders.Location.AbsoluteUri; - return await DownloadToFileAsync(redirectedUrl, path, speedContainer, headers, fromPosition, toPosition); + if (speedContainer == null) break; + if (speedContainer.ShouldStop) + { + cancellationTokenSource.Cancel(); + speedContainer.ResetLowSpeedCount(); + Logger.WarnMarkUp("Cancel..."); + break; + } + await Task.Delay(500); } - } - response.EnsureSuccessStatusCode(); - var contentLength = response.Content.Headers.ContentLength; - if (speedContainer.SingleSegment) speedContainer.ResponseLength = contentLength; - - using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); - using var responseStream = await response.Content.ReadAsStreamAsync(); - var buffer = new byte[16 * 1024]; - var size = 0; - while ((size = await responseStream.ReadAsync(buffer)) > 0) + }); + try { - speedContainer.Add(size); - await stream.WriteAsync(buffer, 0, size); - } + using var response = await AppHttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token); + if (response.StatusCode == HttpStatusCode.Found || response.StatusCode == HttpStatusCode.Moved) + { + HttpResponseHeaders respHeaders = response.Headers; + Logger.Debug(respHeaders.ToString()); + if (respHeaders != null && respHeaders.Location != null) + { + var redirectedUrl = respHeaders.Location.AbsoluteUri; + return await DownloadToFileAsync(redirectedUrl, path, speedContainer, headers, fromPosition, toPosition); + } + } + response.EnsureSuccessStatusCode(); + var contentLength = response.Content.Headers.ContentLength; + if (speedContainer.SingleSegment) speedContainer.ResponseLength = contentLength; - return new DownloadResult() + using var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None); + using var responseStream = await response.Content.ReadAsStreamAsync(cancellationTokenSource.Token); + var buffer = new byte[16 * 1024]; + var size = 0; + while ((size = await responseStream.ReadAsync(buffer, cancellationTokenSource.Token)) > 0) + { + speedContainer.Add(size); + await stream.WriteAsync(buffer, 0, size); + } + + return new DownloadResult() + { + ActualContentLength = stream.Length, + RespContentLength = contentLength, + ActualFilePath = path + }; + } + catch (OperationCanceledException oce) when (oce.CancellationToken == cancellationTokenSource.Token) { - ActualContentLength = stream.Length, - RespContentLength = contentLength, - ActualFilePath = path - }; + throw new Exception("Download speed too slow!"); + } } } }