Skip to content

Commit

Permalink
支持单文件多线程下载
Browse files Browse the repository at this point in the history
  • Loading branch information
nilaoda committed Jun 1, 2023
1 parent e3c1eea commit d8ed94c
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace N_m3u8DL_RE.CommandLine
{
internal partial class CommandInvoker
{
public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20230412";
public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20230601";

[GeneratedRegex("((best|worst)\\d*|all)")]
private static partial Regex ForStrRegex();
Expand Down
8 changes: 7 additions & 1 deletion src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ private async Task<bool> DownloadStreamAsync(StreamSpec streamSpec, ProgressTask

var segments = streamSpec.Playlist?.MediaParts.SelectMany(m => m.MediaSegments);
if (segments == null || !segments.Any()) return false;
if (segments.Count() == 1) speedContainer.SingleSegment = true;
//单分段尝试切片并行下载
if (segments.Count() == 1)
{
var splitSegments = await LargeSingleFileSplitUtil.SplitUrlAsync(segments.First(), DownloaderConfig.Headers);
if (splitSegments != null) segments = splitSegments;
else speedContainer.SingleSegment = true;
}

var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO;
var dirName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}_{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
Expand Down
122 changes: 122 additions & 0 deletions src/N_m3u8DL-RE/Util/LargeSingleFileSplitUtil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using N_m3u8DL_RE.Common.Entity;
using N_m3u8DL_RE.Common.Log;
using N_m3u8DL_RE.Common.Util;
using NiL.JS.Expressions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace N_m3u8DL_RE.Util
{
internal class LargeSingleFileSplitUtil
{
class Clip
{
public required int index;
public required long from;
public required long to;
}

/// <summary>
/// URL大文件切片处理
/// </summary>
/// <param name="url"></param>
/// <param name="headers"></param>
/// <param name="splitSegments"></param>
/// <returns></returns>
public static async Task<List<MediaSegment>?> SplitUrlAsync(MediaSegment segment, Dictionary<string,string> headers)
{
var url = segment.Url;
if (!await CanSplitAsync(url, headers)) return null;

if (segment.StartRange != null) return null;

long fileSize = await GetFileSizeAsync(url, headers);
if (fileSize == 0) return null;

List<Clip> allClips = GetAllClips(url, fileSize);
var splitSegments = new List<MediaSegment>();
foreach (Clip clip in allClips)
{
splitSegments.Add(new MediaSegment()
{
Index = clip.index,
Url = url,
StartRange = clip.from,
ExpectLength = clip.to == -1 ? null : clip.to - clip.from + 1,
EncryptInfo = segment.EncryptInfo,
});
}

return splitSegments;
}

public static async Task<bool> CanSplitAsync(string url, Dictionary<string, string> headers)
{
try
{
var request = new HttpRequestMessage(HttpMethod.Head, url);
var response = (await HTTPUtil.AppHttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)).EnsureSuccessStatusCode();
bool supportsRangeRequests = response.Headers.Contains("Accept-Ranges");

return supportsRangeRequests;
}
catch (Exception ex)
{
Logger.DebugMarkUp(ex.Message);
return false;
}
}

private static async Task<long> GetFileSizeAsync(string url, Dictionary<string, string> headers)
{
using var httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.RequestUri = new(url);
foreach (var header in headers)
{
httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
var response = (await HTTPUtil.AppHttpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead)).EnsureSuccessStatusCode();
long totalSizeBytes = response.Content.Headers.ContentLength ?? 0;

return totalSizeBytes;
}

//此函数主要是切片下载逻辑
private static List<Clip> GetAllClips(string url, long fileSize)
{
List<Clip> clips = new();
int index = 0;
long counter = 0;
int perSize = 10 * 1024 * 1024;
while (fileSize > 0)
{
Clip c = new()
{
index = index,
from = counter,
to = counter + perSize
};
//没到最后
if (fileSize - perSize > 0)
{
fileSize -= perSize;
counter += perSize + 1;
index++;
clips.Add(c);
}
//已到最后
else
{
c.to = -1;
clips.Add(c);
break;
}
}
return clips;
}
}
}

0 comments on commit d8ed94c

Please sign in to comment.