Add download speed limit
This commit is contained in:
parent
0ed5062683
commit
9ec877999e
@ -41,6 +41,10 @@ namespace AaxDecrypter
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool IsCancelled => _cancellationSource.IsCancellationRequested;
|
public bool IsCancelled => _cancellationSource.IsCancellationRequested;
|
||||||
|
|
||||||
|
private static long _globalSpeedLimit = 0;
|
||||||
|
/// <summary>bytes per second</summary>
|
||||||
|
public static long GlobalSpeedLimit { get => _globalSpeedLimit; set => _globalSpeedLimit = value <= 0 ? 0 : Math.Max(value, MIN_BYTES_PER_SECOND); }
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Properties
|
#region Private Properties
|
||||||
@ -61,6 +65,13 @@ namespace AaxDecrypter
|
|||||||
//DATA_FLUSH_SZ bytes are written to the file stream.
|
//DATA_FLUSH_SZ bytes are written to the file stream.
|
||||||
private const int DATA_FLUSH_SZ = 1024 * 1024;
|
private const int DATA_FLUSH_SZ = 1024 * 1024;
|
||||||
|
|
||||||
|
//Number of times per second the download rate is checkd and throttled
|
||||||
|
private const int THROTTLE_FREQUENCY = 8;
|
||||||
|
|
||||||
|
//Minimum throttle rate. The minimum amount of data that can be throttled
|
||||||
|
//on each iteration of the download loop is DOWNLOAD_BUFF_SZ.
|
||||||
|
private const int MIN_BYTES_PER_SECOND = DOWNLOAD_BUFF_SZ * THROTTLE_FREQUENCY;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructor
|
#region Constructor
|
||||||
@ -168,6 +179,8 @@ namespace AaxDecrypter
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
DateTime startTime = DateTime.Now;
|
||||||
|
long bytesReadSinceThrottle = 0;
|
||||||
int bytesRead;
|
int bytesRead;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -185,6 +198,22 @@ namespace AaxDecrypter
|
|||||||
_downloadedPiece.Set();
|
_downloadedPiece.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region throttle
|
||||||
|
|
||||||
|
bytesReadSinceThrottle += bytesRead;
|
||||||
|
|
||||||
|
if (GlobalSpeedLimit >= MIN_BYTES_PER_SECOND && bytesReadSinceThrottle > GlobalSpeedLimit / THROTTLE_FREQUENCY)
|
||||||
|
{
|
||||||
|
var delayMS = (int)(startTime.AddSeconds(1d / THROTTLE_FREQUENCY) - DateTime.Now).TotalMilliseconds;
|
||||||
|
if (delayMS > 0)
|
||||||
|
await Task.Delay(delayMS, _cancellationSource.Token);
|
||||||
|
|
||||||
|
startTime = DateTime.Now;
|
||||||
|
bytesReadSinceThrottle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
} while (downloadPosition < ContentLength && !IsCancelled && bytesRead > 0);
|
} while (downloadPosition < ContentLength && !IsCancelled && bytesRead > 0);
|
||||||
|
|
||||||
WritePosition = downloadPosition;
|
WritePosition = downloadPosition;
|
||||||
@ -195,9 +224,9 @@ namespace AaxDecrypter
|
|||||||
if (WritePosition > ContentLength)
|
if (WritePosition > ContentLength)
|
||||||
throw new WebException($"Downloaded size (0x{WritePosition:X10}) is greater than {nameof(ContentLength)} (0x{ContentLength:X10}).");
|
throw new WebException($"Downloaded size (0x{WritePosition:X10}) is greater than {nameof(ContentLength)} (0x{ContentLength:X10}).");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (TaskCanceledException)
|
||||||
{
|
{
|
||||||
Serilog.Log.Error(ex, "An error was encountered while downloading {Uri}", Uri);
|
Serilog.Log.Information("Download was cancelled");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@ -176,6 +176,9 @@ namespace AppScaffolding
|
|||||||
|
|
||||||
if (!config.Exists(nameof(config.AutoDownloadEpisodes)))
|
if (!config.Exists(nameof(config.AutoDownloadEpisodes)))
|
||||||
config.AutoDownloadEpisodes = false;
|
config.AutoDownloadEpisodes = false;
|
||||||
|
|
||||||
|
if (!config.Exists(nameof(config.DownloadSpeedLimit)))
|
||||||
|
config.DownloadSpeedLimit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Initialize logging. Wire-up events. Run after migration</summary>
|
/// <summary>Initialize logging. Wire-up events. Run after migration</summary>
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
|
using LibationFileManager;
|
||||||
using ReactiveUI;
|
using ReactiveUI;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -25,9 +26,14 @@ namespace LibationAvalonia.ViewModels
|
|||||||
|
|
||||||
public ProcessQueueViewModel()
|
public ProcessQueueViewModel()
|
||||||
{
|
{
|
||||||
|
Logger = LogMe.RegisterForm(this);
|
||||||
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
|
Queue.QueuededCountChanged += Queue_QueuededCountChanged;
|
||||||
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
Queue.CompletedCountChanged += Queue_CompletedCountChanged;
|
||||||
Logger = LogMe.RegisterForm(this);
|
|
||||||
|
if (Design.IsDesignMode)
|
||||||
|
AudibleUtilities.AudibleApiStorage.EnsureAccountsSettingsFileExists();
|
||||||
|
|
||||||
|
SpeedLimit = Configuration.Instance.DownloadSpeedLimit / 1024m / 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int _completedCount;
|
private int _completedCount;
|
||||||
@ -35,6 +41,7 @@ namespace LibationAvalonia.ViewModels
|
|||||||
private int _queuedCount;
|
private int _queuedCount;
|
||||||
private string _runningTime;
|
private string _runningTime;
|
||||||
private bool _progressBarVisible;
|
private bool _progressBarVisible;
|
||||||
|
private decimal _speedLimit;
|
||||||
|
|
||||||
public int CompletedCount { get => _completedCount; private set { this.RaiseAndSetIfChanged(ref _completedCount, value); this.RaisePropertyChanged(nameof(AnyCompleted)); } }
|
public int CompletedCount { get => _completedCount; private set { this.RaiseAndSetIfChanged(ref _completedCount, value); this.RaisePropertyChanged(nameof(AnyCompleted)); } }
|
||||||
public int QueuedCount { get => _queuedCount; private set { this.RaiseAndSetIfChanged(ref _queuedCount, value); this.RaisePropertyChanged(nameof(AnyQueued)); } }
|
public int QueuedCount { get => _queuedCount; private set { this.RaiseAndSetIfChanged(ref _queuedCount, value); this.RaisePropertyChanged(nameof(AnyQueued)); } }
|
||||||
@ -46,6 +53,37 @@ namespace LibationAvalonia.ViewModels
|
|||||||
public bool AnyErrors => ErrorCount > 0;
|
public bool AnyErrors => ErrorCount > 0;
|
||||||
public double Progress => 100d * Queue.Completed.Count / Queue.Count;
|
public double Progress => 100d * Queue.Completed.Count / Queue.Count;
|
||||||
|
|
||||||
|
public decimal SpeedLimit
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _speedLimit;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var newValue = Math.Min(999 * 1024 * 1024, (long)(value * 1024 * 1024));
|
||||||
|
var config = Configuration.Instance;
|
||||||
|
config.DownloadSpeedLimit = newValue;
|
||||||
|
|
||||||
|
_speedLimit
|
||||||
|
= config.DownloadSpeedLimit <= newValue ? value
|
||||||
|
: value == 0.01m ? config.DownloadSpeedLimit / 1024m / 1024
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
config.DownloadSpeedLimit = (long)(_speedLimit * 1024 * 1024);
|
||||||
|
|
||||||
|
SpeedLimitIncrement = _speedLimit > 100 ? 10
|
||||||
|
: _speedLimit > 10 ? 1
|
||||||
|
: _speedLimit > 1 ? 0.1m
|
||||||
|
: 0.01m;
|
||||||
|
|
||||||
|
this.RaisePropertyChanged(nameof(SpeedLimitIncrement));
|
||||||
|
this.RaisePropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public decimal SpeedLimitIncrement { get; private set; }
|
||||||
|
|
||||||
private void Queue_CompletedCountChanged(object sender, int e)
|
private void Queue_CompletedCountChanged(object sender, int e)
|
||||||
{
|
{
|
||||||
int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail);
|
int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail);
|
||||||
|
|||||||
@ -172,7 +172,7 @@
|
|||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Border Grid.Row="2" BorderThickness="1" BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
<Border Grid.Row="2" BorderThickness="1" BorderBrush="{DynamicResource DataGridGridLinesBrush}">
|
||||||
<SplitView IsPaneOpen="{Binding QueueOpen}" DisplayMode="Inline" OpenPaneLength="375" PanePlacement="Right">
|
<SplitView IsPaneOpen="{Binding QueueOpen}" DisplayMode="Inline" OpenPaneLength="400" MinWidth="400" PanePlacement="Right">
|
||||||
|
|
||||||
<!-- Process Queue -->
|
<!-- Process Queue -->
|
||||||
<SplitView.Pane>
|
<SplitView.Pane>
|
||||||
|
|||||||
@ -4,10 +4,15 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:views="clr-namespace:LibationAvalonia.Views"
|
xmlns:views="clr-namespace:LibationAvalonia.Views"
|
||||||
|
xmlns:viewModels="clr-namespace:LibationAvalonia.ViewModels"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="850"
|
mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="850"
|
||||||
x:Class="LibationAvalonia.Views.ProcessQueueControl">
|
x:Class="LibationAvalonia.Views.ProcessQueueControl">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<views:DecimalConverter x:Key="myConverter" />
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<RecyclePool x:Key="RecyclePool" />
|
<RecyclePool x:Key="RecyclePool" />
|
||||||
<DataTemplate x:Key="queuedBook">
|
<DataTemplate x:Key="queuedBook">
|
||||||
@ -32,25 +37,43 @@
|
|||||||
<TabItem.Header>
|
<TabItem.Header>
|
||||||
<TextBlock FontSize="14" VerticalAlignment="Center">Process Queue</TextBlock>
|
<TextBlock FontSize="14" VerticalAlignment="Center">Process Queue</TextBlock>
|
||||||
</TabItem.Header>
|
</TabItem.Header>
|
||||||
<Grid ColumnDefinitions="*" RowDefinitions="*,40">
|
<Grid Background="AliceBlue" ColumnDefinitions="*" RowDefinitions="*,40">
|
||||||
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource DataGridGridLinesBrush}" Background="WhiteSmoke">
|
<Border Grid.Column="0" Grid.Row="0" BorderThickness="1" BorderBrush="{DynamicResource DataGridGridLinesBrush}" Background="WhiteSmoke">
|
||||||
<ScrollViewer
|
<ScrollViewer
|
||||||
Name="scroller"
|
Name="scroller"
|
||||||
HorizontalScrollBarVisibility="Disabled"
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
VerticalScrollBarVisibility="Auto">
|
VerticalScrollBarVisibility="Auto">
|
||||||
<ItemsRepeater IsVisible="True"
|
<ItemsRepeater IsVisible="True"
|
||||||
Grid.Column="0"
|
Grid.Column="0"
|
||||||
Name="repeater"
|
Name="repeater"
|
||||||
VerticalCacheLength="1.2"
|
VerticalCacheLength="1.2"
|
||||||
HorizontalCacheLength="1"
|
HorizontalCacheLength="1"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
Items="{Binding Items}"
|
Items="{Binding Items}"
|
||||||
ItemTemplate="{StaticResource elementFactory}" />
|
ItemTemplate="{StaticResource elementFactory}" />
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
<Grid Grid.Column="0" Grid.Row="1" ColumnDefinitions="*,Auto" Margin="6,0,6,0">
|
<Grid Grid.Column="0" Grid.Row="1" ColumnDefinitions="*,Auto,Auto">
|
||||||
<Button Grid.Column="0" FontSize="12" HorizontalAlignment="Left" Click="CancelAllBtn_Click">Cancel All</Button>
|
<Button Name="cancelAllBtn" Grid.Column="0" FontSize="12" HorizontalAlignment="Left" Click="CancelAllBtn_Click">Cancel All</Button>
|
||||||
<Button Grid.Column="1" FontSize="12" HorizontalAlignment="Right" Click="ClearFinishedBtn_Click">Clear Finished</Button>
|
<StackPanel Orientation="Horizontal" Grid.Column="1" Margin="0,0,10,0" >
|
||||||
|
<StackPanel.Styles>
|
||||||
|
<Style Selector="NumericUpDown#PART_Spinner">
|
||||||
|
<Setter Property="Background" Value="Black"/>
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Styles>
|
||||||
|
<TextBlock Margin="0,0,6,0" FontSize="11" Text="DL
Limit" VerticalAlignment="Center" />
|
||||||
|
<NumericUpDown
|
||||||
|
FontSize="12"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
TextConverter="{StaticResource myConverter}"
|
||||||
|
Height="{Binding #cancelAllBtn.DesiredSize.Height}"
|
||||||
|
Value="{Binding SpeedLimit, Mode=TwoWay}"
|
||||||
|
Minimum="0"
|
||||||
|
KeyDown="NumericUpDown_KeyDown"
|
||||||
|
Increment="{Binding SpeedLimitIncrement}"
|
||||||
|
Maximum="999" />
|
||||||
|
</StackPanel>
|
||||||
|
<Button Grid.Column="2" FontSize="12" HorizontalAlignment="Right" Click="ClearFinishedBtn_Click">Clear Finished</Button>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Data.Converters;
|
||||||
using Avalonia.Markup.Xaml;
|
using Avalonia.Markup.Xaml;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using LibationAvalonia.ViewModels;
|
using LibationAvalonia.ViewModels;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationAvalonia.Views
|
namespace LibationAvalonia.Views
|
||||||
@ -86,6 +88,11 @@ namespace LibationAvalonia.Views
|
|||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void NumericUpDown_KeyDown(object sender, Avalonia.Input.KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Avalonia.Input.Key.Enter && sender is Avalonia.Input.IInputElement input) input.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
@ -148,4 +155,41 @@ namespace LibationAvalonia.Views
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DecimalConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public static readonly DecimalConverter Instance = new();
|
||||||
|
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is string sourceText && targetType.IsAssignableTo(typeof(decimal?)))
|
||||||
|
{
|
||||||
|
if (sourceText == "∞") return 0;
|
||||||
|
|
||||||
|
for (int i = sourceText.Length; i > 0; i--)
|
||||||
|
{
|
||||||
|
if (decimal.TryParse(sourceText[..i], out var val))
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is decimal val)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
val == 0 ? "∞"
|
||||||
|
: (
|
||||||
|
val >= 10 ? ((long)val).ToString()
|
||||||
|
: val >= 1 ? val.ToString("F1")
|
||||||
|
: val.ToString("F2")
|
||||||
|
) + " MB/s";
|
||||||
|
}
|
||||||
|
return value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -274,9 +274,24 @@ namespace LibationFileManager
|
|||||||
set => persistentDictionary.SetNonString(nameof(SavePodcastsToParentFolder), value);
|
set => persistentDictionary.SetNonString(nameof(SavePodcastsToParentFolder), value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region templates: custom file naming
|
[Description("Global download speed limit in bytes per second.")]
|
||||||
|
public long DownloadSpeedLimit
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
AaxDecrypter.NetworkFileStream.GlobalSpeedLimit = persistentDictionary.GetNonString<long>(nameof(DownloadSpeedLimit));
|
||||||
|
return AaxDecrypter.NetworkFileStream.GlobalSpeedLimit;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
AaxDecrypter.NetworkFileStream.GlobalSpeedLimit = value;
|
||||||
|
persistentDictionary.SetNonString(nameof(DownloadSpeedLimit), AaxDecrypter.NetworkFileStream.GlobalSpeedLimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Description("Edit how filename characters are replaced")]
|
#region templates: custom file naming
|
||||||
|
|
||||||
|
[Description("Edit how filename characters are replaced")]
|
||||||
public ReplacementCharacters ReplacementCharacters
|
public ReplacementCharacters ReplacementCharacters
|
||||||
{
|
{
|
||||||
get => persistentDictionary.GetNonString<ReplacementCharacters>(nameof(ReplacementCharacters));
|
get => persistentDictionary.GetNonString<ReplacementCharacters>(nameof(ReplacementCharacters));
|
||||||
|
|||||||
@ -15,6 +15,7 @@ namespace LibationWinForms
|
|||||||
private void Configure_ProcessQueue()
|
private void Configure_ProcessQueue()
|
||||||
{
|
{
|
||||||
processBookQueue1.popoutBtn.Click += ProcessBookQueue1_PopOut;
|
processBookQueue1.popoutBtn.Click += ProcessBookQueue1_PopOut;
|
||||||
|
splitContainer1.Panel2MinSize = 350;
|
||||||
var coppalseState = Configuration.Instance.GetNonString<bool>(nameof(splitContainer1.Panel2Collapsed));
|
var coppalseState = Configuration.Instance.GetNonString<bool>(nameof(splitContainer1.Panel2Collapsed));
|
||||||
WidthChange = splitContainer1.Panel2.Width + splitContainer1.SplitterWidth;
|
WidthChange = splitContainer1.Panel2.Width + splitContainer1.SplitterWidth;
|
||||||
int width = this.Width;
|
int width = this.Width;
|
||||||
|
|||||||
@ -31,6 +31,8 @@ namespace LibationWinForms.GridView
|
|||||||
public override Type EditType => typeof(MyRatingCellEditor);
|
public override Type EditType => typeof(MyRatingCellEditor);
|
||||||
public override Type ValueType => typeof(Rating);
|
public override Type ValueType => typeof(Rating);
|
||||||
|
|
||||||
|
public MyRatingGridViewCell() { ToolTipText = "Click to change ratings"; }
|
||||||
|
|
||||||
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
|
public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
|
||||||
{
|
{
|
||||||
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
|
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
|
||||||
@ -43,7 +45,7 @@ namespace LibationWinForms.GridView
|
|||||||
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||||
{
|
{
|
||||||
if (value is Rating rating)
|
if (value is Rating rating)
|
||||||
{
|
{
|
||||||
ToolTipText = "Click to change ratings";
|
ToolTipText = "Click to change ratings";
|
||||||
|
|
||||||
var starString = rating.ToStarString();
|
var starString = rating.ToStarString();
|
||||||
|
|||||||
@ -43,6 +43,8 @@
|
|||||||
this.panel3 = new System.Windows.Forms.Panel();
|
this.panel3 = new System.Windows.Forms.Panel();
|
||||||
this.virtualFlowControl2 = new LibationWinForms.ProcessQueue.VirtualFlowControl();
|
this.virtualFlowControl2 = new LibationWinForms.ProcessQueue.VirtualFlowControl();
|
||||||
this.panel1 = new System.Windows.Forms.Panel();
|
this.panel1 = new System.Windows.Forms.Panel();
|
||||||
|
this.label1 = new System.Windows.Forms.Label();
|
||||||
|
this.numericUpDown1 = new LibationWinForms.ProcessQueue.NumericUpDownSuffix();
|
||||||
this.btnCleanFinished = new System.Windows.Forms.Button();
|
this.btnCleanFinished = new System.Windows.Forms.Button();
|
||||||
this.cancelAllBtn = new System.Windows.Forms.Button();
|
this.cancelAllBtn = new System.Windows.Forms.Button();
|
||||||
this.tabPage2 = new System.Windows.Forms.TabPage();
|
this.tabPage2 = new System.Windows.Forms.TabPage();
|
||||||
@ -58,6 +60,7 @@
|
|||||||
this.tabControl1.SuspendLayout();
|
this.tabControl1.SuspendLayout();
|
||||||
this.tabPage1.SuspendLayout();
|
this.tabPage1.SuspendLayout();
|
||||||
this.panel1.SuspendLayout();
|
this.panel1.SuspendLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).BeginInit();
|
||||||
this.tabPage2.SuspendLayout();
|
this.tabPage2.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.logDGV)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.logDGV)).BeginInit();
|
||||||
this.panel2.SuspendLayout();
|
this.panel2.SuspendLayout();
|
||||||
@ -166,6 +169,8 @@
|
|||||||
//
|
//
|
||||||
this.panel1.BackColor = System.Drawing.SystemColors.Control;
|
this.panel1.BackColor = System.Drawing.SystemColors.Control;
|
||||||
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
|
||||||
|
this.panel1.Controls.Add(this.label1);
|
||||||
|
this.panel1.Controls.Add(this.numericUpDown1);
|
||||||
this.panel1.Controls.Add(this.btnCleanFinished);
|
this.panel1.Controls.Add(this.btnCleanFinished);
|
||||||
this.panel1.Controls.Add(this.cancelAllBtn);
|
this.panel1.Controls.Add(this.cancelAllBtn);
|
||||||
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
|
||||||
@ -174,6 +179,44 @@
|
|||||||
this.panel1.Size = new System.Drawing.Size(390, 25);
|
this.panel1.Size = new System.Drawing.Size(390, 25);
|
||||||
this.panel1.TabIndex = 2;
|
this.panel1.TabIndex = 2;
|
||||||
//
|
//
|
||||||
|
// label1
|
||||||
|
//
|
||||||
|
this.label1.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||||
|
this.label1.AutoSize = true;
|
||||||
|
this.label1.Location = new System.Drawing.Point(148, 4);
|
||||||
|
this.label1.Name = "label1";
|
||||||
|
this.label1.Size = new System.Drawing.Size(54, 15);
|
||||||
|
this.label1.TabIndex = 5;
|
||||||
|
this.label1.Text = "DL Limit:";
|
||||||
|
//
|
||||||
|
// numericUpDown1
|
||||||
|
//
|
||||||
|
this.numericUpDown1.Anchor = System.Windows.Forms.AnchorStyles.Right;
|
||||||
|
this.numericUpDown1.DecimalPlaces = 1;
|
||||||
|
this.numericUpDown1.Increment = new decimal(new int[] {
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
65536});
|
||||||
|
this.numericUpDown1.Location = new System.Drawing.Point(208, 0);
|
||||||
|
this.numericUpDown1.Maximum = new decimal(new int[] {
|
||||||
|
999,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.numericUpDown1.Name = "numericUpDown1";
|
||||||
|
this.numericUpDown1.Size = new System.Drawing.Size(84, 23);
|
||||||
|
this.numericUpDown1.Suffix = " MB/s";
|
||||||
|
this.numericUpDown1.TabIndex = 4;
|
||||||
|
this.numericUpDown1.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||||
|
this.numericUpDown1.ThousandsSeparator = true;
|
||||||
|
this.numericUpDown1.Value = new decimal(new int[] {
|
||||||
|
999,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.numericUpDown1.ValueChanged += new System.EventHandler(this.numericUpDown1_ValueChanged);
|
||||||
|
//
|
||||||
// btnCleanFinished
|
// btnCleanFinished
|
||||||
//
|
//
|
||||||
this.btnCleanFinished.Dock = System.Windows.Forms.DockStyle.Right;
|
this.btnCleanFinished.Dock = System.Windows.Forms.DockStyle.Right;
|
||||||
@ -305,6 +348,8 @@
|
|||||||
this.tabControl1.ResumeLayout(false);
|
this.tabControl1.ResumeLayout(false);
|
||||||
this.tabPage1.ResumeLayout(false);
|
this.tabPage1.ResumeLayout(false);
|
||||||
this.panel1.ResumeLayout(false);
|
this.panel1.ResumeLayout(false);
|
||||||
|
this.panel1.PerformLayout();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.numericUpDown1)).EndInit();
|
||||||
this.tabPage2.ResumeLayout(false);
|
this.tabPage2.ResumeLayout(false);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.logDGV)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.logDGV)).EndInit();
|
||||||
this.panel2.ResumeLayout(false);
|
this.panel2.ResumeLayout(false);
|
||||||
@ -337,5 +382,7 @@
|
|||||||
private System.Windows.Forms.DataGridViewTextBoxColumn timestampColumn;
|
private System.Windows.Forms.DataGridViewTextBoxColumn timestampColumn;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn logEntryColumn;
|
private System.Windows.Forms.DataGridViewTextBoxColumn logEntryColumn;
|
||||||
private System.Windows.Forms.Button logCopyBtn;
|
private System.Windows.Forms.Button logCopyBtn;
|
||||||
|
private NumericUpDownSuffix numericUpDown1;
|
||||||
|
private System.Windows.Forms.Label label1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
|
using LibationFileManager;
|
||||||
|
|
||||||
namespace LibationWinForms.ProcessQueue
|
namespace LibationWinForms.ProcessQueue
|
||||||
{
|
{
|
||||||
@ -46,6 +48,9 @@ namespace LibationWinForms.ProcessQueue
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
var speedLimitMBps = Configuration.Instance.DownloadSpeedLimit / 1024m / 1024;
|
||||||
|
numericUpDown1.Value = speedLimitMBps > numericUpDown1.Maximum || speedLimitMBps < numericUpDown1.Minimum ? 0 : speedLimitMBps;
|
||||||
|
|
||||||
popoutBtn.DisplayStyle = ToolStripItemDisplayStyle.Text;
|
popoutBtn.DisplayStyle = ToolStripItemDisplayStyle.Text;
|
||||||
popoutBtn.Name = "popoutBtn";
|
popoutBtn.Name = "popoutBtn";
|
||||||
popoutBtn.Text = "Pop Out";
|
popoutBtn.Text = "Pop Out";
|
||||||
@ -424,5 +429,57 @@ This error appears to be caused by a temporary interruption of service that some
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var newValue = (long)(numericUpDown1.Value * 1024 * 1024);
|
||||||
|
|
||||||
|
var config = Configuration.Instance;
|
||||||
|
config.DownloadSpeedLimit = newValue;
|
||||||
|
if (config.DownloadSpeedLimit > newValue)
|
||||||
|
numericUpDown1.Value =
|
||||||
|
numericUpDown1.Value == 0.01m ? config.DownloadSpeedLimit / 1024m / 1024
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
numericUpDown1.Increment =
|
||||||
|
numericUpDown1.Value > 100 ? 10
|
||||||
|
: numericUpDown1.Value > 10 ? 1
|
||||||
|
: numericUpDown1.Value > 1 ? 0.1m
|
||||||
|
: 0.01m;
|
||||||
|
|
||||||
|
numericUpDown1.DecimalPlaces =
|
||||||
|
numericUpDown1.Value >= 10 ? 0
|
||||||
|
: numericUpDown1.Value >= 1 ? 1
|
||||||
|
: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class NumericUpDownSuffix : NumericUpDown
|
||||||
|
{
|
||||||
|
[Description("Suffix displayed after numeric value."), Category("Data")]
|
||||||
|
[Browsable(true)]
|
||||||
|
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||||
|
[DisallowNull]
|
||||||
|
public string Suffix
|
||||||
|
{
|
||||||
|
get => _suffix;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
base.Text = string.IsNullOrEmpty(_suffix) ? base.Text : base.Text.Replace(_suffix, value);
|
||||||
|
_suffix = value;
|
||||||
|
ChangingText = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private string _suffix = string.Empty;
|
||||||
|
public override string Text
|
||||||
|
{
|
||||||
|
get => string.IsNullOrEmpty(Suffix) ? base.Text : base.Text.Replace(Suffix, string.Empty);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (Value == Minimum)
|
||||||
|
base.Text = "∞";
|
||||||
|
else
|
||||||
|
base.Text = value + Suffix;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user