More resilient handle of disconnection and reconnection

This commit is contained in:
Théo Marchal 2022-04-10 02:25:41 +02:00
parent 3b59e51368
commit 0ab1afc2f8
3 changed files with 170 additions and 128 deletions

View File

@ -65,12 +65,10 @@ namespace unison
private MpcConnection _connection; private MpcConnection _connection;
private MpcConnection _commandConnection; private MpcConnection _commandConnection;
private IPEndPoint _mpdEndpoint; private IPEndPoint _mpdEndpoint;
private CancellationTokenSource cancelToken; private CancellationTokenSource _cancelToken;
public MPDHandler() public MPDHandler()
{ {
cancelToken = new CancellationTokenSource();
Initialize(null, null); Initialize(null, null);
_stats = new Statistics(); _stats = new Statistics();
@ -161,7 +159,7 @@ namespace unison
IMpdMessage<T> response = await _commandConnection.SendAsync(command); IMpdMessage<T> response = await _commandConnection.SendAsync(command);
if (!response.IsResponseValid) if (!response.IsResponseValid)
{ {
var mpdError = response.Response?.Result?.MpdError; string mpdError = response.Response?.Result?.MpdError;
if (mpdError != null && mpdError != "") if (mpdError != null && mpdError != "")
throw new Exception(mpdError); throw new Exception(mpdError);
else else
@ -186,7 +184,9 @@ namespace unison
public async void Connect() public async void Connect()
{ {
CancellationToken token = cancelToken.Token; _cancelToken = new CancellationTokenSource();
CancellationToken token = _cancelToken.Token;
try try
{ {
_connection = await ConnectInternal(token); _connection = await ConnectInternal(token);
@ -194,8 +194,10 @@ namespace unison
} }
catch (MpcNET.Exceptions.MpcConnectException e) catch (MpcNET.Exceptions.MpcConnectException e)
{ {
_connected = false;
_invalidIp = true; _invalidIp = true;
Trace.WriteLine($"Error in connect: {e.Message}"); Trace.WriteLine($"Error in connect: {e.Message}");
ConnectionChanged?.Invoke(this, EventArgs.Empty);
return; return;
} }
if (_connection != null && _commandConnection != null) if (_connection != null && _commandConnection != null)
@ -210,6 +212,7 @@ namespace unison
} }
else else
{ {
_connected = false;
ConnectionChanged?.Invoke(this, EventArgs.Empty); ConnectionChanged?.Invoke(this, EventArgs.Empty);
return; return;
} }
@ -261,12 +264,16 @@ namespace unison
return connection; return connection;
} }
private void Disconnected() public void Disconnected()
{ {
_connected = false; _connected = false;
_connection = null;
_commandConnection = null;
ConnectionChanged?.Invoke(this, EventArgs.Empty); ConnectionChanged?.Invoke(this, EventArgs.Empty);
if (_connection != null)
_connection = null;
if (_commandConnection != null)
_commandConnection = null;
} }
private void Loop(CancellationToken token) private void Loop(CancellationToken token)
@ -277,27 +284,29 @@ namespace unison
{ {
try try
{ {
token.ThrowIfCancellationRequested();
if (token.IsCancellationRequested || _connection == null) if (token.IsCancellationRequested || _connection == null)
break; break;
var idleChanges = await _connection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options")); IMpdMessage<string> idleChanges = await _connection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options update"));
if (idleChanges.IsResponseValid) if (idleChanges.IsResponseValid)
await HandleIdleResponseAsync(idleChanges.Response.Content); await HandleIdleResponseAsync(idleChanges.Response.Content);
else else
{ {
Trace.WriteLine($"Error in Idle connection thread: {idleChanges.Response?.Content}"); Trace.WriteLine($"Error in Idle connection thread: {idleChanges.Response?.Content}");
//throw new Exception(idleChanges.Response?.Content); throw new Exception(idleChanges.Response?.Content);
} }
} }
catch (Exception e) catch (Exception e)
{ {
Trace.WriteLine($"Error in Idle connection thread: {e.Message}"); Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
Disconnected(); Disconnected();
break;
} }
} }
}).ConfigureAwait(false); }, token).ConfigureAwait(false);
} }
private async Task HandleIdleResponseAsync(string subsystems) private async Task HandleIdleResponseAsync(string subsystems)
@ -383,12 +392,12 @@ namespace unison
if (_connection == null) if (_connection == null)
return; return;
var albumReq = await _connection.SendAsync(new AlbumArtCommand(path, currentSize)); IMpdMessage<MpdBinaryData> albumReq = await _connection.SendAsync(new AlbumArtCommand(path, currentSize));
if (!albumReq.IsResponseValid) if (!albumReq.IsResponseValid)
break; break;
var response = albumReq.Response.Content; MpdBinaryData response = albumReq.Response.Content;
if (response.Binary == 0) if (response == null || response.Binary == 0)
break; break;
totalBinarySize = response.Size; totalBinarySize = response.Size;
@ -512,7 +521,6 @@ namespace unison
public void AddSong(string Uri) public void AddSong(string Uri)
{ {
Debug.WriteLine("AddCommand path: " + Uri);
SendCommand(new AddCommand(Uri)); SendCommand(new AddCommand(Uri));
} }
@ -526,6 +534,8 @@ namespace unison
{ {
Dictionary<string, string> response = await SafelySendCommandAsync(new StatsCommand()); Dictionary<string, string> response = await SafelySendCommandAsync(new StatsCommand());
if (response != null)
{
_stats.Songs = int.Parse(response["songs"]); _stats.Songs = int.Parse(response["songs"]);
_stats.Albums = int.Parse(response["albums"]); _stats.Albums = int.Parse(response["albums"]);
_stats.Artists = int.Parse(response["artists"]); _stats.Artists = int.Parse(response["artists"]);
@ -543,3 +553,4 @@ namespace unison
} }
} }
} }
}

