Skip to content

Commit

Permalink
Add retry button to failed and canceled tasks (#1194)
Browse files Browse the repository at this point in the history
* Delete TwitchTaskException

* Hide cancel button when tasks cannot be canceled

* Remove public setters for ITwitchTask.Info and ITwitchTask.TokenSource

* Add reinitialization logic

* Reorder task context menu

* Update editorconfig

* Add translations

* Fix chained chat render tasks not starting after being reinitialized
  • Loading branch information
ScrubN authored Aug 27, 2024
1 parent 3a844c2 commit 818aa28
Show file tree
Hide file tree
Showing 24 changed files with 238 additions and 83 deletions.
7 changes: 6 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@

[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,c++m,cc,ccm,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,cxxm,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,mxx,nuspec,paml,razor,resw,resx,shader,skin,tpp,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}]
[*.{appxmanifest,asax,ascx,aspx,axaml,build,c,c++,c++m,cc,ccm,cginc,compute,cp,cpp,cppm,cs,cshtml,cu,cuh,cxx,cxxm,dtd,fs,fsi,fsscript,fsx,fx,fxh,h,hh,hlsl,hlsli,hlslinc,hpp,hxx,inc,inl,ino,ipp,ixx,master,ml,mli,mpp,mq4,mq5,mqh,mxx,nuspec,paml,razor,resw,shader,skin,tpp,usf,ush,uxml,vb,xaml,xamlx,xoml,xsd}]
indent_style = space
indent_size = 4
tab_width = 4

[*.resx]
indent_style = space
indent_size = 2
tab_width = 4

[*.cs]

# Microsoft .NET properties
Expand Down
1 change: 1 addition & 0 deletions TwitchDownloaderWPF/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/SkinDefault.xaml" />
<ResourceDictionary Source="pack://application:,,,/HandyControl;component/Themes/Theme.xaml" />
<!-- Top dictionaries MUST be HandyControl, other merged dictionaries below -->
<ResourceDictionary Source="pack://application:,,,/WpfExtensions.Xaml;component/converters/importoperators.xaml" />
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="AppBackground" Color="#FFFFFF" />
<SolidColorBrush x:Key="AppElementBackground" Color="#FFFFFF" />
Expand Down
19 changes: 13 additions & 6 deletions TwitchDownloaderWPF/PageQueue.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:fa="http://schemas.fontawesome.com/icons/"
xmlns:markup="clr-namespace:WpfExtensions.Xaml.Markup;assembly=WpfExtensions.Xaml"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="800"
Title="PageQueue" Loaded="Page_Loaded">
Expand Down Expand Up @@ -45,14 +46,19 @@
<fa:SvgAwesome Icon="Solid_StopCircle" Foreground="{DynamicResource AppText}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{lex:Loc TaskErrorButton}" Click="MenuItemTaskError_Click" Visibility="{Binding Exception.Visibility}" Foreground="{DynamicResource AppText}">
<MenuItem Header="{lex:Loc ContextMenuRemoveTask}" Click="MenuItemRemoveTask_Click" Foreground="{DynamicResource AppText}">
<MenuItem.Icon>
<fa:SvgAwesome Icon="Solid_ExclamationTriangle" Foreground="{DynamicResource AppText}" />
<fa:SvgAwesome Icon="Solid_Times" Foreground="{DynamicResource AppText}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{lex:Loc ContextMenuRemoveTask}" Click="MenuItemRemoveTask_Click" Foreground="{DynamicResource AppText}">
<MenuItem Header="{lex:Loc TaskRetry}" Click="MenuItemTaskRetry_Click" Visibility="{Binding CanReinitialize, Converter={StaticResource Boolean2VisibilityConverter}}" Foreground="{DynamicResource AppText}">
<MenuItem.Icon>
<fa:SvgAwesome Icon="Solid_Times" Foreground="{DynamicResource AppText}" />
<fa:SvgAwesome Icon="Solid_Redo" Foreground="{DynamicResource AppText}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{lex:Loc TaskErrorButton}" Click="MenuItemTaskError_Click" Visibility="{Binding Exception, Converter={markup:Compose {StaticResource IsNullOperator}, {StaticResource NotOperator}, {StaticResource Boolean2VisibilityConverter}}}" Foreground="{DynamicResource AppText}">
<MenuItem.Icon>
<fa:SvgAwesome Icon="Solid_ExclamationTriangle" Foreground="{DynamicResource AppText}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="{lex:Loc ContextMenuOpenTaskFolder}" Click="MenuItemOpenTaskFolder_Click" Foreground="{DynamicResource AppText}">
Expand All @@ -74,8 +80,9 @@
<TextBlock Text="{Binding TaskType, StringFormat=Task Type: {0}}" Foreground="{DynamicResource AppText}"></TextBlock>
<TextBlock Text="{Binding DisplayStatus, StringFormat=Status: {0}, Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" Foreground="{DynamicResource AppText}"></TextBlock>
<StackPanel Orientation="Horizontal" Margin="1,1,0,1">
<Button Content="{lex:Loc TaskCancel}" Margin="0,0,4,0" MinWidth="60" Height="28" FontSize="12" Click="BtnCancelTask_Click" IsEnabled="{Binding CanCancel}" Background="{DynamicResource ActionButtonBackground}" Foreground="{DynamicResource ActionButtonText}" BorderBrush="{DynamicResource ActionButtonBorder}" />
<Button Visibility="{Binding Exception.Visibility}" Content="{lex:Loc TaskErrorButton}" Margin="0,0,4,0" MinWidth="60" Height="28" FontSize="12" Click="BtnTaskError_Click" Background="{DynamicResource ActionButtonBackground}" Foreground="{DynamicResource ActionButtonText}" BorderBrush="{DynamicResource ActionButtonBorder}" />
<Button Content="{lex:Loc TaskCancel}" Margin="0,0,4,0" MinWidth="60" Height="28" FontSize="12" Click="BtnCancelTask_Click" Visibility="{Binding CanCancel, Converter={StaticResource Boolean2VisibilityConverter}}" Background="{DynamicResource ActionButtonBackground}" Foreground="{DynamicResource ActionButtonText}" BorderBrush="{DynamicResource ActionButtonBorder}" />
<Button Content="{lex:Loc TaskRetry}" Margin="0,0,4,0" MinWidth="60" Height="28" FontSize="12" Click="BtnRetryTask_Click" Visibility="{Binding CanReinitialize, Converter={StaticResource Boolean2VisibilityConverter}}" Background="{DynamicResource ActionButtonBackground}" Foreground="{DynamicResource ActionButtonText}" BorderBrush="{DynamicResource ActionButtonBorder}" />
<Button Content="{lex:Loc TaskErrorButton}" Margin="0,0,4,0" MinWidth="60" Height="28" FontSize="12" Click="BtnTaskError_Click" Visibility="{Binding Exception, Converter={markup:Compose {StaticResource IsNullOperator}, {StaticResource NotOperator}, {StaticResource Boolean2VisibilityConverter}}}" Background="{DynamicResource ActionButtonBackground}" Foreground="{DynamicResource ActionButtonText}" BorderBrush="{DynamicResource ActionButtonBorder}" />
<ProgressBar Height="16" Width="200" Minimum="0" Maximum="100" Value="{Binding Progress, Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Background="{DynamicResource AppElementInnerBackground}" Foreground="{DynamicResource ProgressBarForeground}" />
<Image MaxHeight="20" Margin="5,0" gif:ImageBehavior.AnimatedSource="{Binding StatusImage, Mode=OneWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
Expand Down
34 changes: 31 additions & 3 deletions TwitchDownloaderWPF/PageQueue.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,15 +248,15 @@ private static void ShowTaskException(ITwitchTask task)
{
var taskException = task.Exception;

if (taskException?.Exception == null)
if (taskException is null)
{
return;
}

var errorMessage = taskException.Exception.Message;
var errorMessage = taskException.Message;
if (Settings.Default.VerboseErrors)
{
errorMessage = taskException.Exception.ToString();
errorMessage = taskException.ToString();
}

MessageBox.Show(Application.Current.MainWindow!, errorMessage, Translations.Strings.MessageBoxTitleError, MessageBoxButton.OK, MessageBoxImage.Error);
Expand Down Expand Up @@ -305,5 +305,33 @@ private void MenuItemOpenTaskFolder_Click(object sender, RoutedEventArgs e)

FileService.OpenExplorerForFile(new FileInfo(task.OutputFile));
}

