Skip to content

Commit

Permalink
fixed issue #179 Download speed limit on chunks
Browse files Browse the repository at this point in the history
  • Loading branch information
bezzad committed Jan 13, 2025
1 parent 7216e5b commit 6e0d787
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 110 deletions.
21 changes: 16 additions & 5 deletions src/Downloader.Test/UnitTests/DownloadConfigurationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,33 @@

public class DownloadConfigurationTest(ITestOutputHelper output) : BaseTestClass(output)
{
[Fact]
public void MaximumSpeedPerChunkTest()
[Theory]
[InlineData(10, 5, 2)]
[InlineData(10, 2, 2)]
[InlineData(10, 4, 4)]
[InlineData(10, 10, 4)]
[InlineData(10, 10, 10)]
[InlineData(10, 10, 1)]
[InlineData(10, 20, 1)]
[InlineData(10, 20, 2)]
[InlineData(10, 20, 4)]
public void MaximumSpeedPerChunkTest(int chunks, int parallelCount, int activeCount)
{
// arrange
var configuration =
new DownloadConfiguration {
MaximumBytesPerSecond = 10240,
ParallelDownload = true,
ChunkCount = 10
ChunkCount = chunks,
ParallelCount = parallelCount,
ActiveChunks = activeCount
};

// act
var maxSpeed = configuration.MaximumSpeedPerChunk;
var expectedSpeed = configuration.MaximumBytesPerSecond / Math.Max(Math.Min(Math.Min(chunks, parallelCount), activeCount), 1);

// assert
Assert.Equal(configuration.MaximumBytesPerSecond / configuration.ChunkCount, maxSpeed);
Assert.Equal(expectedSpeed, configuration.MaximumSpeedPerChunk);
}

[Fact]
Expand Down
5 changes: 3 additions & 2 deletions src/Downloader/AbstractDownloadService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,16 +449,17 @@ protected void OnChunkDownloadProgressChanged(object sender, DownloadProgressCha
{
if (e.ReceivedBytesSize > Package.TotalFileSize)
Package.TotalFileSize = e.ReceivedBytesSize;

Bandwidth.CalculateSpeed(e.ProgressedByteSize);
Options.ActiveChunks = Options.ParallelCount - ParallelSemaphore.CurrentCount;
var totalProgressArg = new DownloadProgressChangedEventArgs(nameof(DownloadService)) {
TotalBytesToReceive = Package.TotalFileSize,
ReceivedBytesSize = Package.ReceivedBytesSize,
BytesPerSecondSpeed = Bandwidth.Speed,
AverageBytesPerSecondSpeed = Bandwidth.AverageSpeed,
ProgressedByteSize = e.ProgressedByteSize,
ReceivedBytes = e.ReceivedBytes,
ActiveChunks = Options.ParallelCount - ParallelSemaphore.CurrentCount,
ActiveChunks = Options.ActiveChunks
};
Package.SaveProgress = totalProgressArg.ProgressPercentage;
e.ActiveChunks = totalProgressArg.ActiveChunks;
Expand Down
2 changes: 1 addition & 1 deletion src/Downloader/ChunkDownloader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public ChunkDownloader(Chunk chunk, DownloadConfiguration config, ConcurrentStre
private void ConfigurationPropertyChanged(object sender, PropertyChangedEventArgs e)
{
_logger?.LogDebug($"Changed configuration {e.PropertyName} property");
if (e.PropertyName == nameof(_configuration.MaximumBytesPerSecond) &&
if (e.PropertyName is nameof(_configuration.MaximumBytesPerSecond) or nameof(_configuration.ActiveChunks) &&
_sourceStream?.CanRead == true)
{
_sourceStream.BandwidthLimit = _configuration.MaximumSpeedPerChunk;
Expand Down
33 changes: 27 additions & 6 deletions src/Downloader/DownloadConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Downloader;
/// </summary>
public class DownloadConfiguration : ICloneable, INotifyPropertyChanged
{
private int _activeChunks = 1; // number of active chunks
private int _bufferBlockSize = 1024; // usually, hosts support max to 8000 bytes
private int _chunkCount = 1; // file parts to download
private long _maximumBytesPerSecond = ThrottledStream.Infinite; // No-limitation in download speed
Expand All @@ -21,11 +22,18 @@ public class DownloadConfiguration : ICloneable, INotifyPropertyChanged
private bool _rangeDownload; // enable ranged download
private long _rangeLow; // starting byte offset
private long _rangeHigh; // ending byte offset
private bool _clearPackageOnCompletionWithFailure; // Clear package and downloaded data when download completed with failure

private bool
_clearPackageOnCompletionWithFailure; // Clear package and downloaded data when download completed with failure

private long _minimumSizeOfChunking = 512; // minimum size of chunking to download a file in multiple parts
private bool _reserveStorageSpaceBeforeStartingDownload; // Before starting the download, reserve the storage space of the file as file size.
private bool _enableLiveStreaming; // Get on demand downloaded data with ReceivedBytes on downloadProgressChanged event


private bool
_reserveStorageSpaceBeforeStartingDownload; // Before starting the download, reserve the storage space of the file as file size.

private bool
_enableLiveStreaming; // Get on demand downloaded data with ReceivedBytes on downloadProgressChanged event

/// <summary>
/// To bind view models to fire changes in MVVM pattern
/// </summary>
Expand All @@ -41,6 +49,19 @@ protected void OnPropertyChanged([CallerMemberName] string name = null)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}

/// <summary>
/// Gets the number of active chunks.
/// </summary>
public int ActiveChunks
{
get => _activeChunks;
internal set
{
_activeChunks = value;
OnPropertyChanged();
}
}

/// <summary>
/// Gets or sets the stream buffer size which is used for the size of blocks.
/// </summary>
Expand Down Expand Up @@ -98,7 +119,7 @@ public long MaximumBytesPerSecond
/// This property is read-only.
/// </summary>
public long MaximumSpeedPerChunk => ParallelDownload
? MaximumBytesPerSecond / Math.Min(ChunkCount, ParallelCount)
? MaximumBytesPerSecond / Math.Max(Math.Min(Math.Min(ChunkCount, ParallelCount), ActiveChunks), 1)
: MaximumBytesPerSecond;

/// <summary>
Expand Down Expand Up @@ -260,7 +281,7 @@ public long MaximumMemoryBufferBytes
OnPropertyChanged();
}
}

/// <summary>
/// Gets or sets a value indicating whether live-streaming is enabled or not. If it's enabled, get the on-demand downloaded data
/// with ReceivedBytes on the downloadProgressChanged event.
Expand Down
2 changes: 1 addition & 1 deletion src/Samples/Downloader.Sample/Program.Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ private static DownloadConfiguration GetDownloadConfiguration()
return new DownloadConfiguration {
BufferBlockSize = 10240, // usually, hosts support max to 8000 bytes, default values is 8000
ChunkCount = 8, // file parts to download, default value is 1
MaximumBytesPerSecond = 1024 * 1024 * 10, // download speed limited to 10MB/s, default values is zero or unlimited
MaximumBytesPerSecond = 1024 * 1024 * 20, // download speed limited to 20MB/s, default values is zero or unlimited
MaxTryAgainOnFailover = 5, // the maximum number of times to fail
MaximumMemoryBufferBytes = 1024 * 1024 * 500, // release memory buffer after each 500MB
ParallelDownload = true, // download parts of file as parallel or not. Default value is false
Expand Down
8 changes: 0 additions & 8 deletions src/Samples/Downloader.Sample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,6 @@ private static async Task DownloadFile(DownloadItem downloadItem)
Logger = FileLogger.Factory(downloadItem.FolderPath, Path.GetFileName(downloadItem.FileName));

CurrentDownloadConfiguration = GetDownloadConfiguration();
if (downloadItem.Url.Contains(".m3u", StringComparison.OrdinalIgnoreCase))
{
VideoDownloaderHelper helper = new(CurrentDownloadConfiguration.RequestConfiguration.Proxy);
await helper.DownloadM3U8File(downloadItem.Url,
downloadItem.FileName ?? Path.Combine(downloadItem.FolderPath, Path.GetRandomFileName(), ".mp4"));
return;
}

CurrentDownloadService = CreateDownloadService(CurrentDownloadConfiguration, Logger);

if (string.IsNullOrWhiteSpace(downloadItem.FileName))
Expand Down
87 changes: 0 additions & 87 deletions src/Samples/Downloader.Sample/VideoDownloaderHelper.cs

This file was deleted.

0 comments on commit 6e0d787

Please sign in to comment.