Merge pull request #78 from mateuszskoczek/features/errors

Features/errors
This commit is contained in:
2024-03-10 01:57:02 +01:00
committed by GitHub
Unverified
16 changed files with 458 additions and 244 deletions

View File

@@ -117,10 +117,10 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AuthenticationButtonSignIn.Value" xml:space="preserve">
<data name="AuthenticationButtonSignIn.Text" xml:space="preserve">
<value>Sign in</value>
</data>
<data name="AuthenticationButtonSignOut.Value" xml:space="preserve">
<data name="AuthenticationButtonSignOut.Text" xml:space="preserve">
<value>Sign out</value>
</data>
<data name="AuthenticationDescriptionLoading.Value" xml:space="preserve">
@@ -135,9 +135,18 @@
<data name="TwitchAuthenticationDescriptionAuthenticationInvalid" xml:space="preserve">
<value>Token expired or is invalid. Please log in again</value>
</data>
<data name="TwitchAuthenticationDescriptionCannotValidate" xml:space="preserve">
<value>Cannot validate token. Check your internet connection and try again later.</value>
</data>
<data name="TwitchAuthenticationDescriptionNotAuthenticated" xml:space="preserve">
<value>You are not authenticated. Please sign in</value>
</data>
<data name="TwitchAuthenticationDescriptionNotAuthenticatedNoInternetConnection" xml:space="preserve">
<value>You are not authenticated and there is no internet connection. Check your internet connection and try again later.</value>
</data>
<data name="TwitchAuthenticationDialogMessage" xml:space="preserve">
<value>An unknown error occured during Twitch login</value>
</data>
<data name="TwitchAuthenticationDialogTitle" xml:space="preserve">
<value>Twitch authentication error</value>
</data>

View File

@@ -117,6 +117,21 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="DialogErrorMessageNoInternetConnection" xml:space="preserve">
<value>No internet connection. Try again later.</value>
</data>
<data name="DialogErrorTitle" xml:space="preserve">
<value>Error</value>
</data>
<data name="ErrorDownloadingTimeout" xml:space="preserve">
<value>Downloading timeout. Check your internet connection and try again later.</value>
</data>
<data name="ErrorFFmpeg" xml:space="preserve">
<value>FFmpeg exited with error.</value>
</data>
<data name="ErrorFFmpegPath" xml:space="preserve">
<value>No FFmpeg executables found. Check path in settings and try again.</value>
</data>
<data name="StatusCancelled.Text" xml:space="preserve">
<value>Cancelled</value>
</data>

View File

@@ -117,6 +117,18 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ErrorInfoBar.Title" xml:space="preserve">
<value>Error</value>
</data>
<data name="ErrorInfoBarNoInternetConnection" xml:space="preserve">
<value>No internet connection</value>
</data>
<data name="OptionBarCancelAll.Label" xml:space="preserve">
<value>Cancel all</value>
</data>
<data name="OptionBarCancelAll.Width" xml:space="preserve">
<value>80</value>
</data>
<data name="OptionBarDownloadAll.Label" xml:space="preserve">
<value>Download all</value>
</data>

View File

@@ -123,6 +123,9 @@
<data name="EmptyUrl" xml:space="preserve">
<value>Invalid url. Url cannot be empty.</value>
</data>
<data name="SearchTimeout" xml:space="preserve">
<value>Search timeout. Check your internet connection.</value>
</data>
<data name="SourceNotSupported" xml:space="preserve">
<value>Invalid url. Url does not match any of supported source.</value>
</data>

View File

@@ -126,6 +126,9 @@
<data name="Header.Text" xml:space="preserve">
<value>Subscriptions</value>
</data>
<data name="NoInternetConnectionError" xml:space="preserve">
<value>No internet connection</value>
</data>
<data name="PlaylistSearchButton.Content" xml:space="preserve">
<value>Add</value>
</data>

View File

