Archived
1
0

Basic visual interactions. Torrent client (non-functional)

This commit is contained in:
Michael Gordeev
2020-05-09 17:11:43 +03:00
parent e292bb15d8
commit 1b0c195452
18 changed files with 701 additions and 570 deletions
+4
View File
@@ -7,16 +7,19 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTorrent", "WinTorrent\Wi
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|ARM = Debug|ARM Debug|ARM = Debug|ARM
Debug|ARM64 = Debug|ARM64 Debug|ARM64 = Debug|ARM64
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
Debug|x86 = Debug|x86 Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|ARM = Release|ARM Release|ARM = Release|ARM
Release|ARM64 = Release|ARM64 Release|ARM64 = Release|ARM64
Release|x64 = Release|x64 Release|x64 = Release|x64
Release|x86 = Release|x86 Release|x86 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|Any CPU.ActiveCfg = Debug|x86
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|ARM.ActiveCfg = Debug|ARM {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|ARM.ActiveCfg = Debug|ARM
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|ARM.Build.0 = Debug|ARM {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|ARM.Build.0 = Debug|ARM
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|ARM.Deploy.0 = Debug|ARM {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|ARM.Deploy.0 = Debug|ARM
@@ -29,6 +32,7 @@ Global
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|x86.ActiveCfg = Debug|x86 {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|x86.ActiveCfg = Debug|x86
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|x86.Build.0 = Debug|x86 {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|x86.Build.0 = Debug|x86
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|x86.Deploy.0 = Debug|x86 {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Debug|x86.Deploy.0 = Debug|x86
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|Any CPU.ActiveCfg = Release|x86
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|ARM.ActiveCfg = Release|ARM {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|ARM.ActiveCfg = Release|ARM
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|ARM.Build.0 = Release|ARM {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|ARM.Build.0 = Release|ARM
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|ARM.Deploy.0 = Release|ARM {88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|ARM.Deploy.0 = Release|ARM
@@ -2,16 +2,38 @@
x:Class="WinTorrent.Dialogs.AddTorrentDialog" x:Class="WinTorrent.Dialogs.AddTorrentDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinTorrent.Dialogs"
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"
mc:Ignorable="d" mc:Ignorable="d"
Title="TITLE" Title="Add new torrent"
PrimaryButtonText="Button1" PrimaryButtonText="Add"
SecondaryButtonText="Button2" CloseButtonText="Cancel"
DefaultButton="Primary"
IsPrimaryButtonEnabled="False"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick" PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
SecondaryButtonClick="ContentDialog_SecondaryButtonClick"> Loaded="ContentDialog_Loaded">
<Grid> <StackPanel Width="400">
<RadioButton GroupName="source" Content="From file" IsChecked="True" Tag="file" x:Name="selectFile"/>
<Grid ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox IsReadOnly="True" PlaceholderText=".torrent file path" x:Name="filePathField"/>
<Button Grid.Column="1" Content="Select file" x:Name="filePickerButton" Click="FilePickerButton_Click"/>
</Grid> </Grid>
<RadioButton GroupName="source" Content="From URL" Margin="0,10,0,0" Tag="url" x:Name="selectUrl" IsEnabled="False"/>
<TextBox PlaceholderText="Insert magnet URL" IsEnabled="False" x:Name="urlField" />
<Grid ColumnSpacing="10" Margin="0,20,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox IsReadOnly="True" PlaceholderText=".torrent file path" x:Name="destinationPath"/>
<Button Grid.Column="1" Content="Pick a folder" x:Name="savePickerButton" Click="SavePickerButton_Click"/>
</Grid>
</StackPanel>
</ContentDialog> </ContentDialog>
@@ -1,35 +1,121 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives; using Windows.Storage;
using Windows.UI.Xaml.Data; using Windows.Storage.Pickers;
using Windows.UI.Xaml.Input; using WinTorrent.Utils;
using Windows.UI.Xaml.Media; using MonoTorrent;
using Windows.UI.Xaml.Navigation; using System.Security.Cryptography;
// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace WinTorrent.Dialogs namespace WinTorrent.Dialogs
{ {
public sealed partial class AddTorrentDialog : ContentDialog public sealed partial class AddTorrentDialog : ContentDialog
{ {
private StorageFile File { get; set; }
private Uri MagnetUrl { get; set; }
public StorageFolder DestinationFolder { get; set; }
public Torrent Torrent { get; private set; }
public AddTorrentDialog() public AddTorrentDialog()
{ {
this.InitializeComponent(); InitializeComponent();
RequestedTheme = Settings.Theme switch
{
0 => ElementTheme.Light,
1 => ElementTheme.Dark,
_ => ElementTheme.Default
};
} }
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) private void RadioButton_Checked(object sender, RoutedEventArgs e)
{ {
bool useFile = ((RadioButton)sender).Tag as string == "file";
filePathField.IsEnabled = useFile;
filePickerButton.IsEnabled = useFile;
urlField.IsEnabled = !useFile;
if (useFile)
IsPrimaryButtonEnabled = File != null;
else
IsPrimaryButtonEnabled = MagnetUrl != null;
IsPrimaryButtonEnabled = DestinationFolder == null ? false : IsPrimaryButtonEnabled;
} }
private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args) private async void FilePickerButton_Click(object sender, RoutedEventArgs e)
{ {
FileOpenPicker picker = new FileOpenPicker
{
CommitButtonText = "Open",
SuggestedStartLocation = PickerLocationId.Downloads,
};
picker.FileTypeFilter.Add(".torrent");
File = await picker.PickSingleFileAsync();
if (File == null)
return;
filePathField.Text = File.Path;
IsPrimaryButtonEnabled = DestinationFolder != null;
}
private async void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
var defferal = args.GetDeferral();
try
{
if (selectFile.IsChecked.Value)
{
StorageFile cachedFile = await File.CopyAsync(ApplicationData.Current.LocalFolder, File.Name, NameCollisionOption.GenerateUniqueName);
Torrent = await Torrent.LoadAsync(cachedFile.Path);
}
else
Torrent = await Torrent.LoadAsync(MagnetUrl, ApplicationData.Current.LocalFolder.Path);
}
catch (Exception e)
{
args.Cancel = true;
await new ContentDialog
{
Title = "Something went wrong...",
Content = "We can't load information for source. It may be corrupted or unavailable." +
"\n" + e.GetType() +
"\n" + e.Message,
PrimaryButtonText = "Close",
DefaultButton = ContentDialogButton.Primary
}.ShowAsync();
}
defferal.Complete();
}
private void ContentDialog_Loaded(object sender, RoutedEventArgs e)
{
selectFile.Checked += RadioButton_Checked;
selectUrl.Checked += RadioButton_Checked;
}
private async void SavePickerButton_Click(object sender, RoutedEventArgs e)
{
FolderPicker folderPicker = new FolderPicker
{
SuggestedStartLocation = PickerLocationId.Downloads,
CommitButtonText = "Choose folder"
};
folderPicker.FileTypeFilter.Add("*");
DestinationFolder = await folderPicker.PickSingleFolderAsync();
if (DestinationFolder == null)
return;
destinationPath.Text = DestinationFolder.Path;
IsPrimaryButtonEnabled = MagnetUrl != null || File != null;
} }
} }
} }
-63
View File
@@ -1,63 +0,0 @@
using System;
using System.Collections.Generic;
namespace WinTorrent.Models
{
public struct DataSize : IComparable<DataSize>, IEquatable<DataSize>, IComparer<DataSize>
{
public long ByteSize { get; set; }
public DataSize(long sizeInBytes) =>
ByteSize = sizeInBytes;
public override string ToString() =>
$"{ByteSize} B";
public int CompareTo(DataSize other) =>
(int)Math.Clamp(ByteSize - other.ByteSize, -1, 1);
public bool Equals(DataSize other) =>
ByteSize == other.ByteSize;
public int Compare(DataSize x, DataSize y) =>
(int)Math.Clamp(x.ByteSize - y.ByteSize, -1, 1);
public static double operator /(DataSize s1, DataSize s2) =>
s1.ByteSize / s2.ByteSize;
public static bool operator ==(DataSize s1, DataSize s2) =>
s1.ByteSize == s2.ByteSize;
public static bool operator !=(DataSize s1, DataSize s2) =>
s1.ByteSize != s2.ByteSize;
public static bool operator ==(DataSize s1, int s2) =>
s1.ByteSize == s2;
public static bool operator !=(DataSize s1, int s2) =>
s1.ByteSize != s2;
}
public struct DownloadSpeed : IComparable<DownloadSpeed>, IEquatable<DownloadSpeed>, IComparer<DownloadSpeed>
{
public long BytesPerSecond { get; set; }
public DownloadSpeed(long bps) =>
BytesPerSecond = bps;
public override string ToString() =>
$"{BytesPerSecond} B/s";
public int CompareTo(DownloadSpeed other) =>
(int)Math.Clamp(BytesPerSecond - other.BytesPerSecond, -1, 1);
public bool Equals(DownloadSpeed other) =>
BytesPerSecond == other.BytesPerSecond;
public int Compare(DownloadSpeed x, DownloadSpeed y) =>
(int)Math.Clamp(x.BytesPerSecond - y.BytesPerSecond, -1, 1);
public static double operator /(DownloadSpeed s1, DownloadSpeed s2) =>
s1.BytesPerSecond / s2.BytesPerSecond;
public static bool operator ==(DownloadSpeed s1, DownloadSpeed s2) =>
s1.BytesPerSecond == s2.BytesPerSecond;
public static bool operator !=(DownloadSpeed s1, DownloadSpeed s2) =>
s1.BytesPerSecond != s2.BytesPerSecond;
}
}
@@ -1,4 +1,5 @@
using Windows.UI.Xaml; using MonoTorrent.Client;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using WinTorrent.Pages; using WinTorrent.Pages;
@@ -6,7 +7,7 @@ namespace WinTorrent.Models
{ {
class PointerHoverStateTrigger : StateTriggerBase class PointerHoverStateTrigger : StateTriggerBase
{ {
public TorrentItem TargetItem public TorrentManager TargetItem
{ {
get => _targetItem; get => _targetItem;
set set
@@ -26,7 +27,7 @@ namespace WinTorrent.Models
_targetElement.PointerExited += PointerExited; _targetElement.PointerExited += PointerExited;
} }
} }
private TorrentItem _targetItem; private TorrentManager _targetItem;
private FrameworkElement _targetElement; private FrameworkElement _targetElement;
private void PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) => private void PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) =>
-133
View File
@@ -1,133 +0,0 @@
using System;
using System.ComponentModel;
using Windows.Storage;
using WinTorrent.Utils;
namespace WinTorrent.Models
{
public enum TorrentState
{
Initializing = 0,
Downloading = 1,
Cancelling = 2,
Pausing = 3,
Paused = 4,
Resuming = 5,
Completed = 6,
Seeding = 7,
Error = 8
}
public class TorrentItem : INotifyPropertyChanged
{
public event TorrentStateChangedEventHandler StateChanged;
public event PropertyChangedEventHandler PropertyChanged;
public string Title
{
get => _title;
set
{
if (_title == value)
return;
_title = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Title)));
}
}
private string _title;
public TorrentState State
{
get => _state;
set
{
if (_state == value)
return;
TorrentState previousState = _state;
_state = value;
StateChanged?.Invoke(this, previousState);
TorrentClient.OnItemStateChanged(this, previousState);
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(State)));
}
}
private TorrentState _state = TorrentState.Initializing;
public DownloadSpeed TransmissionSpeed
{
get => _transmissionSpeed;
set
{
if (_transmissionSpeed == value)
return;
_transmissionSpeed = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TransmissionSpeed)));
}
}
private DownloadSpeed _transmissionSpeed;
public DataSize Downloaded
{
get => _downloaded;
set
{
if (_downloaded == value)
return;
_downloaded = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Downloaded)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Progress)));
}
}
private DataSize _downloaded;
public DataSize TotalSize
{
get => _totalSize;
set
{
if (_totalSize == value)
return;
_totalSize = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TotalSize)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Progress)));
}
}
private DataSize _totalSize;
public double Progress => TotalSize != 0 ? ((double)Downloaded.ByteSize / TotalSize.ByteSize) : 0;
public int SeedCount
{
get => _seedCount;
set
{
if (_seedCount == value)
return;
_seedCount = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeedCount)));
}
}
private int _seedCount;
public TimeSpan RemainingTime
{
get => _remainingTime;
set
{
if (_remainingTime == value)
return;
_remainingTime = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SeedCount)));
}
}
private TimeSpan _remainingTime;
public IStorageItem Files { get; set; }
}
}
@@ -0,0 +1,10 @@
namespace WinTorrent.Models
{
public class TorrentMetadataInfo
{
public string TorrentFileName { get; set; }
public string DestinationFolderFALToken { get; set; }
public string TorrentHash { get; set; }
public bool IsActive { get; set; }
}
}
@@ -1,46 +0,0 @@
using Windows.UI.Xaml;
namespace WinTorrent.Models
{
public class TorrentStateDataTrigger : StateTriggerBase
{
public TorrentState TargetState
{
get => _targetState;
set
{
if (_targetState == value)
return;
_targetState = value;
UpdateTrigger();
}
}
private TorrentState _targetState;
public TorrentItem TargetElement
{
get => _targetElement;
set
{
if (_targetElement == value)
return;
if (_targetElement != null)
_targetElement.StateChanged -= TargetElementStateChanged;
_targetElement = value;
_targetElement.StateChanged += TargetElementStateChanged;
UpdateTrigger();
}
}
private TorrentItem _targetElement;
private void UpdateTrigger() =>
SetActive(TargetElement?.State == TargetState);
private void TargetElementStateChanged(TorrentItem sender, TorrentState previousState) =>
UpdateTrigger();
}
}
@@ -0,0 +1,16 @@
using MonoTorrent.Client;
namespace WinTorrent.Models
{
public class TorrentStateValuesEntry
{
public object Value { get; set; }
public TorrentState[] AppliedTo { get; set; }
public TorrentStateValuesEntry(object value, params TorrentState[] states)
{
Value = value;
AppliedTo = states;
}
}
}
+3 -1
View File
@@ -4,7 +4,8 @@
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
IgnorableNamespaces="uap mp"> xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap">
<Identity <Identity
Name="ce54543c-294b-4541-b28d-ae993c9aab5f" Name="ce54543c-294b-4541-b28d-ae993c9aab5f"
@@ -50,6 +51,7 @@
</Applications> </Applications>
<Capabilities> <Capabilities>
<rescap:Capability Name="broadFileSystemAccess" />
<Capability Name="internetClient" /> <Capability Name="internetClient" />
</Capabilities> </Capabilities>
</Package> </Package>
+43 -176
View File
@@ -4,11 +4,16 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
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:models="using:WinTorrent.Models" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converters="using:WinTorrent.ValueConverters"
xmlns:client="using:MonoTorrent.Client"
xmlns:models="using:WinTorrent.Models"
mc:Ignorable="d"> mc:Ignorable="d">
<Page.Resources> <Page.Resources>
<ThemeShadow x:Name="sharedShadow"/> <ThemeShadow x:Name="sharedShadow"/>
<converters:TorrentStateValueConverter x:Name="stateConverter"/>
<converters:EstimatesValueConverter x:Name="etaConverter"/>
</Page.Resources> </Page.Resources>
<Grid> <Grid>
@@ -101,172 +106,13 @@
</ListView.ItemContainerStyle> </ListView.ItemContainerStyle>
<ListView.ItemTemplate> <ListView.ItemTemplate>
<DataTemplate x:DataType="models:TorrentItem"> <DataTemplate x:DataType="client:TorrentManager">
<UserControl> <UserControl>
<Grid> <Grid>
<Grid.Resources>
<SolidColorBrush x:Name="SystemAccentColorBrush" Color="{StaticResource SystemAccentColor}"/>
</Grid.Resources>
<VisualStateManager.VisualStateGroups> <VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="TorentStateGroup">
<VisualState x:Name="Initializing">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Initializing"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Clock"/>
<Setter Target="StatusLabel.Text" Value="Initializing"/>
<Setter Target="SpeedLabel.Visibility" Value="Collapsed"/>
<Setter Target="ProgressLabel.Visibility" Value="Collapsed"/>
<Setter Target="EtaLabel.Visibility" Value="Collapsed"/>
<Setter Target="PauseButton.Visibility" Value="Collapsed"/>
<Setter Target="StatusIcon.Foreground" Value="Orange"/>
<Setter Target="StatusLabel.Foreground" Value="Orange"/>
<Setter Target="ProgressBar.Foreground" Value="Orange"/>
<Setter Target="ProgressBar.IsIndeterminate" Value="True"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Downloading"/>
<VisualState x:Name="Cancelling">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Cancelling"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Clear"/>
<Setter Target="StatusLabel.Text" Value="Cancelling"/>
<Setter Target="SpeedLabel.Visibility" Value="Collapsed"/>
<Setter Target="ProgressLabel.Visibility" Value="Collapsed"/>
<Setter Target="EtaLabel.Visibility" Value="Collapsed"/>
<Setter Target="PauseButton.Visibility" Value="Collapsed"/>
<Setter Target="StatusIcon.Foreground" Value="Orange"/>
<Setter Target="StatusLabel.Foreground" Value="Orange"/>
<Setter Target="ProgressBar.Foreground" Value="Orange"/>
<Setter Target="ProgressBar.IsIndeterminate" Value="True"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Pausing">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Pausing"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Pause"/>
<Setter Target="StatusLabel.Text" Value="Pausing"/>
<Setter Target="PauseButton.Visibility" Value="Collapsed"/>
<Setter Target="StatusIcon.Foreground" Value="Gray"/>
<Setter Target="StatusLabel.Foreground" Value="Gray"/>
<Setter Target="ProgressBar.Foreground" Value="Gray"/>
<Setter Target="ProgressBar.IsIndeterminate" Value="True"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Paused">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Paused"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Pause"/>
<Setter Target="PauseButtonIcon.Symbol" Value="Play"/>
<Setter Target="StatusLabel.Text" Value="Paused"/>
<Setter Target="SpeedLabel.Visibility" Value="Collapsed"/>
<Setter Target="ProgressLabel.Visibility" Value="Collapsed"/>
<Setter Target="EtaLabel.Visibility" Value="Collapsed"/>
<Setter Target="StatusIcon.Foreground" Value="Gray"/>
<Setter Target="StatusLabel.Foreground" Value="Gray"/>
<Setter Target="ProgressBar.Foreground" Value="Gray"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Resuming">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Resuming"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Clock"/>
<Setter Target="StatusLabel.Text" Value="Resuming"/>
<Setter Target="SpeedLabel.Visibility" Value="Collapsed"/>
<Setter Target="ProgressLabel.Visibility" Value="Collapsed"/>
<Setter Target="EtaLabel.Visibility" Value="Collapsed"/>
<Setter Target="PauseButton.Visibility" Value="Collapsed"/>
<Setter Target="ProgressBar.IsIndeterminate" Value="True"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Completed">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Completed"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Accept"/>
<Setter Target="PauseButtonIcon.Symbol" Value="Globe"/>
<Setter Target="PauseButton.(ToolTipService.ToolTip)" Value="Seed torrent"/>
<Setter Target="StatusLabel.Text" Value="Completed"/>
<Setter Target="SpeedLabel.Visibility" Value="Collapsed"/>
<Setter Target="ProgressLabel.Visibility" Value="Collapsed"/>
<Setter Target="EtaLabel.Visibility" Value="Collapsed"/>
<Setter Target="StatusIcon.Foreground" Value="#5b5"/>
<Setter Target="StatusLabel.Foreground" Value="#5b5"/>
<Setter Target="ProgressBar.Foreground" Value="#5b5"/>
<Setter Target="ProgressBar.Value" Value="100"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Seeding">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Seeding"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Upload"/>
<Setter Target="StatusLabel.Text" Value="Seeding"/>
<Setter Target="ProgressLabel.Visibility" Value="Collapsed"/>
<Setter Target="EtaLabel.Visibility" Value="Collapsed"/>
<Setter Target="PauseButton.Visibility" Value="Collapsed"/>
<Setter Target="StatusIcon.Foreground" Value="#5b5"/>
<Setter Target="StatusLabel.Foreground" Value="#5b5"/>
<Setter Target="ProgressBar.Foreground" Value="#5b5"/>
<Setter Target="ProgressBar.Value" Value="1"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Error">
<VisualState.StateTriggers>
<models:TorrentStateDataTrigger TargetElement="{x:Bind}" TargetState="Error"/>
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="StatusIcon.Symbol" Value="Clear"/>
<Setter Target="PauseButtonIcon.Symbol" Value="RepeatAll"/>
<Setter Target="PauseButton.(ToolTipService.ToolTip)" Value="Retry"/>
<Setter Target="StatusLabel.Text" Value="Error"/>
<Setter Target="SpeedLabel.Visibility" Value="Collapsed"/>
<Setter Target="ProgressLabel.Visibility" Value="Collapsed"/>
<Setter Target="EtaLabel.Visibility" Value="Collapsed"/>
<Setter Target="PauseButton.Visibility" Value="Visible"/>
<Setter Target="StatusIcon.Foreground" Value="Red"/>
<Setter Target="StatusLabel.Foreground" Value="Red"/>
<Setter Target="ProgressBar.Foreground" Value="Red"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="CommonStates"> <VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"> <VisualState x:Name="Normal">
<VisualState.Storyboard> <VisualState.Storyboard>
@@ -309,24 +155,45 @@
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.ColumnSpan="4" Text="{Binding Title}" MaxLines="1" TextTrimming="CharacterEllipsis"/> <TextBlock Grid.Row="0" Grid.ColumnSpan="4" Text="{Binding Path=Torrent.Name}" MaxLines="1" TextTrimming="CharacterEllipsis"/>
<SymbolIcon x:Name="StatusIcon" Grid.Row="1" Grid.Column="0" Symbol="Download" Foreground="{StaticResource SystemAccentColor}" /> <FontIcon Grid.Row="1" Grid.Column="0"
<TextBlock x:Name="StatusLabel" Grid.Row="1" Grid.Column="1" Text="Downloading" Foreground="{StaticResource SystemAccentColor}"/> Glyph="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=StatusIcon}"
<TextBlock x:Name="SpeedLabel" Grid.Row="1" Grid.Column="2" Text="{Binding TransmissionSpeed}" Foreground="Gray"/> Foreground="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=AccentColor}" />
<TextBlock x:Name="ProgressLabel" Grid.Row="1" Grid.Column="3" Foreground="Gray">
<Run Text="{Binding Downloaded}"/> of <Run Text="{Binding TotalSize}"/> <TextBlock Grid.Row="1" Grid.Column="1"
</TextBlock> Text="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=StatusLabel}"
<TextBlock x:Name="EtaLabel" Grid.Row="1" Grid.Column="4" Foreground="Gray"> Foreground="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=AccentColor}" />
<Run Text="{Binding RemainingTime}"/> remaining
<TextBlock Grid.Row="1" Grid.Column="2" Foreground="Gray"
Text="{Binding Path=Error.Exception.Message}"
Visibility="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=ErrorVisibility, TargetNullValue=Collapsed}"/>
<TextBlock Grid.Row="1" Grid.Column="2" Foreground="Gray"
Text="{Binding Path=Monitor.DownloadSpeed, TargetNullValue={Binding Path=Monitor.UploadSpeed}}"
Visibility="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=StatsVisibility, TargetNullValue=Collapsed}"/>
<TextBlock Grid.Row="1" Grid.Column="3" Foreground="Gray"
Visibility="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=StatsVisibility, TargetNullValue=Collapsed}">
<Run Text="{Binding Path=Monitor.DataBytesDownloaded}"/> of <Run Text="{Binding Path=Torrent.Size}"/>
</TextBlock> </TextBlock>
<Button x:Name="PauseButton" Grid.Column="5" Grid.RowSpan="2" Background="Transparent" Height="50" Width="50" Opacity="0" Tag="{Binding}" Click="PauseItem"> <TextBlock Grid.Row="1" Grid.Column="4" Foreground="Gray"
<SymbolIcon x:Name="PauseButtonIcon" Symbol="Pause"/> Visibility="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=StatsVisibility, TargetNullValue=Collapsed}">
<Run Text="{Binding Converter={StaticResource etaConverter}}"/> remaining
</TextBlock>
<Button Grid.Column="5" Grid.RowSpan="2" Background="Transparent" Height="50" Width="50" Tag="{Binding}" Opacity="0" Click="PauseItem"
Visibility="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=PauseButtonVisibility, TargetNullValue=Collapsed}"
ToolTipService.ToolTip="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=PauseButtonTooltip}">
<FontIcon Glyph="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=PauseButtonIcon}"/>
</Button> </Button>
</Grid> </Grid>
<ProgressBar x:Name="ProgressBar" Grid.Row="2" Grid.ColumnSpan="4" Height="3" Value="{Binding Progress}" Background="Transparent" Maximum="1" Minimum="0"/> <ProgressBar Grid.Row="2" Grid.ColumnSpan="4" Height="3" Background="Transparent" Maximum="1" Minimum="0"
Value="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=ProgressValue, TargetNullValue={Binding Progress}}"
IsIndeterminate="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=ProgressIndeterminate}"
Foreground="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=AccentColor, TargetNullValue=False}" />
</Grid> </Grid>
</UserControl> </UserControl>
</DataTemplate> </DataTemplate>
+28 -13
View File
@@ -1,10 +1,13 @@
using System; using MonoTorrent.Client;
using System;
using System.IO;
using Windows.Storage;
using Windows.System;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
using WinTorrent.Dialogs; using WinTorrent.Dialogs;
using WinTorrent.Models;
using WinTorrent.Utils; using WinTorrent.Utils;
namespace WinTorrent.Pages namespace WinTorrent.Pages
@@ -19,7 +22,9 @@ namespace WinTorrent.Pages
search.Translation += new System.Numerics.Vector3(0, 0, 10); search.Translation += new System.Numerics.Vector3(0, 0, 10);
navigationList.SelectedIndex = 0; navigationList.SelectedIndex = 0;
TorrentClient.TorrentItemStateChanged += (s, e) => NavigationViewList_SelectionChanged(this, null);
//File.Create("E://FUCKYOU.txt");
//File.WriteAllText("E://FUCKYOU.txt", "FUCK YOU!");
} }
protected override void OnNavigatedTo(NavigationEventArgs e) protected override void OnNavigatedTo(NavigationEventArgs e)
@@ -32,17 +37,27 @@ namespace WinTorrent.Pages
private void OpenSettings(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) => private void OpenSettings(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) =>
Frame.Navigate(typeof(SettingsPage), null, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromLeft }); Frame.Navigate(typeof(SettingsPage), null, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromLeft });
private async void AddTorrent(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) => private async void AddTorrent(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
await new AddTorrentDialog().ShowAsync(); {
AddTorrentDialog addDialog = new AddTorrentDialog();
await addDialog.ShowAsync();
if (addDialog.Torrent == null)
return;
TorrentClient.AddTorrent(addDialog.Torrent, addDialog.DestinationFolder);
// TODO: Configure torrent
}
private void NavigationViewList_SelectionChanged(object sender, SelectionChangedEventArgs e) private void NavigationViewList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
list.ItemsSource = (navigationList.SelectedItem as NavigationViewItem)?.Tag as string switch list.ItemsSource = (navigationList.SelectedItem as NavigationViewItem)?.Tag as string switch
{ {
"downloading" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Downloading, TorrentState.Cancelling, TorrentState.Error, TorrentState.Initializing, TorrentState.Pausing)), "downloading" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Downloading, TorrentState.Hashing, TorrentState.Error, TorrentState.Metadata, TorrentState.Starting, TorrentState.Stopping)),
"seeding" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Seeding)), "seeding" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Seeding)),
"completed" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Completed, TorrentState.Seeding)), "completed" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Stopped, TorrentState.Seeding)),
"paused" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Paused, TorrentState.Resuming)), "paused" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Paused, TorrentState.HashingPaused)),
_ => TorrentClient.Torrents _ => TorrentClient.Torrents
}; };
search.ItemsSource = null; search.ItemsSource = null;
@@ -56,8 +71,8 @@ namespace WinTorrent.Pages
sender.ItemsSource = null; sender.ItemsSource = null;
navigationList.SelectedIndex = 0; navigationList.SelectedIndex = 0;
var result = TorrentClient.Torrents.FindAll(i => var result = TorrentClient.Torrents.FindAll(i =>
i.Title.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) || i.Torrent.Name.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) ||
(i.Files?.Path.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) ?? false) || (i?.SavePath.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) ?? false) ||
i.State.ToString().ToLowerInvariant().Contains(sender.Text.ToLowerInvariant())); i.State.ToString().ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()));
list.ItemsSource = result; list.ItemsSource = result;
@@ -88,12 +103,12 @@ namespace WinTorrent.Pages
} }
private void list_ItemClick(object sender, ItemClickEventArgs e) => private void list_ItemClick(object sender, ItemClickEventArgs e) =>
OpenItem(e.ClickedItem as TorrentItem); OpenItem(e.ClickedItem as TorrentManager);
private void ViewItem(object sender, RoutedEventArgs e) => private void ViewItem(object sender, RoutedEventArgs e) =>
OpenItem(list.ItemFromContainer(((sender as FrameworkElement).Parent as FrameworkElement).Parent) as TorrentItem); OpenItem(list.ItemFromContainer(((sender as FrameworkElement).Parent as FrameworkElement).Parent) as TorrentManager);
private void OpenItem(TorrentItem item) private void OpenItem(TorrentManager item)
{ {
if (list.ContainerFromItem(item) as ListViewItem != null) if (list.ContainerFromItem(item) as ListViewItem != null)
list.PrepareConnectedAnimation("ca1", item, "caTarget"); list.PrepareConnectedAnimation("ca1", item, "caTarget");
@@ -1,4 +1,5 @@
using Windows.UI.Xaml; using MonoTorrent.Client;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation; using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation; using Windows.UI.Xaml.Navigation;
@@ -8,7 +9,7 @@ namespace WinTorrent.Pages
{ {
public sealed partial class TorrentDetailsPage : Page public sealed partial class TorrentDetailsPage : Page
{ {
public TorrentItem Item { get; private set; } public TorrentManager Item { get; private set; }
public TorrentDetailsPage() public TorrentDetailsPage()
{ {
@@ -20,7 +21,7 @@ namespace WinTorrent.Pages
{ {
base.OnNavigatedTo(e); base.OnNavigatedTo(e);
Item = e.Parameter as TorrentItem; Item = e.Parameter as TorrentManager;
if (ConnectedAnimationService.GetForCurrentView().GetAnimation("ca1") is ConnectedAnimation animation) if (ConnectedAnimationService.GetForCurrentView().GetAnimation("ca1") is ConnectedAnimation animation)
animation.TryStart(caTarget); animation.TryStart(caTarget);
+1 -1
View File
@@ -62,7 +62,7 @@ namespace WinTorrent.Utils
public static async Task<StorageFolder> GetDefaultFolder() public static async Task<StorageFolder> GetDefaultFolder()
{ {
if (string.IsNullOrWhiteSpace(DefaultFolder)) if (string.IsNullOrWhiteSpace(DefaultFolder))
return await DownloadsFolder.CreateFolderAsync(Package.Current.DisplayName, CreationCollisionOption.OpenIfExists); return null;
else else
return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(DefaultFolder); return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(DefaultFolder);
} }
+290 -113
View File
@@ -1,140 +1,317 @@
using System; using MonoTorrent;
using MonoTorrent.BEncoding;
using MonoTorrent.Client;
using Newtonsoft.Json;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.Storage.AccessCache;
using Windows.UI.Xaml.Controls;
using WinTorrent.Models; using WinTorrent.Models;
namespace WinTorrent.Utils namespace WinTorrent.Utils
{ {
public delegate void TorrentStateChangedEventHandler(TorrentItem sender, TorrentState previousState);
public static class TorrentClient public static class TorrentClient
{ {
public static event TorrentStateChangedEventHandler TorrentItemStateChanged; private static ApplicationDataContainer LocalSettings { get; } = ApplicationData.Current.LocalSettings;
private static StorageFolder LocalStorage { get; } = ApplicationData.Current.LocalFolder;
public static List<TorrentItem> Torrents { get; } private static ClientEngine Engine { get; set; }
public static EngineSettings Settings => Engine?.Settings;
public static List<TorrentManager> Torrents { get; } = new List<TorrentManager>();
private static BEncodedDictionary FastResumeData { get; set; } = new BEncodedDictionary();
private static List<TorrentMetadataInfo> TorrentsMetadata { get; set; }
static TorrentClient() static TorrentClient()
{ {
Torrents = new List<TorrentItem> #region TestData
//Torrents = new List<TorrentItem>
//{
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Downloading,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Completed,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Paused,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Seeding,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Cancelling,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Initializing,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Pausing,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Resuming,
// SeedCount = 15
// },
// new TorrentItem
// {
// Title = "Microsoft Edge",
// TotalSize = new DataSize(3221225472),
// Downloaded = new DataSize(125829120),
// TransmissionSpeed = new DownloadSpeed(33554432),
// RemainingTime = TimeSpan.FromSeconds(36),
// State = Models.TorrentState.Error,
// SeedCount = 15
// }
//};
#endregion
EngineSettings engineSettings = new EngineSettings
{ {
new TorrentItem SavePath = ApplicationData.Current.LocalFolder.Path,
{ MaximumDownloadSpeed = 0, // Set from settings
Title = "Microsoft Edge", MaximumUploadSpeed = 0
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Downloading,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Completed,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Paused,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Seeding,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Cancelling,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Initializing,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Pausing,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Resuming,
SeedCount = 15
},
new TorrentItem
{
Title = "Microsoft Edge",
TotalSize = new DataSize(3221225472),
Downloaded = new DataSize(125829120),
TransmissionSpeed = new DownloadSpeed(33554432),
RemainingTime = TimeSpan.FromSeconds(36),
State = TorrentState.Error,
SeedCount = 15
}
}; };
Engine = new ClientEngine(engineSettings);
Restore();
} }
public static void AddTorrent() public static async void AddTorrent(Torrent torrent, StorageFolder destinationFolder)
{ {
TorrentMetadataInfo meta = new TorrentMetadataInfo
}
public static void PauseTorrent()
{ {
DestinationFolderFALToken = StorageApplicationPermissions.FutureAccessList.Add(destinationFolder),
TorrentFileName = new FileInfo(torrent.TorrentPath).Name
};
} TorrentManager manager = new TorrentManager(torrent, destinationFolder.Path);
meta.TorrentHash = manager.InfoHash.ToHex();
public static void CancelTorrent() if (TorrentsMetadata.Any(i => i.TorrentHash == meta.TorrentHash))
{ {
ContentDialog alert = new ContentDialog
}
public static void ResumeTorrent()
{ {
Title = "Duplicate torrent",
Content = "You have previously added this torrent to the queue. Do you want to discard it and start a new download?",
PrimaryButtonText = "Yes",
CloseButtonText = "No",
DefaultButton = ContentDialogButton.Close
};
} if (await alert.ShowAsync() == ContentDialogResult.Primary)
public static void SeedTorrent()
{ {
FastResumeData.Remove(meta.TorrentHash);
TorrentsMetadata.RemoveAll(i => i.TorrentHash == meta.TorrentHash);
foreach(TorrentManager t in Torrents.FindAll(i => i.InfoHash.ToHex() == meta.TorrentHash))
{
await t.StopAsync();
await Engine.Unregister(t);
}
Torrents.RemoveAll(i => i.InfoHash.ToHex() == meta.TorrentHash);
}
else
return;
//return null;
} }
public static void OnItemStateChanged(TorrentItem item, TorrentState previousState) => TorrentsMetadata.Add(meta);
TorrentItemStateChanged?.Invoke(item, previousState); SaveData();
if (FastResumeData.ContainsKey(meta.TorrentHash))
manager.LoadFastResume(new FastResume((BEncodedDictionary)FastResumeData[torrent.InfoHash.ToHex()]));
await Engine.Register(manager);
Torrents.Add(manager);
await manager.StartAsync();
//return manager;
}
public static async void Restore()
{
await Engine.EnablePortForwardingAsync(CancellationToken.None);
TorrentsMetadata = JsonConvert.DeserializeObject<List<TorrentMetadataInfo>>(LocalSettings.Values["TorrentsMetadata"] as string ?? "") ?? new List<TorrentMetadataInfo>();
try
{
StorageFile fastResumeFile = await LocalStorage.CreateFileAsync("TorrentFastResume.data", CreationCollisionOption.OpenIfExists);
FastResumeData = BEncodedValue.Decode<BEncodedDictionary>(await fastResumeFile.OpenStreamForReadAsync());
}
catch { }
foreach (TorrentMetadataInfo meta in TorrentsMetadata)
RestoreTorrent(meta);
}
private static async void RestoreTorrent(TorrentMetadataInfo meta)
{
try
{
StorageFile torrentFile = await LocalStorage.GetFileAsync(meta.TorrentFileName);
Torrent torrent = await Torrent.LoadAsync(await torrentFile.OpenStreamForReadAsync());
StorageFolder destinationFolder = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(meta.DestinationFolderFALToken);
TorrentManager manager = new TorrentManager(torrent, destinationFolder.Path);
if (FastResumeData.ContainsKey(meta.TorrentHash))
manager.LoadFastResume(new FastResume((BEncodedDictionary)FastResumeData[meta.TorrentHash]));
await Engine.Register(manager);
Torrents.Add(manager);
if (meta.IsActive)
await manager.StartAsync();
}
catch
{
TorrentsMetadata.Remove(meta);
SaveData();
}
}
public static async Task Suspend()
{
SaveData();
BEncodedDictionary fastResume = new BEncodedDictionary();
foreach (TorrentManager item in Torrents)
{
await item.StopAsync();
if (item.HashChecked)
fastResume.Add(item.InfoHash.ToHex(), item.SaveFastResume().Encode());
}
StorageFile fastResumeFile = await LocalStorage.CreateFileAsync("TorrentFastResume.data", CreationCollisionOption.OpenIfExists);
while (true)
try
{
await File.WriteAllBytesAsync(fastResumeFile.Path, fastResume.Encode());
break;
}
catch { }
Engine.Dispose();
}
private static void SaveData()
{
foreach (TorrentManager item in Torrents)
TorrentsMetadata.Find(i => i.TorrentHash == item.InfoHash.ToHex()).IsActive = !item.State.BelongsTo(TorrentState.Paused, TorrentState.HashingPaused, TorrentState.Error, TorrentState.Stopped, TorrentState.Stopping);
LocalSettings.Values["TorrentsMetadata"] = JsonConvert.SerializeObject(TorrentsMetadata);
}
public static async void PauseTorrent(TorrentManager torrent)
{
await torrent.PauseAsync();
if (!torrent.HashChecked)
return;
if (FastResumeData.ContainsKey(torrent.InfoHash.ToHex()))
FastResumeData[torrent.InfoHash.ToHex()] = torrent.SaveFastResume().Encode();
else
FastResumeData.Add(torrent.InfoHash.ToHex(), torrent.SaveFastResume().Encode());
try
{
StorageFile fastResumeFile = await LocalStorage.CreateFileAsync("TorrentFastResume.data", CreationCollisionOption.OpenIfExists);
await File.WriteAllBytesAsync(fastResumeFile.Path, FastResumeData.Encode());
}
catch { }
}
public static async void RemoveTorrent(TorrentManager torrent, bool removeFiles = false)
{
await torrent.StopAsync();
TorrentMetadataInfo meta = TorrentsMetadata.Find(i => i.TorrentHash == torrent.InfoHash.ToHex());
if (removeFiles)
{
StorageFolder destinationFolder = await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(meta.DestinationFolderFALToken);
foreach (TorrentFile file in torrent.Torrent.Files)
await (await destinationFolder.GetFileAsync(file.Path)).DeleteAsync(StorageDeleteOption.PermanentDelete);
}
StorageApplicationPermissions.FutureAccessList.Remove(meta.DestinationFolderFALToken);
await (await LocalStorage.GetFileAsync(meta.TorrentFileName))?.DeleteAsync(StorageDeleteOption.PermanentDelete);
FastResumeData.Remove(meta.TorrentHash);
TorrentsMetadata.RemoveAll(i => i.TorrentHash == torrent.InfoHash.ToHex());
SaveData();
try
{
StorageFile fastResumeFile = await LocalStorage.CreateFileAsync("TorrentFastResume.data", CreationCollisionOption.OpenIfExists);
await File.WriteAllBytesAsync(fastResumeFile.Path, FastResumeData.Encode());
}
catch { }
}
} }
} }
@@ -0,0 +1,25 @@
using MonoTorrent.Client;
using System;
using Windows.UI.Xaml.Data;
namespace WinTorrent.ValueConverters
{
public class EstimatesValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
TorrentManager item = value as TorrentManager;
try
{
return TimeSpan.FromSeconds((item.Torrent.Size - item.Monitor.DataBytesDownloaded) / item.Monitor.DownloadSpeed);
}
catch(DivideByZeroException)
{
return "";
}
}
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
null;
}
}
@@ -0,0 +1,143 @@
using MonoTorrent;
using MonoTorrent.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Media;
using WinTorrent.Models;
namespace WinTorrent.ValueConverters
{
public class TorrentStateValueConverter : IValueConverter
{
private static Dictionary<string, TorrentStateValuesEntry[]> StateDictionary { get; } = new Dictionary<string, TorrentStateValuesEntry[]>
{
{
"AccentColor",
new []
{
new TorrentStateValuesEntry(new SolidColorBrush(Colors.DeepSkyBlue),
TorrentState.Downloading),
new TorrentStateValuesEntry(new SolidColorBrush(Colors.Orange),
TorrentState.Hashing,
TorrentState.Metadata,
TorrentState.Starting,
TorrentState.Stopping),
new TorrentStateValuesEntry(new SolidColorBrush(Colors.Gray),
TorrentState.HashingPaused,
TorrentState.Paused,
TorrentState.Stopped),
new TorrentStateValuesEntry(new SolidColorBrush(Color.FromArgb(255, 85, 187, 85)),
TorrentState.Seeding),
new TorrentStateValuesEntry(new SolidColorBrush(Colors.Red),
TorrentState.Error),
}
},
{
"ProgressIndeterminate",
new [] { new TorrentStateValuesEntry(true,
TorrentState.Stopping,
TorrentState.Hashing,
TorrentState.Metadata,
TorrentState.Starting) }
},
{
"StatsVisibility",
new [] { new TorrentStateValuesEntry(Visibility.Visible, TorrentState.Downloading) }
},
{
"ErrorVisibility",
new [] { new TorrentStateValuesEntry(Visibility.Visible, TorrentState.Error) }
},
{
"ProgressValue",
new [] { new TorrentStateValuesEntry(1, TorrentState.Error,
TorrentState.Seeding,
TorrentState.Stopped) }
},
{
"PauseButtonVisibility",
new [] { new TorrentStateValuesEntry(Visibility.Visible,
TorrentState.Downloading,
TorrentState.Error,
TorrentState.Paused,
TorrentState.HashingPaused,
TorrentState.Stopped) }
},
{
"PauseButtonTooltip",
new []
{
new TorrentStateValuesEntry("Pause", TorrentState.Downloading),
new TorrentStateValuesEntry("Retry", TorrentState.Error),
new TorrentStateValuesEntry("Resume", TorrentState.Paused, TorrentState.HashingPaused),
new TorrentStateValuesEntry("Start", TorrentState.Stopped)
}
},
{
"PauseButtonIcon",
new []
{
new TorrentStateValuesEntry("\xE103", TorrentState.Downloading),
new TorrentStateValuesEntry("\xE149", TorrentState.Error),
new TorrentStateValuesEntry("\xE102", TorrentState.Paused,
TorrentState.HashingPaused,
TorrentState.Stopped)
}
},
{
"StatusLabel",
new []
{
new TorrentStateValuesEntry("Downloading", TorrentState.Downloading),
new TorrentStateValuesEntry("Error", TorrentState.Error),
new TorrentStateValuesEntry("Hashing", TorrentState.Hashing),
new TorrentStateValuesEntry("Hashing paused", TorrentState.HashingPaused),
new TorrentStateValuesEntry("Metadata", TorrentState.Metadata),
new TorrentStateValuesEntry("Paused", TorrentState.Paused),
new TorrentStateValuesEntry("Seeding", TorrentState.Seeding),
new TorrentStateValuesEntry("Starting", TorrentState.Starting),
new TorrentStateValuesEntry("Stopped", TorrentState.Stopped),
new TorrentStateValuesEntry("Stopping", TorrentState.Stopping),
}
},
{
"StatusIcon",
new []
{
new TorrentStateValuesEntry("\xE121",
TorrentState.Stopping,
TorrentState.Starting,
TorrentState.Metadata,
TorrentState.Hashing),
new TorrentStateValuesEntry("\xE103",
TorrentState.HashingPaused,
TorrentState.Paused),
new TorrentStateValuesEntry("\xE118",
TorrentState.Downloading),
new TorrentStateValuesEntry("\xE11C",
TorrentState.Seeding),
new TorrentStateValuesEntry("\xE7BA",
TorrentState.Error),
new TorrentStateValuesEntry("\xE15B",
TorrentState.Stopped)
}
}
};
public object Convert(object value, Type targetType, object parameter, string language)
{
try { return StateDictionary[parameter as string].FirstOrDefault(i => i.AppliedTo.Contains((TorrentState)value))?.Value; }
catch { return value; }
}
public object ConvertBack(object value, Type targetType, object parameter, string language) =>
null;
}
}
+7 -3
View File
@@ -123,9 +123,9 @@
<Compile Include="Dialogs\AddTorrentDialog.xaml.cs"> <Compile Include="Dialogs\AddTorrentDialog.xaml.cs">
<DependentUpon>AddTorrentDialog.xaml</DependentUpon> <DependentUpon>AddTorrentDialog.xaml</DependentUpon>
</Compile> </Compile>
<Compile Include="Models\DataSize.cs" />
<Compile Include="Models\PointerHoverStateTrigger.cs" /> <Compile Include="Models\PointerHoverStateTrigger.cs" />
<Compile Include="Models\TorrentStateDataTrigger.cs" /> <Compile Include="Models\TorrentMetadataInfo.cs" />
<Compile Include="Models\TorrentStateValuesEntry.cs" />
<Compile Include="Pages\TorrentDetailsPage.xaml.cs"> <Compile Include="Pages\TorrentDetailsPage.xaml.cs">
<DependentUpon>TorrentDetailsPage.xaml</DependentUpon> <DependentUpon>TorrentDetailsPage.xaml</DependentUpon>
</Compile> </Compile>
@@ -141,7 +141,8 @@
<Compile Include="Utils\Metrics.cs" /> <Compile Include="Utils\Metrics.cs" />
<Compile Include="Utils\Settings.cs" /> <Compile Include="Utils\Settings.cs" />
<Compile Include="Utils\TorrentClient.cs" /> <Compile Include="Utils\TorrentClient.cs" />
<Compile Include="Models\TorrentItem.cs" /> <Compile Include="ValueConverters\TorrentStateValueConverter.cs" />
<Compile Include="ValueConverters\EstimatesValueConverter.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AppxManifest Include="Package.appxmanifest"> <AppxManifest Include="Package.appxmanifest">
@@ -265,6 +266,9 @@
<Name>Visual C++ 2015 Runtime for Universal Windows Platform Apps</Name> <Name>Visual C++ 2015 Runtime for Universal Windows Platform Apps</Name>
</SDKReference> </SDKReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Controls\" />
</ItemGroup>
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' "> <PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' &lt; '14.0' ">
<VisualStudioVersion>14.0</VisualStudioVersion> <VisualStudioVersion>14.0</VisualStudioVersion>
</PropertyGroup> </PropertyGroup>