private void BtnRetryTask_Click(object sender, RoutedEventArgs e)
{
if (sender is not Button { DataContext: ITwitchTask task })
{
return;
}

RetryTask(task);
}

private void MenuItemTaskRetry_Click(object sender, RoutedEventArgs e)
{
if (sender is not MenuItem { DataContext: ITwitchTask task })
{
return;
}

RetryTask(task);
}

private static void RetryTask(ITwitchTask task)
{
if (task.CanReinitialize)
{
task.Reinitialize();
}
}
}
}
10 changes: 10 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.es.resx
Original file line number Diff line number Diff line change
Expand Up @@ -934,4 +934,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.fr.resx
Original file line number Diff line number Diff line change
Expand Up @@ -933,4 +933,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.it.resx
Original file line number Diff line number Diff line change
Expand Up @@ -934,4 +934,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.ja.resx
Original file line number Diff line number Diff line change
Expand Up @@ -932,4 +932,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>正確なトリミングでトリミングされた動画は、まれに最初と最後の数秒間映像や音声が途切れることがあります。セーフなトリミングでは、映像や音声が途切れることはありませんが映像が若干長くなる可能性があります。</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.pl.resx
Original file line number Diff line number Diff line change
Expand Up @@ -933,4 +933,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.pt-br.resx
Original file line number Diff line number Diff line change
Expand Up @@ -932,4 +932,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -932,4 +932,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.ru.resx
Original file line number Diff line number Diff line change
Expand Up @@ -933,4 +933,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Иногда обрезанное видео может испытывать проблемы с заиканьем в первых/последних нескольких секунд. Безопасная обрезка гарантирует отсутствие заикания, но может немного удлинить видео.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.tr.resx
Original file line number Diff line number Diff line change
Expand Up @@ -934,4 +934,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.uk.resx
Original file line number Diff line number Diff line change
Expand Up @@ -933,4 +933,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>Videos trimmed with exact trim may rarely experience video/audio stuttering within the first/last few seconds. Safe trimming is guaranteed to not stutter but may result in a slightly longer video.</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
3 changes: 3 additions & 0 deletions TwitchDownloaderWPF/Translations/Strings.zh-cn.resx
Original file line number Diff line number Diff line change
Expand Up @@ -935,4 +935,7 @@
<data name="VideoTrimModeTooltip" xml:space="preserve">
<value>使用精确模式修剪的视频在前几秒或最后几秒可能会出现视频 / 音频卡顿。安全模式保证不会出现卡顿,但可能会导致视频稍长。</value>
</data>
<data name="TaskRetry" xml:space="preserve">
<value>Retry</value>
</data>
</root>
1 change: 1 addition & 0 deletions TwitchDownloaderWPF/TwitchDownloaderWPF.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
<PackageReference Include="System.Management" Version="8.0.0" />
<PackageReference Include="WpfExtensions.Xaml" Version="1.2.0" />
<PackageReference Include="WPFLocalizeExtension" Version="3.10.0" />
<PackageReference Include="Xabe.FFmpeg.Downloader" Version="5.2.6" />
<PackageReference Include="Autoupdater.NET.Official" Version="1.9.1" />
Expand Down
37 changes: 27 additions & 10 deletions TwitchDownloaderWPF/TwitchTasks/ChatDownloadTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace TwitchDownloaderWPF.TwitchTasks
{
internal class ChatDownloadTask : ITwitchTask
{
public TaskData Info { get; set; } = new TaskData();
public TaskData Info { get; } = new();

private int _progress;
public int Progress
Expand Down Expand Up @@ -43,26 +43,33 @@ public string StatusImage
}

public ChatDownloadOptions DownloadOptions { get; init; }
public CancellationTokenSource TokenSource { get; set; } = new CancellationTokenSource();
public CancellationTokenSource TokenSource { get; private set; } = new();
public ITwitchTask DependantTask { get; set; }
public string TaskType { get; } = Translations.Strings.ChatDownload;

private TwitchTaskException _exception = new();
public TwitchTaskException Exception
private Exception _exception;
public Exception Exception
{
get => _exception;
private set => SetField(ref _exception, value);
}

public string OutputFile => DownloadOptions.Filename;

private bool _canCancel = true;
private bool _canCancel;
public bool CanCancel
{
get => _canCancel;
private set => SetField(ref _canCancel, value);
}

private bool _canReinitialize;
public bool CanReinitialize
{
get => _canReinitialize;
private set => SetField(ref _canReinitialize, value);
}

public event PropertyChangedEventHandler PropertyChanged;

public void Cancel()
Expand All @@ -83,6 +90,15 @@ public void Cancel()
ChangeStatus(TwitchTaskStatus.Canceled);
}

