Cleaned things for shuffle, but still has some deadlocks and crashes

This commit is contained in:
Théo Marchal 2022-04-18 01:02:26 +02:00
parent 792437b839
commit 1d3515a39d
6 changed files with 231 additions and 201 deletions

View File

@ -9,6 +9,7 @@ namespace unison
private TaskbarIcon _systray; private TaskbarIcon _systray;
private HotkeyHandler _hotkeys; private HotkeyHandler _hotkeys;
private SnapcastHandler _snapcast; private SnapcastHandler _snapcast;
private ShuffleHandler _shuffle;
private MPDHandler _mpd; private MPDHandler _mpd;
protected override void OnStartup(StartupEventArgs e) protected override void OnStartup(StartupEventArgs e)
@ -29,6 +30,9 @@ namespace unison
_snapcast = new SnapcastHandler(); _snapcast = new SnapcastHandler();
Current.Properties["snapcast"] = _snapcast; Current.Properties["snapcast"] = _snapcast;
_shuffle = new ShuffleHandler();
Current.Properties["shuffle"] = _shuffle;
Current.MainWindow = new MainWindow(); Current.MainWindow = new MainWindow();
_systray = (TaskbarIcon)FindResource("SystrayTaskbar"); _systray = (TaskbarIcon)FindResource("SystrayTaskbar");

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@ -34,7 +35,7 @@ namespace unison
public class MPDHandler public class MPDHandler
{ {
private bool _connected; private bool _connected;
public string _version; private string _version;
private int _currentVolume; private int _currentVolume;
private int _previousVolume; private int _previousVolume;
private bool _currentRandom; private bool _currentRandom;
@ -43,6 +44,7 @@ namespace unison
private bool _currentConsume; private bool _currentConsume;
private double _currentTime; private double _currentTime;
private double _totalTime; private double _totalTime;
private IEnumerable<IMpdFile> _Playlist;
private MpdStatus _currentStatus; private MpdStatus _currentStatus;
private IMpdFile _currentSong; private IMpdFile _currentSong;
@ -155,9 +157,7 @@ namespace unison
try try
{ {
Debug.WriteLine("SafelySendCommandAsync => before command");
IMpdMessage<T> response = await _commandConnection.SendAsync(command); IMpdMessage<T> response = await _commandConnection.SendAsync(command);
Debug.WriteLine("SafelySendCommandAsync => after command");
if (!response.IsResponseValid) if (!response.IsResponseValid)
{ {
string mpdError = response.Response?.Result?.MpdError; string mpdError = response.Response?.Result?.MpdError;
@ -492,14 +492,26 @@ namespace unison
public string GetVersion() => _version; public string GetVersion() => _version;
public Statistics GetStats() => _stats; public Statistics GetStats() => _stats;
public double GetCurrentTime() => _currentTime; public double GetCurrentTime() => _currentTime;
public IEnumerable<IMpdFile> GetPlaylist() => _Playlist;
public bool IsConnected() => _connected; public bool IsConnected() => _connected;
public bool IsPlaying() => _currentStatus?.State == MpdState.Play; public bool IsPlaying() => _currentStatus?.State == MpdState.Play;
public void Prev() => SendCommand(new PreviousCommand()); public bool CanPrevNext = true;
public void Next() => SendCommand(new NextCommand());
public void PlayPause() => SendCommand(new PauseResumeCommand());
public void Prev()
{
if (CanPrevNext)
SendCommand(new PreviousCommand());
}
public void Next()
{
if (CanPrevNext)
SendCommand(new NextCommand());
}
public void PlayPause() => SendCommand(new PauseResumeCommand());
public void Play(int pos) => SendCommand(new PlayCommand(pos)); public void Play(int pos) => SendCommand(new PlayCommand(pos));
public void Random() => SendCommand(new RandomCommand(!_currentRandom)); public void Random() => SendCommand(new RandomCommand(!_currentRandom));
@ -555,6 +567,15 @@ namespace unison
SendCommand(commandList); SendCommand(commandList);
} }
public async Task QueryPlaylist() => _Playlist = await SafelySendCommandAsync(new PlaylistCommand());
public int GetPlaylistCount()
{
if (_Playlist == null)
return 0;
return _Playlist.ToArray().Count();
}
public async void QueryStats() public async void QueryStats()
{ {
Dictionary<string, string> Response = await SafelySendCommandAsync(new StatsCommand()); Dictionary<string, string> Response = await SafelySendCommandAsync(new StatsCommand());

134
Handlers/ShuffleHandler.cs Normal file
View File

@ -0,0 +1,134 @@
using MpcNET.Commands.Database;
using MpcNET.Commands.Queue;
using MpcNET.Tags;
using MpcNET.Types;
using MpcNET.Types.Filters;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace unison
{
class ShuffleHandler
{
private MPDHandler _mpd;
public List<string> _songList { get; }
public int AddedSongs = 0;
public ShuffleHandler()
{
_songList = new();
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
}
private bool IsOnMainThread()
{
return Application.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread;
}
public async Task GetSongsFromFilter(List<IFilter> filter)
{
Debug.WriteLine("[GetSongsFromFilterBefore] is on main thread => " + IsOnMainThread());
await Task.Run(async() =>
{
Debug.WriteLine("[GetSongsFromFilterAfter] is on main thread => " + IsOnMainThread());
_songList.Clear();
int song = _mpd.GetStats().Songs;
IEnumerable<IMpdFile> response = await _mpd.SafelySendCommandAsync(new SearchCommand(filter, 0, song + 1));
Debug.WriteLine("got response => " + response.Count());
foreach (IMpdFile file in response)
{
_songList.Add(file.Path);
Debug.WriteLine(file.Path);
}
});
}
public async Task AddToQueueRandom(int SongNumber)
{
Debug.WriteLine("Add To Queue Random");
await Task.Run(async () =>
{
int AddedSongs = 0;
Debug.WriteLine("song to add => " + SongNumber);
for (int i = 0; i < SongNumber; i++)
{
// generate random number
int song = new Random().Next(0, _mpd.GetStats().Songs - 1);
Debug.WriteLine("song " + song + " - song total " + _mpd.GetStats().Songs);
IEnumerable<IMpdFile> Response = await _mpd.SafelySendCommandAsync(new SearchCommand(new FilterTag(MpdTags.Title, "", FilterOperator.Contains), song, song + 1));
Debug.WriteLine("got response");
await Task.Delay(1);
if (Response.Count() > 0)
{
string filePath = Response.First().Path;
_mpd.AddSong(filePath);
Debug.WriteLine("song path => " + filePath);
if (i == 0)
{
if (!_mpd.IsPlaying())
_mpd.Play(0);
}
AddedSongs++;
}
}
});
Debug.WriteLine("Add To Queue Random - finished");
}
public async Task AddToQueueFilter(int SongNumber)
{
Debug.WriteLine("Add To Queue Filter");
await Task.Run(async () =>
{
int AddedSongs = 0;
Debug.WriteLine("song to add => " + SongNumber);
// more requested songs than available => add everything
if (SongNumber > _songList.Count)
{
foreach (string path in _songList)
{
await Task.Delay(1);
_mpd.AddSong(path);
Debug.WriteLine("song path => " + path);
AddedSongs++;
}
}
// more available songs than requested =>
// we add unique indexes until we reach the requested amount
else
{
HashSet<int> SongIndex = new();
Debug.WriteLine("while - before");
while (SongIndex.Count < SongNumber)
{
int MaxIndex = new Random().Next(0, _songList.Count - 1);
SongIndex.Add(MaxIndex);
}
foreach (int index in SongIndex)
_mpd.AddSong(_songList[index]);
Debug.WriteLine("while - after");
}
});
Debug.WriteLine("Add To Queue Filter - finished");
}
}
}

View File

@ -6,8 +6,9 @@ using System.Windows.Threading;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using MpcNET.Commands.Queue;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace unison namespace unison
{ {
@ -53,6 +54,8 @@ namespace unison
_mpd.QueryStats(); _mpd.QueryStats();
_settingsWin.UpdateStats(); _settingsWin.UpdateStats();
_mpd.QueryPlaylist().ConfigureAwait(false);
Snapcast.IsEnabled = true; Snapcast.IsEnabled = true;
ConnectionOkIcon.Visibility = Visibility.Visible; ConnectionOkIcon.Visibility = Visibility.Visible;
ConnectionFailIcon.Visibility = Visibility.Collapsed; ConnectionFailIcon.Visibility = Visibility.Collapsed;
@ -121,11 +124,21 @@ namespace unison
if (!_shuffleWin.GetContinuous()) if (!_shuffleWin.GetContinuous())
return; return;
NextTrack.IsEnabled = false; await Task.Run(async () =>
PreviousTrack.IsEnabled = false; {
Debug.WriteLine("start continuous");
await _mpd.QueryPlaylist();
Debug.WriteLine("queried playlist");
});
if (_mpd.GetPlaylistCount() > 5)
return;
_mpd.CanPrevNext = false;
await _shuffleWin.HandleContinuous(); await _shuffleWin.HandleContinuous();
NextTrack.IsEnabled = true; _mpd.CanPrevNext = true;
PreviousTrack.IsEnabled = true;
Debug.WriteLine("finished continuous"); Debug.WriteLine("finished continuous");
} }
@ -227,19 +240,8 @@ namespace unison
} }
public void Pause_Clicked(object sender, RoutedEventArgs e) => _mpd.PlayPause(); public void Pause_Clicked(object sender, RoutedEventArgs e) => _mpd.PlayPause();
public void Previous_Clicked(object sender, RoutedEventArgs e) => _mpd.Prev();
public void Previous_Clicked(object sender, RoutedEventArgs e) public void Next_Clicked(object sender, RoutedEventArgs e) => _mpd.Next();
{
if (PreviousTrack.IsEnabled)
_mpd.Prev();
}
public void Next_Clicked(object sender, RoutedEventArgs e)
{
if (NextTrack.IsEnabled)
_mpd.Next();
}
public void Random_Clicked(object sender, RoutedEventArgs e) => _mpd.Random(); public void Random_Clicked(object sender, RoutedEventArgs e) => _mpd.Random();
public void Repeat_Clicked(object sender, RoutedEventArgs e) => _mpd.Repeat(); public void Repeat_Clicked(object sender, RoutedEventArgs e) => _mpd.Repeat();
public void Single_Clicked(object sender, RoutedEventArgs e) => _mpd.Single(); public void Single_Clicked(object sender, RoutedEventArgs e) => _mpd.Single();

View File

@ -68,6 +68,7 @@
<StackPanel Orientation="Horizontal" Margin="0,5,0,5"> <StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Button Content="Query filter" Click="UpdateFilter_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}" Margin="0,0,10,0"/> <Button Content="Query filter" Click="UpdateFilter_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}" Margin="0,0,10,0"/>
<Button Content="Reset" Click="Reset_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/> <Button Content="Reset" Click="Reset_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
<TextBlock Text="Querying filter..." Margin="15,3,0,0" FontStyle="Italic" Visibility="Visible" />
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
@ -85,7 +86,7 @@
<StackPanel Orientation="Vertical" Margin="5,5,5,0"> <StackPanel Orientation="Vertical" Margin="5,5,5,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Text="Songs to add" Margin="0,0,5,5"/> <TextBlock Text="Songs to add" Margin="0,0,5,5"/>
<TextBox x:Name="SongNumber" PreviewTextInput="QueueValidationTextBox" MaxLength="4" Text="100" Width="35" HorizontalAlignment="Left" VerticalAlignment="Top"/> <TextBox x:Name="SongNumber" PreviewTextInput="QueueValidationTextBox" MaxLength="4" Text="15" Width="35" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0"> <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<Button Content="Add to queue" Click="AddToQueue_Clicked" Padding="5, 2" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/> <Button Content="Add to queue" Click="AddToQueue_Clicked" Padding="5, 2" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>

View File

@ -1,6 +1,4 @@
using MpcNET; using MpcNET.Commands.Database;
using MpcNET.Commands.Database;
using MpcNET.Commands.Reflection;
using MpcNET.Tags; using MpcNET.Tags;
using MpcNET.Types; using MpcNET.Types;
using MpcNET.Types.Filters; using MpcNET.Types.Filters;
@ -14,7 +12,6 @@ using System.Windows.Controls;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Media; using System.Windows.Media;
using System.Linq; using System.Linq;
using MpcNET.Commands.Queue;
using System.Windows.Input; using System.Windows.Input;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -23,8 +20,8 @@ namespace unison
public partial class Shuffle : Window public partial class Shuffle : Window
{ {
private MPDHandler _mpd; private MPDHandler _mpd;
private ShuffleHandler _shuffle;
bool _continuous = false; bool _continuous = false;
List<string> _songList { get; }
List<string> _genreList { get; } List<string> _genreList { get; }
List<string> _folderList { get; } List<string> _folderList { get; }
List<IFilter> _filters { get; } List<IFilter> _filters { get; }
@ -32,13 +29,13 @@ namespace unison
public Shuffle() public Shuffle()
{ {
InitializeComponent(); InitializeComponent();
_songList = new();
_genreList = new(); _genreList = new();
_folderList = new(); _folderList = new();
_filters = new(); _filters = new();
SongFilterNumber.Text = "0"; SongFilterNumber.Text = "0";
_mpd = (MPDHandler)Application.Current.Properties["mpd"]; _mpd = (MPDHandler)Application.Current.Properties["mpd"];
_shuffle = (ShuffleHandler)Application.Current.Properties["shuffle"];
} }
public void Initialize() public void Initialize()
@ -113,8 +110,6 @@ namespace unison
return FindParent<T>(parent); return FindParent<T>(parent);
} }
private void AddFilter_Clicked(object sender, RoutedEventArgs e) private void AddFilter_Clicked(object sender, RoutedEventArgs e)
{ {
FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") }); FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") });
@ -135,6 +130,7 @@ namespace unison
FilterPanel.Children.RemoveRange(0, FilterPanel.Children.Count); FilterPanel.Children.RemoveRange(0, FilterPanel.Children.Count);
FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") }); FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") });
SongFilterNumber.Text = "0"; SongFilterNumber.Text = "0";
_shuffle._songList.Clear();
} }
private ITag FilterEquivalence_Type(string value) private ITag FilterEquivalence_Type(string value)
@ -165,6 +161,13 @@ namespace unison
private async void UpdateFilter_Clicked(object sender, RoutedEventArgs e) private async void UpdateFilter_Clicked(object sender, RoutedEventArgs e)
{ {
await UpdateFilter();
}
private async Task UpdateFilter()
{
Debug.WriteLine("update filter => start");
_filters.Clear(); _filters.Clear();
Debug.WriteLine("is on main thread => " + IsOnMainThread()); Debug.WriteLine("is on main thread => " + IsOnMainThread());
@ -206,60 +209,13 @@ namespace unison
else else
_filters.Add(new FilterBase(value, FilterOperator.None)); _filters.Add(new FilterBase(value, FilterOperator.None));
await GetSongsFromFilter(); await _shuffle.GetSongsFromFilter(_filters);
SongFilterPanel.Visibility = Visibility.Visible;
SongFilterNumber.Text = _shuffle._songList.Count.ToString();
} }
} }
}
private async Task GetSongsFromFilter() Debug.WriteLine("update filter => stop");
{
Debug.WriteLine("get songs from filter => start");
Debug.WriteLine("is on main thread => " + IsOnMainThread());
_songList.Clear();
SongFilterPanel.Visibility = Visibility.Visible;
int song = _mpd.GetStats().Songs;
Debug.WriteLine("get songs from filter => before command");
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new SearchCommand(_filters, 0, song + 1) });
string Response = await _mpd.SafelySendCommandAsync(commandList);
Debug.WriteLine("get songs from filter => after command");
// create a list of the file url
string[] value = Response.Split(", [file, ");
// there are no song in this filter
if (value[0] == "")
{
SongFilterNumber.Text = _songList.Count.ToString();
return;
}
Debug.WriteLine("get songs from filter => before adding to list");
foreach (string file in value)
{
int start = 0;
int end = file.IndexOf("],");
string filePath = file.Substring(start, end - start);
_songList.Add(filePath);
SongFilterNumber.Text = _songList.Count.ToString();
}
Debug.WriteLine("get songs from filter => after adding to list");
// remove characters from first file
_songList[0] = _songList[0].Substring(7, _songList[0].Length - 7);
SongFilterPanel.Visibility = Visibility.Visible;
SongFilterNumber.Text = _songList.Count.ToString();
Debug.WriteLine("get songs from filter => finish");
} }
private void FilterType_Change(object sender, string Operator, List<string> Listing) private void FilterType_Change(object sender, string Operator, List<string> Listing)
@ -340,14 +296,6 @@ namespace unison
SongNumber.Text = "1000"; SongNumber.Text = "1000";
} }
private async void AddToQueue_Clicked(object sender, RoutedEventArgs e) private async void AddToQueue_Clicked(object sender, RoutedEventArgs e)
{ {
QueueValidationNumber(); QueueValidationNumber();
@ -355,93 +303,37 @@ namespace unison
if (_mpd.GetStats() == null) if (_mpd.GetStats() == null)
return; return;
NumberAddedSongs.Text = "0";
SearchStatus.Visibility = Visibility.Visible;
// start dispatcher
// write _shuffle.AddedSongs in dispatcher
await AddToQueue(int.Parse(SongNumber.Text));
Debug.WriteLine("add to queue finished");
SearchStatus.Visibility = Visibility.Collapsed;
}
private async Task AddToQueue(int NumberToAdd)
{
await UpdateFilter();
Debug.WriteLine("check filters");
if (IsFilterEmpty()) if (IsFilterEmpty())
await AddToQueueRandom(int.Parse(SongNumber.Text)); // @TODO await or not??? await _shuffle.AddToQueueRandom(NumberToAdd);
else else
{ {
UpdateFilter_Clicked(null, null); Debug.WriteLine("add to queue filter - before");
await AddToQueueFilter(int.Parse(SongNumber.Text)); // @TODO await or not??? await _shuffle.AddToQueueFilter(NumberToAdd);
Debug.WriteLine("add to queue filter - after");
} }
Debug.WriteLine("add to queue finished");
} }
private async /*void*/ Task AddToQueueRandom(int SongNumber)
{
int AddedSongs = 0;
NumberAddedSongs.Text = AddedSongs.ToString();
SearchStatus.Visibility = Visibility.Visible;
Debug.WriteLine("song to add => " + SongNumber);
for (int i = 0; i < SongNumber; i++)
{
// generate random number
int song = new Random().Next(0, _mpd.GetStats().Songs - 1);
// query random song
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new SearchCommand(new FilterTag(MpdTags.Title, "", FilterOperator.Contains), song, song + 1) });
string Response = await _mpd.SafelySendCommandAsync(commandList);
await Task.Delay(1);
if (Response.Length > 0)
{
// parse song and add it to queue
int start = Response.IndexOf("[file, ");
int end = Response.IndexOf("],");
string filePath = Response.Substring(start + 7, end - (start + 7));
_mpd.AddSong(filePath);
Debug.WriteLine("song path => " + filePath);
AddedSongs++;
NumberAddedSongs.Text = AddedSongs.ToString();
}
}
// TODO make play at first position added, as soon as possible
if (!_mpd.IsPlaying())
_mpd.Play(0);
SearchStatus.Visibility = Visibility.Collapsed;
}
private async /*void*/ Task AddToQueueFilter(int SongNumber)
{
int AddedSongs = 0;
NumberAddedSongs.Text = AddedSongs.ToString();
SearchStatus.Visibility = Visibility.Visible;
Debug.WriteLine("song to add => " + SongNumber);
// more requested songs than available => add everything
if (SongNumber > _songList.Count)
{
foreach (string path in _songList)
{
await Task.Delay(1);
_mpd.AddSong(path);
Debug.WriteLine("song path => " + path);
AddedSongs++;
NumberAddedSongs.Text = AddedSongs.ToString();
}
}
// more available songs than requested =>
// we add unique indexes until we reach the requested amount
else
{
HashSet<int> SongIndex = new();
while (SongIndex.Count < SongNumber)
{
int MaxIndex = new Random().Next(0, _songList.Count - 1);
SongIndex.Add(MaxIndex);
}
foreach (int index in SongIndex)
_mpd.AddSong(_songList[index]);
}
SearchStatus.Visibility = Visibility.Collapsed;
}
public bool GetContinuous() public bool GetContinuous()
{ {
return _continuous; return _continuous;
@ -449,45 +341,21 @@ namespace unison
public async Task HandleContinuous() public async Task HandleContinuous()
{ {
if (!GetContinuous()) if (!_continuous)
return; return;
Debug.WriteLine("is on main thread => " + IsOnMainThread()); await AddToQueue(5);
IEnumerable<IMpdFile> Playlist = await _mpd.SafelySendCommandAsync(new PlaylistCommand());
int queueSize = 0;
foreach (IMpdFile file in Playlist)
{
Debug.WriteLine(file.Path);
queueSize++;
}
Debug.WriteLine("queue size is: " + queueSize);
if (queueSize < 5)
{
if (_mpd.GetStats() == null)
return;
if (IsFilterEmpty())
await AddToQueueRandom(5); // @TODO await or not?
else
await AddToQueueFilter(5); // @TODO await or not?
}
} }
private async void ContinuousShuffle_Checked(object sender, RoutedEventArgs e) private async void ContinuousShuffle_Checked(object sender, RoutedEventArgs e)
{ {
if (ContinuousShuffle.IsChecked == true) if (ContinuousShuffle.IsChecked == true)
{
_continuous = true; _continuous = true;
_songList.Clear();
if (!IsFilterEmpty())
UpdateFilter_Clicked(null, null);
}
else else
_continuous = false; _continuous = false;
await HandleContinuous(); if (_mpd.GetPlaylistCount() < 5)
await HandleContinuous();
} }
} }
} }