MPD handles disconnection and reconnection

This commit is contained in:
Théo Marchal 2021-09-02 00:14:28 +02:00
parent 2c8696155a
commit c568a957f7
10 changed files with 91 additions and 36 deletions

View File

@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Threading;
using MpcNET; using MpcNET;
using MpcNET.Commands.Database; using MpcNET.Commands.Database;
using MpcNET.Commands.Playback; using MpcNET.Commands.Playback;
@ -34,6 +35,7 @@ namespace unison
private IMpdFile _currentSong; private IMpdFile _currentSong;
private BitmapFrame _cover; private BitmapFrame _cover;
private readonly System.Timers.Timer _elapsedTimer; private readonly System.Timers.Timer _elapsedTimer;
private DispatcherTimer _retryTimer;
private event EventHandler ConnectionChanged; private event EventHandler ConnectionChanged;
private event EventHandler StatusChanged; private event EventHandler StatusChanged;
@ -49,7 +51,11 @@ namespace unison
{ {
cancelToken = new CancellationTokenSource(); cancelToken = new CancellationTokenSource();
Initialize(); Initialize(null, null);
_retryTimer = new DispatcherTimer();
_retryTimer.Interval = TimeSpan.FromSeconds(5);
_retryTimer.Tick += Initialize;
_elapsedTimer = new System.Timers.Timer(500); _elapsedTimer = new System.Timers.Timer(500);
_elapsedTimer.Elapsed += new System.Timers.ElapsedEventHandler(ElapsedTimer); _elapsedTimer.Elapsed += new System.Timers.ElapsedEventHandler(ElapsedTimer);
@ -68,8 +74,13 @@ namespace unison
_elapsedTimer.Stop(); _elapsedTimer.Stop();
} }
static void OnConnectionChanged(object sender, EventArgs e) void OnConnectionChanged(object sender, EventArgs e)
{ {
if (!_connected)
_retryTimer.Start();
else
_retryTimer.Stop();
Application.Current.Dispatcher.Invoke(() => Application.Current.Dispatcher.Invoke(() =>
{ {
MainWindow MainWin = (MainWindow)Application.Current.MainWindow; MainWindow MainWin = (MainWindow)Application.Current.MainWindow;
@ -107,9 +118,10 @@ namespace unison
}); });
} }
private void Initialize() private void Initialize(object sender, EventArgs e)
{ {
Connect(); if (!_connected)
Connect();
} }
public async void Connect() public async void Connect()
@ -124,11 +136,19 @@ namespace unison
{ {
Trace.WriteLine("exception: " + exception); Trace.WriteLine("exception: " + exception);
} }
if (_connection.IsConnected) if (_connection != null && _commandConnection != null)
{
if (_connection.IsConnected && _commandConnection.IsConnected)
{
_connected = true;
_version = _connection.Version;
ConnectionChanged?.Invoke(this, EventArgs.Empty);
}
}
else
{ {
_connected = true;
_version = _connection.Version;
ConnectionChanged?.Invoke(this, EventArgs.Empty); ConnectionChanged?.Invoke(this, EventArgs.Empty);
return;
} }
await UpdateStatusAsync(); await UpdateStatusAsync();
@ -179,13 +199,24 @@ namespace unison
} }
catch (Exception e) catch (Exception e)
{ {
System.Diagnostics.Debug.WriteLine($"Error in Idle connection thread: {e.Message}"); Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
Disconnected();
} }
} }
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
private void Disconnected()
{
_connected = false;
_connection = null;
_commandConnection = null;
ConnectionChanged?.Invoke(this, EventArgs.Empty);
}
private async Task HandleIdleResponseAsync(string subsystems) private async Task HandleIdleResponseAsync(string subsystems)
{ {
try try
@ -262,8 +293,22 @@ namespace unison
_isUpdatingSong = false; _isUpdatingSong = false;
} }
public void SendCommand<T>(IMpcCommand<T> command)
{
Task.Run(async () =>
{
await SafelySendCommandAsync(command);
});
}
public async Task<T> SafelySendCommandAsync<T>(IMpcCommand<T> command) public async Task<T> SafelySendCommandAsync<T>(IMpcCommand<T> command)
{ {
if (_commandConnection == null)
{
Trace.WriteLine("[SafelySendCommandAsync] no command connection");
return default(T);
}
try try
{ {
IMpdMessage<T> response = await _commandConnection.SendAsync(command); IMpdMessage<T> response = await _commandConnection.SendAsync(command);
@ -379,17 +424,17 @@ namespace unison
public bool IsConnected() => _connected; public bool IsConnected() => _connected;
public bool IsPlaying() => _currentStatus?.State == MpdState.Play; public bool IsPlaying() => _currentStatus?.State == MpdState.Play;
public async void Prev() => await SafelySendCommandAsync(new PreviousCommand()); public void Prev() => SendCommand(new PreviousCommand());
public async void Next() => await SafelySendCommandAsync(new NextCommand()); public void Next() => SendCommand(new NextCommand());
public async void PlayPause() =>await SafelySendCommandAsync(new PauseResumeCommand()); public void PlayPause() => SendCommand(new PauseResumeCommand());
public async void Random() => await SafelySendCommandAsync(new RandomCommand(!_currentRandom)); public void Random() => SendCommand(new RandomCommand(!_currentRandom));
public async void Repeat() => await SafelySendCommandAsync(new RepeatCommand(!_currentRepeat)); public void Repeat() => SendCommand(new RepeatCommand(!_currentRepeat));
public async void Single() => await SafelySendCommandAsync(new SingleCommand(!_currentSingle)); public void Single() => SendCommand(new SingleCommand(!_currentSingle));
public async void Consume() => await SafelySendCommandAsync(new ConsumeCommand(!_currentConsume)); public void Consume() => SendCommand(new ConsumeCommand(!_currentConsume));
public async void SetTime(double value) => await SafelySendCommandAsync(new SeekCurCommand(value)); public void SetTime(double value) => SendCommand(new SeekCurCommand(value));
public async void SetVolume(int value) => await SafelySendCommandAsync(new SetVolumeCommand((byte)value)); public void SetVolume(int value) => SendCommand(new SetVolumeCommand((byte)value));
public void VolumeUp() public void VolumeUp()
{ {

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 294 KiB

After

Width:  |  Height:  |  Size: 294 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -6,7 +6,7 @@
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
mc:Ignorable="d" mc:Ignorable="d"
Title="unison" Title="unison"
Closing="Window_Closing" Icon="/images/icon-full.ico" ResizeMode="CanMinimize" SizeToContent="WidthAndHeight"> Closing="Window_Closing" Icon="/Ressources/icon-full.ico" ResizeMode="CanMinimize" SizeToContent="WidthAndHeight">
<Window.Resources> <Window.Resources>
<Style TargetType="Border" x:Key="UnselectedButton"> <Style TargetType="Border" x:Key="UnselectedButton">
@ -103,8 +103,8 @@
<StackPanel.OpacityMask> <StackPanel.OpacityMask>
<VisualBrush Visual="{Binding ElementName=mask}"/> <VisualBrush Visual="{Binding ElementName=mask}"/>
</StackPanel.OpacityMask> </StackPanel.OpacityMask>
<Image x:Name="Cover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/images/nocover.png" Visibility="Collapsed" /> <Image x:Name="Cover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/Ressources/nocover.png" Visibility="Collapsed" />
<Image x:Name="NoCover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/images/nocover.png" /> <Image x:Name="NoCover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/Ressources/nocover.png" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</Border> </Border>

View File

@ -41,10 +41,19 @@ namespace unison
public void OnConnectionChanged(object sender, EventArgs e) public void OnConnectionChanged(object sender, EventArgs e)
{ {
Connection.Text = (_mpd.IsConnected() ? "✔️" : "❌") + $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
_settingsWin.UpdateConnectionStatus();
if (_mpd.IsConnected()) if (_mpd.IsConnected())
{
Snapcast.IsEnabled = true; Snapcast.IsEnabled = true;
Connection.Text = "✔️";
}
else
{
_timer.Stop();
DefaultState();
Connection.Text = "❌";
}
_settingsWin.UpdateConnectionStatus();
Connection.Text += $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
} }
public void OnSongChanged(object sender, EventArgs e) public void OnSongChanged(object sender, EventArgs e)
@ -132,6 +141,7 @@ namespace unison
SongFormat.Text = ""; SongFormat.Text = "";
CurrentTime.Text = ""; CurrentTime.Text = "";
EndTime.Text = ""; EndTime.Text = "";
PlayPause.Text = "\xedb5";
TimeSlider.Value = 50; TimeSlider.Value = 50;
TimeSlider.IsEnabled = false; TimeSlider.IsEnabled = false;
NoCover.Visibility = Visibility.Visible; NoCover.Visibility = Visibility.Visible;

View File

@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
mc:Ignorable="d" mc:Ignorable="d"
Closing="Window_Closing" Title="Settings" ResizeMode="CanMinimize" Icon="/images/icon-full.ico" WindowStyle="ToolWindow" SizeToContent="WidthAndHeight"> Closing="Window_Closing" Title="Settings" ResizeMode="CanMinimize" Icon="/Ressources/icon-full.ico" WindowStyle="ToolWindow" SizeToContent="WidthAndHeight">
<Grid> <Grid>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TabControl Margin="10"> <TabControl Margin="10">
@ -29,10 +29,10 @@
<TextBox x:Name="MpdPort" MaxLength="5" PreviewTextInput="NumberValidationTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/> <TextBox x:Name="MpdPort" MaxLength="5" PreviewTextInput="NumberValidationTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
</StackPanel> </StackPanel>
<StackPanel Margin="0,5,0,0"> <!--<StackPanel Margin="0,5,0,0">
<TextBlock Text="Password" TextWrapping="Wrap" Margin="5,0,0,0"/> <TextBlock Text="Password" TextWrapping="Wrap" Margin="5,0,0,0"/>
<TextBox x:Name="MpdPassword" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/> <TextBox x:Name="MpdPassword" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
</StackPanel> </StackPanel>-->
<TextBlock x:Name="ConnectionStatus" Text="Not connected." TextWrapping="Wrap" Margin="5,10,0,0"/> <TextBlock x:Name="ConnectionStatus" Text="Not connected." TextWrapping="Wrap" Margin="5,10,0,0"/>
<Button x:Name="ConnectButton" Content="Connect" Margin="0,10,0,0" Width="120" Click="MPDConnect_Clicked"/> <Button x:Name="ConnectButton" Content="Connect" Margin="0,10,0,0" Width="120" Click="MPDConnect_Clicked"/>

View File

@ -46,7 +46,7 @@ namespace unison
MpdHost.Text = Properties.Settings.Default.mpd_host; MpdHost.Text = Properties.Settings.Default.mpd_host;
MpdPort.Text = Properties.Settings.Default.mpd_port.ToString(); MpdPort.Text = Properties.Settings.Default.mpd_port.ToString();
MpdPassword.Text = Properties.Settings.Default.mpd_password; //MpdPassword.Text = Properties.Settings.Default.mpd_password;
SnapcastStartup.IsChecked = Properties.Settings.Default.snapcast_startup; SnapcastStartup.IsChecked = Properties.Settings.Default.snapcast_startup;
SnapcastWindow.IsChecked = Properties.Settings.Default.snapcast_window; SnapcastWindow.IsChecked = Properties.Settings.Default.snapcast_window;
SnapcastPath.Text = Properties.Settings.Default.snapcast_path; SnapcastPath.Text = Properties.Settings.Default.snapcast_path;
@ -95,7 +95,7 @@ namespace unison
{ {
Properties.Settings.Default.mpd_host = MpdHost.Text; Properties.Settings.Default.mpd_host = MpdHost.Text;
Properties.Settings.Default.mpd_port = int.Parse(MpdPort.Text, CultureInfo.InvariantCulture); Properties.Settings.Default.mpd_port = int.Parse(MpdPort.Text, CultureInfo.InvariantCulture);
Properties.Settings.Default.mpd_password = MpdPassword.Text; //Properties.Settings.Default.mpd_password = MpdPassword.Text;
Properties.Settings.Default.snapcast_startup = (bool)SnapcastStartup.IsChecked; Properties.Settings.Default.snapcast_startup = (bool)SnapcastStartup.IsChecked;
Properties.Settings.Default.snapcast_window = (bool)SnapcastWindow.IsChecked; Properties.Settings.Default.snapcast_window = (bool)SnapcastWindow.IsChecked;
Properties.Settings.Default.snapcast_path = SnapcastPath.Text; Properties.Settings.Default.snapcast_path = SnapcastPath.Text;

View File

@ -7,7 +7,7 @@
<ContextMenu x:Shared="false" x:Key="SystrayMenu"> <ContextMenu x:Shared="false" x:Key="SystrayMenu">
<MenuItem IsEnabled="False"> <MenuItem IsEnabled="False">
<MenuItem.Icon> <MenuItem.Icon>
<Image Source="/images/icon-full.ico" Width="16" Height="16"/> <Image Source="/Ressources/icon-full.ico" Width="16" Height="16"/>
</MenuItem.Icon> </MenuItem.Icon>
<MenuItem.Header> <MenuItem.Header>
<TextBlock Text="{Binding GetAppText}" /> <TextBlock Text="{Binding GetAppText}" />
@ -42,7 +42,7 @@
</MenuItem> </MenuItem>
</ContextMenu> </ContextMenu>
<tb:TaskbarIcon x:Key="SystrayTaskbar" IconSource="/images/icon-mini.ico" ToolTipText="{Binding GetAppText}" DoubleClickCommand="{Binding ShowWindowCommand}" ContextMenu="{StaticResource SystrayMenu}"> <tb:TaskbarIcon x:Key="SystrayTaskbar" IconSource="/Ressources/icon-mini.ico" ToolTipText="{Binding GetAppText}" DoubleClickCommand="{Binding ShowWindowCommand}" ContextMenu="{StaticResource SystrayMenu}">
<tb:TaskbarIcon.DataContext> <tb:TaskbarIcon.DataContext>
<local:SystrayViewModel /> <local:SystrayViewModel />
</tb:TaskbarIcon.DataContext> </tb:TaskbarIcon.DataContext>

View File

@ -4,7 +4,7 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework> <TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<ApplicationIcon>images\icon-full.ico</ApplicationIcon> <ApplicationIcon>Ressources\icon-full.ico</ApplicationIcon>
<Win32Resource></Win32Resource> <Win32Resource></Win32Resource>
<StartupObject>unison.App</StartupObject> <StartupObject>unison.App</StartupObject>
<Version>0.0.1</Version> <Version>0.0.1</Version>
@ -18,9 +18,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Remove="images\icon-full.ico" /> <None Remove="Ressources\icon-full.ico" />
<None Remove="images\icon-mini.ico" /> <None Remove="Ressources\icon-mini.ico" />
<None Remove="images\nocover.png" /> <None Remove="Ressources\nocover.png" />
<None Remove="LICENSE" /> <None Remove="LICENSE" />
<None Remove="snapclient_0.25.0-1_win64\FLAC.dll" /> <None Remove="snapclient_0.25.0-1_win64\FLAC.dll" />
<None Remove="snapclient_0.25.0-1_win64\ogg.dll" /> <None Remove="snapclient_0.25.0-1_win64\ogg.dll" />
@ -36,13 +36,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Resource Include="images\icon-full.ico"> <Resource Include="Ressources\icon-full.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource> </Resource>
<Resource Include="images\icon-mini.ico"> <Resource Include="Ressources\icon-mini.ico">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource> </Resource>
<Resource Include="images\nocover.png"> <Resource Include="Ressources\nocover.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource> </Resource>
<Content Include="LICENSE"> <Content Include="LICENSE">