@@ -13,6 +13,9 @@ using System.IO;
using VDownload.Services.UI.Notifications;
using VDownload.Services.UI.StringResources;
using System.Collections.Generic;
using System.Net.Http;
using Instances.Exceptions;
using FFMpegCore.Exceptions;
namespace VDownload.Core.Tasks
{
@@ -140,7 +143,11 @@ namespace VDownload.Core.Tasks
await _settingsService.Load();
string tempDirectory = $"{_settingsService.Data.Common.Temp.Directory}\\{_configurationService.Common.Path.Temp.TasksDirectory}\\{Id}";
string tempDirectory = $"{_settingsService.Data.Common.Temp.Directory}\\{_configurationService.Common.Path.Temp.TasksDirectory}\\{Guid.NewGuid()}";
if (Directory.Exists(tempDirectory))
{
Directory.Delete(tempDirectory, true);
}
Directory.CreateDirectory(tempDirectory);
List<string> content = new List<string>()
@@ -180,10 +187,11 @@ namespace VDownload.Core.Tasks
.JoinProgressReporter(onProgressProcessing, downloadResult.NewDuration);
await ffmpegBuilder.RunAsync(downloadResult.File, outputPath);
StorageFile outputFile = await StorageFile.GetFileFromPathAsync(outputPath);
UpdateStatusWithDispatcher(DownloadTaskStatus.Finalizing);
string destination = $"{DownloadOptions.Directory}\\{DownloadOptions.Filename}.{DownloadOptions.Extension}";
File.Copy(outputPath, destination, true);
await Finalizing(outputFile);
UpdateStatusWithDispatcher(DownloadTaskStatus.EndedSuccessfully);
@@ -199,7 +207,26 @@ namespace VDownload.Core.Tasks
}
catch (Exception ex)
{
UpdateErrorWithDispatcher(ex.Message);
string message;
if (ex is TaskCanceledException || ex is HttpRequestException)
{
message = _stringResourcesService.HomeDownloadsViewResources.Get("ErrorDownloadingTimeout");
}
else if (ex is InstanceFileNotFoundException)
{
message = _stringResourcesService.HomeDownloadsViewResources.Get("ErrorFFmpegPath");
}
else if (ex is FFMpegException)
{
message = _stringResourcesService.HomeDownloadsViewResources.Get("ErrorFFmpeg");
}
else
{
message = ex.Message;
}
UpdateErrorWithDispatcher(message);
UpdateStatusWithDispatcher(DownloadTaskStatus.EndedUnsuccessfully);
if (_settingsService.Data.Common.Notifications.OnUnsuccessful)
@@ -218,11 +245,19 @@ namespace VDownload.Core.Tasks
}
}
private void UpdateStatusWithDispatcher(DownloadTaskStatus status) => _dispatcherQueue.TryEnqueue(() => Status = status);
protected async Task Finalizing(StorageFile outputFile)
{
StorageFolder destination = await StorageFolder.GetFolderFromPathAsync(DownloadOptions.Directory);
private void UpdateProgressWithDispatcher(double progress) => _dispatcherQueue.TryEnqueue(() => Progress = progress);
string filename = $"{DownloadOptions.Filename}.{DownloadOptions.Extension}";
await outputFile.CopyAsync(destination, filename, NameCollisionOption.ReplaceExisting);
}
private void UpdateErrorWithDispatcher(string message) => _dispatcherQueue.TryEnqueue(() => Error = message);
protected void UpdateStatusWithDispatcher(DownloadTaskStatus status) => _dispatcherQueue.TryEnqueue(() => Status = status);
protected void UpdateProgressWithDispatcher(double progress) => _dispatcherQueue.TryEnqueue(() => Progress = progress);
protected void UpdateErrorWithDispatcher(string message) => _dispatcherQueue.TryEnqueue(() => Error = message);
#endregion
}

View File