public void Reinitialize()
{
Progress = 0;
TokenSource = new CancellationTokenSource();
Exception = null;
CanReinitialize = false;
ChangeStatus(TwitchTaskStatus.Ready);
}

public bool CanRun()
{
return Status == TwitchTaskStatus.Ready;
Expand All @@ -93,10 +109,7 @@ public void ChangeStatus(TwitchTaskStatus newStatus)
Status = newStatus;
DisplayStatus = newStatus.ToString();

if (CanCancel && newStatus is TwitchTaskStatus.Canceled or TwitchTaskStatus.Failed or TwitchTaskStatus.Finished or TwitchTaskStatus.Stopping)
{
CanCancel = false;
}
CanCancel = newStatus is not TwitchTaskStatus.Canceled and not TwitchTaskStatus.Failed and not TwitchTaskStatus.Finished and not TwitchTaskStatus.Stopping;

StatusImage = newStatus switch
{
Expand All @@ -113,6 +126,7 @@ public async Task RunAsync()
{
TokenSource.Dispose();
ChangeStatus(TwitchTaskStatus.Canceled);
CanReinitialize = true;
return;
}

Expand All @@ -125,6 +139,7 @@ public async Task RunAsync()
if (TokenSource.IsCancellationRequested)
{
ChangeStatus(TwitchTaskStatus.Canceled);
CanReinitialize = true;
}
else
{
Expand All @@ -135,11 +150,13 @@ public async Task RunAsync()
catch (Exception ex) when (ex is OperationCanceledException or TaskCanceledException && TokenSource.IsCancellationRequested)
{
ChangeStatus(TwitchTaskStatus.Canceled);
CanReinitialize = true;
}
catch (Exception ex)
{
ChangeStatus(TwitchTaskStatus.Failed);
Exception = new TwitchTaskException(ex);
Exception = ex;
CanReinitialize = true;
}
downloader = null;
TokenSource.Dispose();
Expand Down
Loading

0 comments on commit 818aa28

Please sign in to comment.