Basic visual interactions. Torrent client (non-functional)
This commit is contained in:
@@ -7,16 +7,19 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinTorrent", "WinTorrent\Wi
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|ARM = Debug|ARM
|
||||
Debug|ARM64 = Debug|ARM64
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|ARM = Release|ARM
|
||||
Release|ARM64 = Release|ARM64
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
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.Build.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.Build.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.Build.0 = Release|ARM
|
||||
{88F8E34F-0129-4856-A1F7-4A89DA892C9B}.Release|ARM.Deploy.0 = Release|ARM
|
||||
|
||||
@@ -2,16 +2,38 @@
|
||||
x:Class="WinTorrent.Dialogs.AddTorrentDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="using:WinTorrent.Dialogs"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
mc:Ignorable="d"
|
||||
Title="TITLE"
|
||||
PrimaryButtonText="Button1"
|
||||
SecondaryButtonText="Button2"
|
||||
Title="Add new torrent"
|
||||
PrimaryButtonText="Add"
|
||||
CloseButtonText="Cancel"
|
||||
DefaultButton="Primary"
|
||||
IsPrimaryButtonEnabled="False"
|
||||
PrimaryButtonClick="ContentDialog_PrimaryButtonClick"
|
||||
SecondaryButtonClick="ContentDialog_SecondaryButtonClick">
|
||||
Loaded="ContentDialog_Loaded">
|
||||
|
||||
<Grid>
|
||||
</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>
|
||||
|
||||
<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>
|
||||
|
||||
@@ -1,35 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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.Controls;
|
||||
using Windows.UI.Xaml.Controls.Primitives;
|
||||
using Windows.UI.Xaml.Data;
|
||||
using Windows.UI.Xaml.Input;
|
||||
using Windows.UI.Xaml.Media;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
|
||||
// The Content Dialog item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
|
||||
using Windows.Storage;
|
||||
using Windows.Storage.Pickers;
|
||||
using WinTorrent.Utils;
|
||||
using MonoTorrent;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace WinTorrent.Dialogs
|
||||
{
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 WinTorrent.Pages;
|
||||
|
||||
@@ -6,7 +7,7 @@ namespace WinTorrent.Models
|
||||
{
|
||||
class PointerHoverStateTrigger : StateTriggerBase
|
||||
{
|
||||
public TorrentItem TargetItem
|
||||
public TorrentManager TargetItem
|
||||
{
|
||||
get => _targetItem;
|
||||
set
|
||||
@@ -26,7 +27,7 @@ namespace WinTorrent.Models
|
||||
_targetElement.PointerExited += PointerExited;
|
||||
}
|
||||
}
|
||||
private TorrentItem _targetItem;
|
||||
private TorrentManager _targetItem;
|
||||
private FrameworkElement _targetElement;
|
||||
|
||||
private void PointerExited(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e) =>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,8 @@
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
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
|
||||
Name="ce54543c-294b-4541-b28d-ae993c9aab5f"
|
||||
@@ -50,6 +51,7 @@
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<Capability Name="internetClient" />
|
||||
<rescap:Capability Name="broadFileSystemAccess" />
|
||||
<Capability Name="internetClient" />
|
||||
</Capabilities>
|
||||
</Package>
|
||||
@@ -4,18 +4,23 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
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">
|
||||
|
||||
<Page.Resources>
|
||||
<ThemeShadow x:Name="sharedShadow"/>
|
||||
<converters:TorrentStateValueConverter x:Name="stateConverter"/>
|
||||
<converters:EstimatesValueConverter x:Name="etaConverter"/>
|
||||
</Page.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.BackgroundTransition>
|
||||
<BrushTransition/>
|
||||
</Grid.BackgroundTransition>
|
||||
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="32"/>
|
||||
<RowDefinition/>
|
||||
@@ -101,172 +106,13 @@
|
||||
</ListView.ItemContainerStyle>
|
||||
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:TorrentItem">
|
||||
<DataTemplate x:DataType="client:TorrentManager">
|
||||
<UserControl>
|
||||
<Grid>
|
||||
<Grid.Resources>
|
||||
<SolidColorBrush x:Name="SystemAccentColorBrush" Color="{StaticResource SystemAccentColor}"/>
|
||||
</Grid.Resources>
|
||||
<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">
|
||||
<VisualState x:Name="Normal">
|
||||
<VisualState.Storyboard>
|
||||
@@ -309,24 +155,45 @@
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</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}" />
|
||||
<TextBlock x:Name="StatusLabel" Grid.Row="1" Grid.Column="1" Text="Downloading" Foreground="{StaticResource SystemAccentColor}"/>
|
||||
<TextBlock x:Name="SpeedLabel" Grid.Row="1" Grid.Column="2" Text="{Binding TransmissionSpeed}" Foreground="Gray"/>
|
||||
<TextBlock x:Name="ProgressLabel" Grid.Row="1" Grid.Column="3" Foreground="Gray">
|
||||
<Run Text="{Binding Downloaded}"/> of <Run Text="{Binding TotalSize}"/>
|
||||
</TextBlock>
|
||||
<TextBlock x:Name="EtaLabel" Grid.Row="1" Grid.Column="4" Foreground="Gray">
|
||||
<Run Text="{Binding RemainingTime}"/> remaining
|
||||
<FontIcon Grid.Row="1" Grid.Column="0"
|
||||
Glyph="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=StatusIcon}"
|
||||
Foreground="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=AccentColor}" />
|
||||
|
||||
<TextBlock Grid.Row="1" Grid.Column="1"
|
||||
Text="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=StatusLabel}"
|
||||
Foreground="{Binding Path=State, Converter={StaticResource stateConverter}, ConverterParameter=AccentColor}" />
|
||||
|
||||
<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>
|
||||
|
||||
<Button x:Name="PauseButton" Grid.Column="5" Grid.RowSpan="2" Background="Transparent" Height="50" Width="50" Opacity="0" Tag="{Binding}" Click="PauseItem">
|
||||
<SymbolIcon x:Name="PauseButtonIcon" Symbol="Pause"/>
|
||||
<TextBlock Grid.Row="1" Grid.Column="4" Foreground="Gray"
|
||||
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>
|
||||
</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>
|
||||
</UserControl>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -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.Controls;
|
||||
using Windows.UI.Xaml.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
using WinTorrent.Dialogs;
|
||||
using WinTorrent.Models;
|
||||
using WinTorrent.Utils;
|
||||
|
||||
namespace WinTorrent.Pages
|
||||
@@ -19,7 +22,9 @@ namespace WinTorrent.Pages
|
||||
search.Translation += new System.Numerics.Vector3(0, 0, 10);
|
||||
|
||||
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)
|
||||
@@ -32,17 +37,27 @@ namespace WinTorrent.Pages
|
||||
private void OpenSettings(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) =>
|
||||
Frame.Navigate(typeof(SettingsPage), null, new SlideNavigationTransitionInfo { Effect = SlideNavigationTransitionEffect.FromLeft });
|
||||
|
||||
private async void AddTorrent(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e) =>
|
||||
await new AddTorrentDialog().ShowAsync();
|
||||
private async void AddTorrent(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)),
|
||||
"completed" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Completed, TorrentState.Seeding)),
|
||||
"paused" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Paused, TorrentState.Resuming)),
|
||||
"completed" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Stopped, TorrentState.Seeding)),
|
||||
"paused" => TorrentClient.Torrents.FindAll(i => i.State.BelongsTo(TorrentState.Paused, TorrentState.HashingPaused)),
|
||||
_ => TorrentClient.Torrents
|
||||
};
|
||||
search.ItemsSource = null;
|
||||
@@ -56,8 +71,8 @@ namespace WinTorrent.Pages
|
||||
sender.ItemsSource = null;
|
||||
navigationList.SelectedIndex = 0;
|
||||
var result = TorrentClient.Torrents.FindAll(i =>
|
||||
i.Title.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) ||
|
||||
(i.Files?.Path.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) ?? false) ||
|
||||
i.Torrent.Name.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) ||
|
||||
(i?.SavePath.ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()) ?? false) ||
|
||||
i.State.ToString().ToLowerInvariant().Contains(sender.Text.ToLowerInvariant()));
|
||||
|
||||
list.ItemsSource = result;
|
||||
@@ -88,12 +103,12 @@ namespace WinTorrent.Pages
|
||||
}
|
||||
|
||||
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) =>
|
||||
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)
|
||||
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.Media.Animation;
|
||||
using Windows.UI.Xaml.Navigation;
|
||||
@@ -8,7 +9,7 @@ namespace WinTorrent.Pages
|
||||
{
|
||||
public sealed partial class TorrentDetailsPage : Page
|
||||
{
|
||||
public TorrentItem Item { get; private set; }
|
||||
public TorrentManager Item { get; private set; }
|
||||
|
||||
public TorrentDetailsPage()
|
||||
{
|
||||
@@ -20,7 +21,7 @@ namespace WinTorrent.Pages
|
||||
{
|
||||
base.OnNavigatedTo(e);
|
||||
|
||||
Item = e.Parameter as TorrentItem;
|
||||
Item = e.Parameter as TorrentManager;
|
||||
|
||||
if (ConnectedAnimationService.GetForCurrentView().GetAnimation("ca1") is ConnectedAnimation animation)
|
||||
animation.TryStart(caTarget);
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace WinTorrent.Utils
|
||||
public static async Task<StorageFolder> GetDefaultFolder()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(DefaultFolder))
|
||||
return await DownloadsFolder.CreateFolderAsync(Package.Current.DisplayName, CreationCollisionOption.OpenIfExists);
|
||||
return null;
|
||||
else
|
||||
return await StorageApplicationPermissions.FutureAccessList.GetFolderAsync(DefaultFolder);
|
||||
}
|
||||
|
||||
@@ -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.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;
|
||||
|
||||
namespace WinTorrent.Utils
|
||||
{
|
||||
public delegate void TorrentStateChangedEventHandler(TorrentItem sender, TorrentState previousState);
|
||||
|
||||
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()
|
||||
{
|
||||
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
|
||||
{
|
||||
Title = "Microsoft Edge",
|
||||
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
|
||||
}
|
||||
SavePath = ApplicationData.Current.LocalFolder.Path,
|
||||
MaximumDownloadSpeed = 0, // Set from settings
|
||||
MaximumUploadSpeed = 0
|
||||
};
|
||||
|
||||
Engine = new ClientEngine(engineSettings);
|
||||
|
||||
Restore();
|
||||
}
|
||||
|
||||
public static void AddTorrent()
|
||||
public static async void AddTorrent(Torrent torrent, StorageFolder destinationFolder)
|
||||
{
|
||||
TorrentMetadataInfo meta = new TorrentMetadataInfo
|
||||
{
|
||||
DestinationFolderFALToken = StorageApplicationPermissions.FutureAccessList.Add(destinationFolder),
|
||||
TorrentFileName = new FileInfo(torrent.TorrentPath).Name
|
||||
};
|
||||
|
||||
TorrentManager manager = new TorrentManager(torrent, destinationFolder.Path);
|
||||
meta.TorrentHash = manager.InfoHash.ToHex();
|
||||
|
||||
if (TorrentsMetadata.Any(i => i.TorrentHash == meta.TorrentHash))
|
||||
{
|
||||
ContentDialog alert = new ContentDialog
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
TorrentsMetadata.Add(meta);
|
||||
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 void PauseTorrent()
|
||||
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);
|
||||
}
|
||||
|
||||
public static void CancelTorrent()
|
||||
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 void ResumeTorrent()
|
||||
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();
|
||||
}
|
||||
|
||||
public static void SeedTorrent()
|
||||
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 void OnItemStateChanged(TorrentItem item, TorrentState previousState) =>
|
||||
TorrentItemStateChanged?.Invoke(item, previousState);
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -123,9 +123,9 @@
|
||||
<Compile Include="Dialogs\AddTorrentDialog.xaml.cs">
|
||||
<DependentUpon>AddTorrentDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Models\DataSize.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">
|
||||
<DependentUpon>TorrentDetailsPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -141,7 +141,8 @@
|
||||
<Compile Include="Utils\Metrics.cs" />
|
||||
<Compile Include="Utils\Settings.cs" />
|
||||
<Compile Include="Utils\TorrentClient.cs" />
|
||||
<Compile Include="Models\TorrentItem.cs" />
|
||||
<Compile Include="ValueConverters\TorrentStateValueConverter.cs" />
|
||||
<Compile Include="ValueConverters\EstimatesValueConverter.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AppxManifest Include="Package.appxmanifest">
|
||||
@@ -265,6 +266,9 @@
|
||||
<Name>Visual C++ 2015 Runtime for Universal Windows Platform Apps</Name>
|
||||
</SDKReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Controls\" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
Reference in New Issue
Block a user