@@ -1,8 +1,10 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@@ -44,10 +46,13 @@ namespace VDownload.Core.ViewModels.Authentication
#region PROPERTIES
[ObservableProperty]
private AuthenticationButton _twitchButtonState = AuthenticationButton.Loading;
protected AuthenticationButton _twitchButtonState = AuthenticationButton.Loading;
[ObservableProperty]
private string _twitchDescription;
protected bool _twitchButtonEnable = true;
[ObservableProperty]
protected string _twitchDescription;
#endregion
@@ -107,7 +112,9 @@ namespace VDownload.Core.ViewModels.Authentication
}
else
{
await _dialogsService.ShowOk(_stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDialogTitle"), "An unknown error occured"); // TODO : Change to string resource
string title = _stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDialogTitle");
string message = _stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDialogMessage");
await _dialogsService.ShowOk(title, message);
}
}
await TwitchAuthenticationRefresh();
@@ -122,17 +129,38 @@ namespace VDownload.Core.ViewModels.Authentication
private async Task TwitchAuthenticationRefresh()
{
TwitchButtonState = AuthenticationButton.Loading;
TwitchButtonEnable = true;
byte[]? token = await _twitchAuthenticationService.GetToken();
if (token is null)
{
TwitchDescription = _stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDescriptionNotAuthenticated");
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
TwitchButtonEnable = false;
TwitchDescription = _stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDescriptionNotAuthenticatedNoInternetConnection");
}
else
{
TwitchDescription = _stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDescriptionNotAuthenticated");
}
TwitchButtonState = AuthenticationButton.SignIn;
}
else
{
TwitchValidationResult validationResult = await _twitchAuthenticationService.ValidateToken(token);
TwitchValidationResult validationResult;
try
{
validationResult = await _twitchAuthenticationService.ValidateToken(token);
}
catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException)
{
TwitchDescription = _stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDescriptionCannotValidate");
TwitchButtonState = AuthenticationButton.SignIn;
TwitchButtonEnable = false;
return;
}
if (validationResult.Success)
{
TwitchDescription = string.Format(_stringResourcesService.AuthenticationViewResources.Get("TwitchAuthenticationDescriptionAuthenticated"), validationResult.TokenData.Login, validationResult.TokenData.ExpirationDate);

View File

@@ -68,6 +68,14 @@ namespace VDownload.Core.ViewModels.Home
];
if (idleStatuses.Contains(task.Status))
{
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
string title = _stringResourcesService.HomeDownloadsViewResources.Get("DialogErrorTitle");
string message = _stringResourcesService.HomeDownloadsViewResources.Get("DialogErrorMessageNoInternetConnection");
await _dialogsService.ShowOk(title, message);
return;
}
bool continueEnqueue = true;
if (NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)
{

View File

@@ -4,6 +4,7 @@ using CommunityToolkit.WinUI.Helpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using VDownload.Core.Tasks;
@@ -23,13 +24,6 @@ namespace VDownload.Core.ViewModels.Home
{
#region ENUMS
public enum OptionBarMessageIconType
{
None,
ProgressRing,
Error
}
public enum OptionBarContentType
{
None,
@@ -80,6 +74,11 @@ namespace VDownload.Core.ViewModels.Home
[ObservableProperty]
private Type _mainContent;
[ObservableProperty]
private string _optionBarError;
[ObservableProperty]
private bool _optionBarIsErrorOpened;
[ObservableProperty]
private OptionBarContentType _optionBarContent;
@@ -88,7 +87,7 @@ namespace VDownload.Core.ViewModels.Home
private string _optionBarMessage;
[ObservableProperty]
private OptionBarMessageIconType _optionBarMessageIcon;
private bool _optionBarLoading;
[ObservableProperty]
private bool _optionBarLoadSubscriptionButtonChecked;
@@ -149,8 +148,10 @@ namespace VDownload.Core.ViewModels.Home
MainContent = _downloadsView;
OptionBarContent = OptionBarContentType.None;
OptionBarMessageIcon = OptionBarMessageIconType.None;
OptionBarLoading = false;
OptionBarMessage = null;
OptionBarError = null;
OptionBarIsErrorOpened = true;
OptionBarLoadSubscriptionButtonChecked = false;
OptionBarVideoSearchButtonChecked = false;
OptionBarPlaylistSearchButtonChecked = false;
@@ -163,46 +164,57 @@ namespace VDownload.Core.ViewModels.Home
[RelayCommand]
public async Task LoadFromSubscription()
{
MainContent = _downloadsView;
OptionBarMessageIcon = OptionBarMessageIconType.None;
OptionBarMessage = null;
SearchButtonClicked();
if (OptionBarLoadSubscriptionButtonChecked)
{
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
ShowError(ErrorNoInternetConnection());
OptionBarLoadSubscriptionButtonChecked = false;
return;
}
OptionBarContent = OptionBarContentType.None;
OptionBarVideoSearchButtonChecked = false;
OptionBarPlaylistSearchButtonChecked = false;
OptionBarSearchNotPending = false;
OptionBarMessageIcon = OptionBarMessageIconType.ProgressRing;
OptionBarMessage = _stringResourcesService.HomeViewResources.Get("OptionBarMessageLoading");
StartSearch();
SubscriptionsVideoList subList = new SubscriptionsVideoList { Name = _stringResourcesService.CommonResources.Get("SubscriptionVideoListName") };
List<Task> tasks = new List<Task>();
foreach (Subscription sub in _subscriptionsDataService.Data.ToArray())
try
{
tasks.Add(Task.Run(async () =>
foreach (Subscription sub in _subscriptionsDataService.Data.ToArray())
{
Playlist playlist;
try
tasks.Add(Task.Run(async () =>
{
playlist = await _searchService.SearchPlaylist(sub.Url.OriginalString, 0);
}
catch (MediaSearchException)
{
return;
}
Playlist playlist;
try
{
playlist = await _searchService.SearchPlaylist(sub.Url.OriginalString, 0);
}
catch (MediaSearchException)
{
return;
}
IEnumerable<Video> newIds = playlist.Where(x => !sub.VideoIds.Contains(x.Id));
subList.AddRange(newIds);
foreach (Video video in newIds)
{
sub.VideoIds.Add(video.Id);
}
}));
IEnumerable<Video> newIds = playlist.Where(x => !sub.VideoIds.Contains(x.Id));
subList.AddRange(newIds);
foreach (Video video in newIds)
{
sub.VideoIds.Add(video.Id);
}
}));
}
await Task.WhenAll(tasks);
await _subscriptionsDataService.Save();
}
catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException)
{
ShowError(ErrorSearchTimeout());
return;
}
await Task.WhenAll(tasks);
await _subscriptionsDataService.Save();
if (subList.Count > 0)
{
@@ -217,17 +229,14 @@ namespace VDownload.Core.ViewModels.Home
}
OptionBarSearchNotPending = true;
OptionBarMessageIcon = OptionBarMessageIconType.None;
OptionBarLoading = false;
}
}
[RelayCommand]
public void VideoSearchShow()
{
OptionBarSearchNotPending = true;
OptionBarMessageIcon = OptionBarMessageIconType.None;
OptionBarMessage = null;
MainContent = _downloadsView;
SearchButtonClicked();
if (OptionBarContent != OptionBarContentType.VideoSearch)
{
@@ -244,10 +253,7 @@ namespace VDownload.Core.ViewModels.Home
[RelayCommand]
public void PlaylistSearchShow()
{
OptionBarSearchNotPending = true;
OptionBarMessageIcon = OptionBarMessageIconType.None;
OptionBarMessage = null;
MainContent = _downloadsView;
SearchButtonClicked();
if (OptionBarContent != OptionBarContentType.PlaylistSearch)
{
@@ -264,9 +270,13 @@ namespace VDownload.Core.ViewModels.Home
[RelayCommand]
public async Task VideoSearchStart()
{
OptionBarSearchNotPending = false;
OptionBarMessageIcon = OptionBarMessageIconType.ProgressRing;
OptionBarMessage = _stringResourcesService.HomeViewResources.Get("OptionBarMessageLoading");
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
ShowError(ErrorNoInternetConnection());
return;
}
StartSearch();
Video video;
try
@@ -275,9 +285,12 @@ namespace VDownload.Core.ViewModels.Home
}
catch (MediaSearchException ex)
{
OptionBarMessageIcon = OptionBarMessageIconType.Error;
OptionBarMessage = _stringResourcesService.SearchResources.Get(ex.StringCode);
OptionBarSearchNotPending = true;
ShowError(_stringResourcesService.SearchResources.Get(ex.StringCode));
return;
}
catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException)
{
ShowError(ErrorSearchTimeout());
return;
}
@@ -286,16 +299,20 @@ namespace VDownload.Core.ViewModels.Home
MainContent = _videoView;
OptionBarSearchNotPending = true;
OptionBarMessageIcon = OptionBarMessageIconType.None;
OptionBarLoading = false;
OptionBarMessage = null;
}
[RelayCommand]
public async Task PlaylistSearchStart()
{
OptionBarSearchNotPending = false;
OptionBarMessageIcon = OptionBarMessageIconType.ProgressRing;
OptionBarMessage = _stringResourcesService.HomeViewResources.Get("OptionBarMessageLoading");
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
ShowError(ErrorNoInternetConnection());
return;
}
StartSearch();
Playlist playlist;
try
@@ -304,9 +321,12 @@ namespace VDownload.Core.ViewModels.Home
}
catch (MediaSearchException ex)
{
OptionBarMessageIcon = OptionBarMessageIconType.Error;
OptionBarMessage = _stringResourcesService.SearchResources.Get(ex.StringCode);
OptionBarSearchNotPending = true;
ShowError(_stringResourcesService.SearchResources.Get(ex.StringCode));
return;
}
catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException)
{
ShowError(ErrorSearchTimeout());
return;
}
@@ -315,13 +335,19 @@ namespace VDownload.Core.ViewModels.Home
MainContent = _videoCollectionView;
OptionBarSearchNotPending = true;
OptionBarMessageIcon = OptionBarMessageIconType.None;
OptionBarLoading = false;
OptionBarMessage = null;
}
[RelayCommand]
public async Task Download()
{
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
ShowError(ErrorNoInternetConnection());
return;
}
if (_downloadTaskManager.Tasks.Count > 0 && NetworkHelper.Instance.ConnectionInformation.IsInternetOnMeteredConnection)
{
string title = _stringResourcesService.CommonResources.Get("StartAtMeteredConnectionDialogTitle");
@@ -339,13 +365,58 @@ namespace VDownload.Core.ViewModels.Home
}
}
[RelayCommand]
public async Task Cancel()
{
List<Task> tasks = new List<Task>();
foreach (DownloadTask task in _downloadTaskManager.Tasks)
{
tasks.Add(task.Cancel());
}
await Task.WhenAll(tasks);
}
[RelayCommand]
public void CloseError()
{
OptionBarError = null;
OptionBarIsErrorOpened = true;
}
#endregion
#region PRIVATE METHODS
private async void BackToDownload_EventHandler(object sender, EventArgs e) => await Navigation();
protected void ShowError(string message)
{
OptionBarError = message;
OptionBarSearchNotPending = true;
OptionBarLoading = false;
OptionBarMessage = null;
}
protected void SearchButtonClicked()
{
OptionBarSearchNotPending = true;
OptionBarLoading = false;
OptionBarMessage = null;
MainContent = _downloadsView;
}
protected void StartSearch()
{
OptionBarSearchNotPending = false;
OptionBarLoading = true;
OptionBarMessage = _stringResourcesService.HomeViewResources.Get("OptionBarMessageLoading");
}
protected async void BackToDownload_EventHandler(object sender, EventArgs e) => await Navigation();
protected string ErrorNoInternetConnection() => _stringResourcesService.HomeViewResources.Get("ErrorInfoBarNoInternetConnection");
protected string ErrorSearchTimeout() => _stringResourcesService.SearchResources.Get("SearchTimeout");
#endregion
}