View File

@ -33,12 +33,12 @@
<StackPanel> <StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{x:Static properties:Resources.Settings_Host}" TextWrapping="Wrap" Margin="5,0,0,0"/> <TextBlock Text="{x:Static properties:Resources.Settings_Host}" TextWrapping="Wrap" Margin="5,0,0,0"/>
<TextBox x:Name="MpdHost" KeyDown="ConnectHandler" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/> <TextBox x:Name="MpdHost" KeyDown="ConnectHandler" TextChanged="MpdConnectTextBox" 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="{x:Static properties:Resources.Settings_Port}" TextWrapping="Wrap" Margin="5,0,0,0"/> <TextBlock Text="{x:Static properties:Resources.Settings_Port}" TextWrapping="Wrap" Margin="5,0,0,0"/>
<TextBox x:Name="MpdPort" KeyDown="ConnectHandler" MaxLength="5" PreviewTextInput="NumberValidationTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/> <TextBox x:Name="MpdPort" KeyDown="ConnectHandler" PreviewTextInput="NumberValidationTextBox" MaxLength="5" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
</StackPanel> </StackPanel>
<!--<StackPanel Margin="0,5,0,0"> <!--<StackPanel Margin="0,5,0,0">

View File

@ -37,6 +37,7 @@ namespace unison
HotkeyHandler _hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"]; HotkeyHandler _hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"];
public Settings() public Settings()
{ {
InitHwnd(); InitHwnd();
@ -62,6 +63,134 @@ namespace unison
InitializeShortcuts(); InitializeShortcuts();
} }
public void UpdateConnectionStatus()
{
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
if (mpd.IsConnected())
{
ConnectionStatus.Text = $"{unison.Resources.Resources.Settings_ConnectionStatusConnected} {mpd.GetVersion()}.";
ConnectButton.IsEnabled = false;
}
else
{
ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusOffline;
ConnectButton.IsEnabled = true;
}
}
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
private void MpdConnectTextBox(object sender, TextChangedEventArgs e)
{
TextBox textBox = (TextBox)sender;
if (textBox.Text == Properties.Settings.Default.mpd_host)
ConnectButton.IsEnabled = false;
else
ConnectButton.IsEnabled = true;
e.Handled = true;
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
ProcessStartInfo psi = new(e.Uri.AbsoluteUri);
psi.UseShellExecute = true;
Process.Start(psi);
e.Handled = true;
}
private void MPDConnect_Clicked(object sender, RoutedEventArgs e)
{
if (!ConnectButton.IsEnabled)
return;
SaveSettings();
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
if (mpd.IsConnected())
mpd = new MPDHandler();
ConnectButton.IsEnabled = false;
ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusConnecting;
System.Threading.Tasks.Task.Run(() => { mpd.Connect(); });
}
private void SnapcastReset_Clicked(object sender, RoutedEventArgs e)
{
SnapcastPath.Text = (string)Application.Current.FindResource("snapcastPath");
SnapcastPort.Text = (string)Application.Current.FindResource("snapcastPort");
}
public void UpdateStats()
{
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
StatSong.Text = mpd.GetStats().Songs.ToString();
StatAlbum.Text = mpd.GetStats().Albums.ToString();
StatArtist.Text = mpd.GetStats().Artists.ToString();
StatTotalPlaytime.Text = mpd.GetStats().TotalPlaytime.ToString();
StatUptime.Text = mpd.GetStats().Uptime.ToString();
StatTotalTimePlayed.Text = mpd.GetStats().TotalTimePlayed.ToString();
StatDatabaseUpdate.Text = mpd.GetStats().DatabaseUpdate.ToString();
}
private void ConnectHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
MPDConnect_Clicked(null, null);
}
private void Window_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
SaveSettings();
WindowState = WindowState.Minimized;
Hide();
}
public void InitHwnd()
{
WindowInteropHelper helper = new(this);
helper.EnsureHandle();
}
public void SaveSettings()
{
Properties.Settings.Default.mpd_host = MpdHost.Text;
Properties.Settings.Default.mpd_port = int.Parse(MpdPort.Text, CultureInfo.InvariantCulture);
//Properties.Settings.Default.mpd_password = MpdPassword.Text;
Properties.Settings.Default.snapcast_startup = (bool)SnapcastStartup.IsChecked;
Properties.Settings.Default.snapcast_window = (bool)SnapcastWindow.IsChecked;
Properties.Settings.Default.snapcast_path = SnapcastPath.Text;
Properties.Settings.Default.snapcast_port = int.Parse(SnapcastPort.Text, CultureInfo.InvariantCulture);
Properties.Settings.Default.volume_offset = int.Parse(VolumeOffset.Text, CultureInfo.InvariantCulture);
Properties.Settings.Default.nextTrack_mod = GetMod(Shortcut_NextTrack);
Properties.Settings.Default.nextTrack_vk = GetVk(Shortcut_NextTrack);
Properties.Settings.Default.previousTrack_mod = GetMod(Shortcut_PreviousTrack);
Properties.Settings.Default.previousTrack_vk = GetVk(Shortcut_PreviousTrack);
Properties.Settings.Default.playPause_mod = GetMod(Shortcut_PlayPause);
Properties.Settings.Default.playPause_vk = GetVk(Shortcut_PlayPause);
Properties.Settings.Default.volumeUp_mod = GetMod(Shortcut_VolumeUp);
Properties.Settings.Default.volumeUp_vk = GetVk(Shortcut_VolumeUp);
Properties.Settings.Default.volumeDown_mod = GetMod(Shortcut_VolumeDown);
Properties.Settings.Default.volumeDown_vk = GetVk(Shortcut_VolumeDown);
Properties.Settings.Default.volumeMute_mod = GetMod(Shortcut_VolumeMute);
Properties.Settings.Default.volumeMute_vk = GetVk(Shortcut_VolumeMute);
Properties.Settings.Default.showWindow_mod = GetMod(Shortcut_ShowWindow);
Properties.Settings.Default.showWindow_vk = GetVk(Shortcut_ShowWindow);
Properties.Settings.Default.Save();
}
// Hotkeys
void InitializeShortcuts() void InitializeShortcuts()
{ {
System.Collections.Generic.IEnumerable<StackPanel> stackPanelCollection = RebindKeyWrapper.Children.OfType<StackPanel>(); System.Collections.Generic.IEnumerable<StackPanel> stackPanelCollection = RebindKeyWrapper.Children.OfType<StackPanel>();
@ -108,55 +237,6 @@ namespace unison
} }
} }
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
ProcessStartInfo psi = new(e.Uri.AbsoluteUri);
psi.UseShellExecute = true;
Process.Start(psi);
e.Handled = true;
}
public void UpdateConnectionStatus()
{
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
if (mpd.IsConnected())
ConnectionStatus.Text = $"{unison.Resources.Resources.Settings_ConnectionStatusConnected} {mpd.GetVersion()}.";
else
ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusOffline;
}
private void MPDConnect_Clicked(object sender, RoutedEventArgs e)
{
SaveSettings();
ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusConnecting;
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
mpd.Connect();
}
private void SnapcastReset_Clicked(object sender, RoutedEventArgs e)
{
SnapcastPath.Text = (string)Application.Current.FindResource("snapcastPath");
SnapcastPort.Text = (string)Application.Current.FindResource("snapcastPort");
}
public void UpdateStats()
{
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
StatSong.Text = mpd.GetStats().Songs.ToString();
StatAlbum.Text = mpd.GetStats().Albums.ToString();
StatArtist.Text = mpd.GetStats().Artists.ToString();
StatTotalPlaytime.Text = mpd.GetStats().TotalPlaytime.ToString();
StatUptime.Text = mpd.GetStats().Uptime.ToString();
StatTotalTimePlayed.Text = mpd.GetStats().TotalTimePlayed.ToString();
StatDatabaseUpdate.Text = mpd.GetStats().DatabaseUpdate.ToString();
}
private void HotkeyChanged() private void HotkeyChanged()
{ {
_hotkeys.RemoveHotkeys(); _hotkeys.RemoveHotkeys();
@ -307,54 +387,5 @@ namespace unison
TextBlock textBlock = (TextBlock)button.Content; TextBlock textBlock = (TextBlock)button.Content;
return (uint)(HotkeyHandler.VK)System.Enum.Parse(typeof(HotkeyHandler.VK), textBlock.Text, true); return (uint)(HotkeyHandler.VK)System.Enum.Parse(typeof(HotkeyHandler.VK), textBlock.Text, true);
} }
public void SaveSettings()
{
Properties.Settings.Default.mpd_host = MpdHost.Text;
Properties.Settings.Default.mpd_port = int.Parse(MpdPort.Text, CultureInfo.InvariantCulture);
//Properties.Settings.Default.mpd_password = MpdPassword.Text;
Properties.Settings.Default.snapcast_startup = (bool)SnapcastStartup.IsChecked;
Properties.Settings.Default.snapcast_window = (bool)SnapcastWindow.IsChecked;
Properties.Settings.Default.snapcast_path = SnapcastPath.Text;
Properties.Settings.Default.snapcast_port = int.Parse(SnapcastPort.Text, CultureInfo.InvariantCulture);
Properties.Settings.Default.volume_offset = int.Parse(VolumeOffset.Text, CultureInfo.InvariantCulture);
Properties.Settings.Default.nextTrack_mod = GetMod(Shortcut_NextTrack);
Properties.Settings.Default.nextTrack_vk = GetVk(Shortcut_NextTrack);
Properties.Settings.Default.previousTrack_mod = GetMod(Shortcut_PreviousTrack);
Properties.Settings.Default.previousTrack_vk = GetVk(Shortcut_PreviousTrack);
Properties.Settings.Default.playPause_mod = GetMod(Shortcut_PlayPause);
Properties.Settings.Default.playPause_vk = GetVk(Shortcut_PlayPause);
Properties.Settings.Default.volumeUp_mod = GetMod(Shortcut_VolumeUp);
Properties.Settings.Default.volumeUp_vk = GetVk(Shortcut_VolumeUp);
Properties.Settings.Default.volumeDown_mod = GetMod(Shortcut_VolumeDown);
Properties.Settings.Default.volumeDown_vk = GetVk(Shortcut_VolumeDown);
Properties.Settings.Default.volumeMute_mod = GetMod(Shortcut_VolumeMute);
Properties.Settings.Default.volumeMute_vk = GetVk(Shortcut_VolumeMute);
Properties.Settings.Default.showWindow_mod = GetMod(Shortcut_ShowWindow);
Properties.Settings.Default.showWindow_vk = GetVk(Shortcut_ShowWindow);
Properties.Settings.Default.Save();
}
private void ConnectHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
MPDConnect_Clicked(null, null);
}
private void Window_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
SaveSettings();
WindowState = WindowState.Minimized;
Hide();
}
public void InitHwnd()
{
WindowInteropHelper helper = new(this);
helper.EnsureHandle();
}
} }
} }