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
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) =>
|
||||||
|
|||||||
@@ -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="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>
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)' < '14.0' ">
|
<PropertyGroup Condition=" '$(VisualStudioVersion)' == '' or '$(VisualStudioVersion)' < '14.0' ">
|
||||||
<VisualStudioVersion>14.0</VisualStudioVersion>
|
<VisualStudioVersion>14.0</VisualStudioVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user