View File

@@ -1,10 +1,12 @@
using ABI.System;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.WinUI.Helpers;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using VDownload.Core.ViewModels.Subscriptions.Helpers;
@@ -92,6 +94,12 @@ namespace VDownload.Core.ViewModels.Subscriptions
[RelayCommand]
public async Task Add()
{
if (!NetworkHelper.Instance.ConnectionInformation.IsInternetAvailable)
{
ShowError(_stringResourcesService.SubscriptionsViewResources.Get("NoInternetConnectionError"));
return;
}
Loading = true;
Playlist playlist;
@@ -101,16 +109,18 @@ namespace VDownload.Core.ViewModels.Subscriptions
}
catch (MediaSearchException ex)
{
Error = _stringResourcesService.SearchResources.Get(ex.StringCode);
Loading = false;
ShowError(_stringResourcesService.SearchResources.Get(ex.StringCode));
return;
}
catch (Exception ex) when (ex is TaskCanceledException || ex is HttpRequestException)
{
ShowError(_stringResourcesService.SearchResources.Get("SearchTimeout"));
return;
}
if (_subscriptionsDataService.Data.Any(x => x.Source == playlist.Source && x.Name == playlist.Name))
{
Error = _stringResourcesService.SubscriptionsViewResources.Get("DuplicateError");
Loading = false;
ShowError(_stringResourcesService.SubscriptionsViewResources.Get("DuplicateError"));
return;
}
@@ -136,6 +146,18 @@ namespace VDownload.Core.ViewModels.Subscriptions
IsErrorOpened = true;
}
#endregion
#endregion
#region PRIVATE METHODS
protected void ShowError(string message)
{
Error = message;
Loading = false;
}
#endregion
}
}

View File

@@ -33,27 +33,24 @@
<ctc:SettingsCard Header="Twitch">
<i:Interaction.Behaviors>
<ic:DataTriggerBehavior Binding="{Binding TwitchButtonState, Converter={StaticResource ObjectToStringConverter}}"
ComparisonCondition="NotEqual"
Value="Loading">
ComparisonCondition="NotEqual"
Value="Loading">
<ic:ChangePropertyAction PropertyName="Description"
Value="{Binding TwitchDescription}"/>
Value="{Binding TwitchDescription}"/>
<ic:ChangePropertyAction PropertyName="Content">
<ic:ChangePropertyAction.Value>
<Button Command="{Binding TwitchAuthenticationCommand}">
<i:Interaction.Behaviors>
<ic:DataTriggerBehavior Binding="{Binding TwitchButtonState, Converter={StaticResource ObjectToStringConverter}}"
ComparisonCondition="Equal"
Value="SignIn">
<ic:ChangePropertyAction x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/AuthenticationButtonSignIn"
PropertyName="Content"/>
</ic:DataTriggerBehavior>
<ic:DataTriggerBehavior Binding="{Binding TwitchButtonState, Converter={StaticResource ObjectToStringConverter}}"
ComparisonCondition="Equal"
Value="SignOut">
<ic:ChangePropertyAction x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/AuthenticationButtonSignOut"
PropertyName="Content"/>
</ic:DataTriggerBehavior>
</i:Interaction.Behaviors>
<Button Command="{Binding TwitchAuthenticationCommand}"
IsEnabled="{Binding TwitchButtonEnable}">
<Button.Content>
<ctuc:SwitchPresenter Value="{Binding TwitchButtonState, Converter={StaticResource ObjectToStringConverter}}">
<ctuc:Case Value="SignIn">
<TextBlock x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/AuthenticationButtonSignIn"/>
</ctuc:Case>
<ctuc:Case Value="SignOut">
<TextBlock x:Uid="/VDownload.Core.Strings/AuthenticationViewResources/AuthenticationButtonSignOut"/>
</ctuc:Case>
</ctuc:SwitchPresenter>
</Button.Content>
</Button>
</ic:ChangePropertyAction.Value>
</ic:ChangePropertyAction>

View File

@@ -25,130 +25,132 @@
<Frame Content="{Binding MainContent, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ViewModelToViewConverter}}">
</Frame>
<Grid Grid.Row="1"
Background="{ThemeResource OptionBarBackgroundColor}"
CornerRadius="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ctuc:UniformGrid Grid.Column="0"
Rows="1"
Margin="15,0,0,0">
<ctuc:UniformGrid.RowDefinitions>
<RowDefinition/>
</ctuc:UniformGrid.RowDefinitions>
<ctuc:UniformGrid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</ctuc:UniformGrid.ColumnDefinitions>
<ctuc:SwitchPresenter Grid.Row="0"
VerticalAlignment="Stretch"
Margin="0,0,15,0"
Value="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}">
<i:Interaction.Behaviors>
<ic:DataTriggerBehavior Binding="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}"
ComparisonCondition="Equal"
Value="None">
<ic:ChangePropertyAction PropertyName="Visibility"
Value="Collapsed"/>
</ic:DataTriggerBehavior>
<ic:DataTriggerBehavior Binding="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}"
ComparisonCondition="NotEqual"
Value="None">
<ic:ChangePropertyAction PropertyName="Visibility"
Value="Visible"/>
</ic:DataTriggerBehavior>
</i:Interaction.Behaviors>
<ctuc:Case Value="VideoSearch">
<Grid ColumnSpacing="10"
VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarVideoSearchContentTextBox"
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding OptionBarVideoSearchTBValue, Mode=TwoWay}"/>
<Button x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarSearchButton"
Grid.Column="1"
IsEnabled="{Binding OptionBarSearchNotPending}"
Command="{Binding VideoSearchStartCommand}"/>
</Grid>
</ctuc:Case>
<ctuc:Case Value="PlaylistSearch">
<Grid ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearchContentTextBox"
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding OptionBarPlaylistSearchTBValue, Mode=TwoWay}"/>
<NumberBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearchContentNumberBox"
Grid.Column="1"
VerticalAlignment="Center"
SpinButtonPlacementMode="Compact"
SmallChange="1"
LargeChange="10"
Value="{Binding OptionBarPlaylistSearchNBValue, Mode=TwoWay}"
Minimum="0"/>
<Button x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarSearchButton"
Grid.Column="2"
IsEnabled="{Binding OptionBarSearchNotPending}"
Command="{Binding PlaylistSearchStartCommand}"/>
</Grid>
</ctuc:Case>
</ctuc:SwitchPresenter>
<StackPanel VerticalAlignment="Center"
Orientation="Horizontal">
<StackPanel.Resources>
<x:Double x:Key="IconSize">20</x:Double>
<Thickness x:Key="IconMargin">0,0,10,0</Thickness>
</StackPanel.Resources>
<ctuc:SwitchPresenter Value="{Binding OptionBarMessageIcon, Converter={StaticResource ObjectToStringConverter}}">
<ctuc:Case Value="None"/>
<ctuc:Case Value="ProgressRing">
<ProgressRing Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="{StaticResource IconMargin}"/>
</ctuc:Case>
<ctuc:Case Value="Error">
<Image Width="{StaticResource IconSize}"
Height="{StaticResource IconSize}"
Margin="{StaticResource IconMargin}"
Source="{StaticResource ImageHomeViewError}"/>
</ctuc:Case>
</ctuc:SwitchPresenter>
<TextBlock Text="{Binding OptionBarMessage}"/>
</StackPanel>
</ctuc:UniformGrid>
<StackPanel Grid.Column="2"
Orientation="Horizontal">
<AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarLoadSubscription"
Icon="Favorite"
IsEnabled="{Binding OptionBarSearchNotPending}"
IsChecked="{Binding OptionBarLoadSubscriptionButtonChecked, Mode=TwoWay}"
Command="{Binding LoadFromSubscriptionCommand}"/>
<AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarVideoSearch"
Icon="Video"
IsEnabled="{Binding OptionBarSearchNotPending}"
IsChecked="{Binding OptionBarVideoSearchButtonChecked, Mode=TwoWay}"
Command="{Binding VideoSearchShowCommand}"/>
<AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearch"
Icon="List"
IsEnabled="{Binding OptionBarSearchNotPending}"
IsChecked="{Binding OptionBarPlaylistSearchButtonChecked, Mode=TwoWay}"
Command="{Binding PlaylistSearchShowCommand}"/>
<AppBarSeparator/>
<AppBarButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarDownloadAll"
Icon="Download"
Command="{Binding DownloadCommand}"/>
</StackPanel>
</Grid>
<ctuc:SwitchPresenter Grid.Row="1"
Value="{Binding OptionBarError, Converter={StaticResource IsNotNullConverter}}">
<ctuc:Case Value="False">
<Grid Background="{ThemeResource OptionBarBackgroundColor}"
CornerRadius="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ctuc:UniformGrid Grid.Column="0"
Rows="1"
Margin="15,0,0,0">
<ctuc:UniformGrid.RowDefinitions>
<RowDefinition/>
</ctuc:UniformGrid.RowDefinitions>
<ctuc:UniformGrid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</ctuc:UniformGrid.ColumnDefinitions>
<ctuc:SwitchPresenter Grid.Row="0"
VerticalAlignment="Stretch"
Margin="0,0,15,0"
Value="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}">
<i:Interaction.Behaviors>
<ic:DataTriggerBehavior Binding="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}"
ComparisonCondition="Equal"
Value="None">
<ic:ChangePropertyAction PropertyName="Visibility"
Value="Collapsed"/>
</ic:DataTriggerBehavior>
<ic:DataTriggerBehavior Binding="{Binding OptionBarContent, Converter={StaticResource ObjectToStringConverter}}"
ComparisonCondition="NotEqual"
Value="None">
<ic:ChangePropertyAction PropertyName="Visibility"
Value="Visible"/>
</ic:DataTriggerBehavior>
</i:Interaction.Behaviors>
<ctuc:Case Value="VideoSearch">
<Grid ColumnSpacing="10"
VerticalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarVideoSearchContentTextBox"
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding OptionBarVideoSearchTBValue, Mode=TwoWay}"/>
<Button x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarSearchButton"
Grid.Column="1"
IsEnabled="{Binding OptionBarSearchNotPending}"
Command="{Binding VideoSearchStartCommand}"/>
</Grid>
</ctuc:Case>
<ctuc:Case Value="PlaylistSearch">
<Grid ColumnSpacing="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearchContentTextBox"
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding OptionBarPlaylistSearchTBValue, Mode=TwoWay}"/>
<NumberBox x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearchContentNumberBox"
Grid.Column="1"
VerticalAlignment="Center"
SpinButtonPlacementMode="Compact"
SmallChange="1"
LargeChange="10"
Value="{Binding OptionBarPlaylistSearchNBValue, Mode=TwoWay}"
Minimum="0"/>
<Button x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarSearchButton"
Grid.Column="2"
IsEnabled="{Binding OptionBarSearchNotPending}"
Command="{Binding PlaylistSearchStartCommand}"/>
</Grid>
</ctuc:Case>
</ctuc:SwitchPresenter>
<StackPanel VerticalAlignment="Center"
Orientation="Horizontal">
<ProgressRing Width="20"
Height="20"
Margin="0,0,10,0"
Visibility="{Binding OptionBarLoading, Converter={StaticResource BoolToVisibilityConverter}}"/>
<TextBlock Text="{Binding OptionBarMessage}"/>
</StackPanel>
</ctuc:UniformGrid>
<StackPanel Grid.Column="2"
Orientation="Horizontal"
Margin="0,0,4,0">
<AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarLoadSubscription"
Icon="Favorite"
IsEnabled="{Binding OptionBarSearchNotPending}"
IsChecked="{Binding OptionBarLoadSubscriptionButtonChecked, Mode=TwoWay}"
Command="{Binding LoadFromSubscriptionCommand}"/>
<AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarVideoSearch"
Icon="Video"
IsEnabled="{Binding OptionBarSearchNotPending}"
IsChecked="{Binding OptionBarVideoSearchButtonChecked, Mode=TwoWay}"
Command="{Binding VideoSearchShowCommand}"/>
<AppBarToggleButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarPlaylistSearch"
Icon="List"
IsEnabled="{Binding OptionBarSearchNotPending}"
IsChecked="{Binding OptionBarPlaylistSearchButtonChecked, Mode=TwoWay}"
Command="{Binding PlaylistSearchShowCommand}"/>
<AppBarSeparator/>
<AppBarButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarCancelAll"
Icon="Cancel"
Command="{Binding CancelCommand}"/>
<AppBarButton x:Uid="/VDownload.Core.Strings/HomeViewResources/OptionBarDownloadAll"
Icon="Download"
Command="{Binding DownloadCommand}"/>
</StackPanel>
</Grid>
</ctuc:Case>
<ctuc:Case Value="True">
<InfoBar x:Uid="/VDownload.Core.Strings/HomeViewResources/ErrorInfoBar"
Severity="Error"
IsOpen="{Binding OptionBarIsErrorOpened, Mode=TwoWay}"
Message="{Binding OptionBarError}"
CloseButtonCommand="{Binding CloseErrorCommand}"/>
</ctuc:Case>
</ctuc:SwitchPresenter>
</Grid>
</Page>

View File

@@ -7,6 +7,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Closed="Window_Closed">
Closed="Window_Closed"
Activated="Window_Activated">
<WebView2 x:Name="WebView" NavigationCompleted="WebView_NavigationCompleted"/>
</Window>

View File

@@ -1,3 +1,5 @@
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
@@ -84,6 +86,14 @@ namespace VDownload.Services.UI.WebView
_isOpened = false;
}
private void Window_Activated(object sender, WindowActivatedEventArgs args)
{
IntPtr windowHandle = WinRT.Interop.WindowNative.GetWindowHandle(this);
WindowId windowId = Win32Interop.GetWindowIdFromWindow(windowHandle);
AppWindow appWindow = AppWindow.GetFromWindowId(windowId);
appWindow.SetIcon(@"Assets\Logo\Logo.ico");
}
#endregion
}
}

View File

@@ -40,6 +40,9 @@ namespace VDownload.Services.Utility.FFmpeg
protected string _inputFile;
protected string _outputFile;
protected string _audioCodec;
protected string _videoCodec;
protected FFOptions _options;
#endregion
@@ -103,6 +106,10 @@ namespace VDownload.Services.Utility.FFmpeg
_inputFile = inputFile;
_outputFile = outputFile;
IMediaAnalysis analysis = await FFProbe.AnalyseAsync(_inputFile, _options);
_audioCodec = analysis.AudioStreams.First().CodecName;
_videoCodec = analysis.VideoStreams.First().CodecName;
FFMpegArgumentProcessor ffmpegArguments = FFMpegArguments.FromFileInput(inputFile, true, async (options) => await BuildInputArgumentOptions(options))
.OutputToFile(outputFile, true, async (options) => await BuildOutputArgumentOptions(options));
@@ -154,17 +161,13 @@ namespace VDownload.Services.Utility.FFmpeg
private async Task BuildOutputArgumentOptions(FFMpegArgumentOptions options)
{
IMediaAnalysis analysis = await FFProbe.AnalyseAsync(_inputFile, _options);
string audioCodec = analysis.AudioStreams.First().CodecName;
string videoCodec = analysis.VideoStreams.First().CodecName;
string extension = Path.GetExtension(_outputFile).Replace(".", string.Empty);
Data.Configuration.Models.Muxer muxer = _configurationService.Common.Processing.Muxers.First(x => x.Extension == extension);
if (_mediaType != MediaType.OnlyAudio)
{
IEnumerable<string> availableCodecs = muxer.VideoCodecs;
string selectedCodec = availableCodecs.Contains(videoCodec) ? "copy" : availableCodecs.First();
string selectedCodec = availableCodecs.Contains(_videoCodec) ? "copy" : availableCodecs.First();
options.WithCustomArgument($"-vcodec {selectedCodec}");
}
else
@@ -175,7 +178,7 @@ namespace VDownload.Services.Utility.FFmpeg
if (_mediaType != MediaType.OnlyVideo)
{
IEnumerable<string> availableCodecs = muxer.AudioCodecs;
string selectedCodec = availableCodecs.Contains(audioCodec) ? "copy" : availableCodecs.First();
string selectedCodec = availableCodecs.Contains(_audioCodec) ? "copy" : availableCodecs.First();
options.WithCustomArgument($"-acodec {selectedCodec}");
}
else

View File

@@ -145,15 +145,17 @@ namespace VDownload.Sources.Twitch.Models
{
token.ThrowIfCancellationRequested();
using (FileStream inputStream = File.OpenRead(path))
using (FileStream inputStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
inputStream.CopyTo(outputStream);
}
if (deleteSource)
{
File.Delete(path);
}
}
}
foreach (string item in sourceFiles)
{
if (deleteSource)
{
File.Delete(item);
}
}
}
@@ -184,10 +186,7 @@ namespace VDownload.Sources.Twitch.Models
int retriesCount = 0;
while (true)
{
if (token.IsCancellationRequested)
{
return;
}
token.ThrowIfCancellationRequested();
try
{
byte[] data = await _httpClient.GetByteArrayAsync(chunk.Url, token);
@@ -195,10 +194,6 @@ namespace VDownload.Sources.Twitch.Models
onTaskEndSuccessfully.Invoke();
return;
}
catch (OperationCanceledException)
{
return;
}
catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException)
{
if (_settingsService.Data.Twitch.Vod.ChunkDownloadingError.Retry && retriesCount < _settingsService.Data.Twitch.Vod.ChunkDownloadingError.RetriesCount)