Basic shuffle features, must fix deadlocks

This commit is contained in:
Théo Marchal 2022-04-17 16:02:09 +02:00
parent c5e8534af7
commit 5bfa7d3b5b
5 changed files with 451 additions and 256 deletions

View File

@ -155,7 +155,9 @@ namespace unison
try
{
Debug.WriteLine("SafelySendCommandAsync => before command");
IMpdMessage<T> response = await _commandConnection.SendAsync(command);
Debug.WriteLine("SafelySendCommandAsync => after command");
if (!response.IsResponseValid)
{
string mpdError = response.Response?.Result?.MpdError;
@ -498,6 +500,8 @@ namespace unison
public void Next() => SendCommand(new NextCommand());
public void PlayPause() => SendCommand(new PauseResumeCommand());
public void Play(int pos) => SendCommand(new PlayCommand(pos));
public void Random() => SendCommand(new RandomCommand(!_currentRandom));
public void Repeat() => SendCommand(new RepeatCommand(!_currentRepeat));
public void Single() => SendCommand(new SingleCommand(!_currentSingle));

View File

@ -50,12 +50,13 @@ namespace unison
{
if (_mpd.IsConnected())
{
_mpd.QueryStats();
_settingsWin.UpdateStats();
Snapcast.IsEnabled = true;
ConnectionOkIcon.Visibility = Visibility.Visible;
ConnectionFailIcon.Visibility = Visibility.Collapsed;
_shuffleWin.ListGenre();
_shuffleWin.ListFolder();
_shuffleWin.Initialize();
}
else
{
@ -117,29 +118,15 @@ namespace unison
Debug.WriteLine("Song changed called!");
// handle continuous shuffle
if (_shuffleWin.GetContinuous())
{
System.Collections.Generic.IEnumerable<MpcNET.Types.IMpdFile> a = await _mpd.SafelySendCommandAsync(new PlaylistCommand());
int queueSize = 0;
foreach (var i in a)
{
Debug.WriteLine(i.Path);
queueSize++;
}
Debug.WriteLine("queue size is: " + queueSize);
if (!_shuffleWin.GetContinuous())
return;
if (queueSize < 5)
{
_shuffleWin.AddContinuousSongs();
}
// query queue
// if (queue.SongRemaining < 5)
//{
// // query shuffle songs
//}
}
NextTrack.IsEnabled = false;
PreviousTrack.IsEnabled = false;
await _shuffleWin.HandleContinuous();
NextTrack.IsEnabled = true;
PreviousTrack.IsEnabled = true;
Debug.WriteLine("finished continuous");
}
public void OnStatusChanged(object sender, EventArgs e)
@ -168,9 +155,6 @@ namespace unison
DefaultState();
}
}
_mpd.QueryStats();
_settingsWin.UpdateStats();
}
private void DefaultState(bool LostConnection = false)
@ -243,8 +227,18 @@ namespace unison
}
public void Pause_Clicked(object sender, RoutedEventArgs e) => _mpd.PlayPause();
public void Previous_Clicked(object sender, RoutedEventArgs e) => _mpd.Prev();
public void Next_Clicked(object sender, RoutedEventArgs e) => _mpd.Next();
public void Previous_Clicked(object sender, RoutedEventArgs e)
{
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 Repeat_Clicked(object sender, RoutedEventArgs e) => _mpd.Repeat();

View File

@ -208,19 +208,16 @@
</GroupBox.Header>
<Grid MaxWidth="500">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBox TextWrapping="Wrap" Width="25" PreviewTextInput="NumberValidationTextBox" Margin="0,2,0,0"/>
<TextBlock Text="Prevent repetition rate (0-100%)" TextWrapping="Wrap" Margin="5,2,0,0"/>
</StackPanel>
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0">
<TextBlock TextWrapping="Wrap">
<Run>The shuffle window allows to add random songs to your queue. Both options take into account the filter.</Run>
<Run>If the filter is empty, the entire music library is taken into account.</Run><LineBreak/><LineBreak/>
<Run FontWeight="Bold">Continuous shuffle</Run><LineBreak/>
<Run>By enabling this option, unison will automatically add songs to the queue so you never run out of songs to listen to.</Run>
<LineBreak/><LineBreak/>
<Run FontWeight="Bold">Add to queue</Run><LineBreak/>
<Run>Add a fixed number of songs to the queue. It can take a long time to add more than 100 songs, so the option is limited to 1000 songs.</Run>
<LineBreak/><LineBreak/>
<Run FontWeight="Bold">Continuous shuffle</Run><LineBreak/>
<Run>By enabling this option, unison will automatically add songs to the queue so you never run out of songs to listen to.</Run>
</TextBlock>
</StackPanel>
</Grid>

View File

@ -4,10 +4,44 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
xmlns:local="clr-namespace:unison"
xmlns:local="clr-namespace:unison" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
Title="Shuffle" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<Window.Resources>
<x:Array x:Key="FilterType" Type="sys:String">
<sys:String>Song</sys:String>
<sys:String>Artist</sys:String>
<sys:String>Album</sys:String>
<sys:String>Year</sys:String>
<sys:String>Genre</sys:String>
<sys:String>Directory</sys:String>
</x:Array>
<x:Array x:Key="OperatorTypeA" Type="sys:String">
<sys:String>contains</sys:String>
<sys:String>is</sys:String>
<sys:String>is not</sys:String>
</x:Array>
<x:Array x:Key="OperatorTypeB" Type="sys:String">
<sys:String>is</sys:String>
<sys:String>is not</sys:String>
</x:Array>
<x:Array x:Key="OperatorTypeC" Type="sys:String">
<sys:String>is</sys:String>
</x:Array>
<DataTemplate x:Key="FilterPanel">
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<ComboBox x:Name="FilterType" SelectionChanged="FilterType_SelectionChanged" ItemsSource="{StaticResource FilterType}" SelectedIndex="0" Width="100" ScrollViewer.CanContentScroll="False" FocusVisualStyle="{x:Null}"/>
<ComboBox x:Name="FilterOperator" SelectionChanged="OperatorType_SelectionChanged" ItemsSource="{StaticResource OperatorTypeA}" SelectedIndex="0" Width="80" ScrollViewer.CanContentScroll="False" Margin="5,0,0,0" FocusVisualStyle="{x:Null}"/>
<ComboBox x:Name="FilterList" SelectedIndex="0" Width="240" Visibility="Collapsed" ScrollViewer.CanContentScroll="False" Margin="5,0,0,0" FocusVisualStyle="{x:Null}"/>
<TextBox x:Name="FilterValue" Width="240" Margin="5,0,0,0"/>
<Button Content="-" Padding="5, 2" Click="RemoveFilter_Clicked" Width="20" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}" Margin="5,0,0,0"/>
<Button Content="+" Padding="5, 2" Click="AddFilter_Clicked" Width="20" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,5">
@ -19,83 +53,63 @@
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Vertical" Margin="5,0,5,0">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<TextBlock Text="Song" Margin="0,0,0,2"/>
<TextBox x:Name="Song" Width="240" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
<TextBlock Text="Artist" Margin="0,0,0,2"/>
<TextBox x:Name="Artist" Width="240" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel x:Name="FilterPanel">
<ContentPresenter ContentTemplate="{StaticResource FilterPanel}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<StackPanel Orientation="Vertical">
<TextBlock Text="Album" Margin="0,0,0,2"/>
<TextBox x:Name="Album" Width="240" Margin="5,0,0,0"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
<TextBlock Text="Year" Margin="0,0,0,2"/>
<TextBox x:Name="Year" Width="240" Margin="5,0,0,0"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<StackPanel Orientation="Vertical">
<TextBlock Text="Genre" Margin="0,0,0,2"/>
<ComboBox x:Name="Genre" SelectedIndex="0" Width="240" ScrollViewer.CanContentScroll="False" Margin="5,0,0,0" FocusVisualStyle="{x:Null}"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
<TextBlock Text="Directory" Margin="0,0,0,2" TextDecorations="{x:Null}"/>
<ComboBox x:Name="Directory" SelectedIndex="0" Width="240" ScrollViewer.CanContentScroll="False" Margin="5,0,0,0" IsEnabled="True"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Button Content="Reset" Click="Reset_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
</StackPanel>
<StackPanel x:Name="SongFilterPanel" Margin="0,5,0,0" Visibility="Collapsed">
<StackPanel x:Name="SongFilterPanel" Margin="0,10,0,0">
<TextBlock>
<Run Text="Number of songs in filter: "/><Run x:Name="SongFilterNumber" FontWeight="Bold"/>
</TextBlock>
</StackPanel>
<StackPanel Margin="0,5,0,0">
<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="Reset" Click="Reset_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox Margin="0,5,0,0" HorizontalAlignment="Stretch">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="♾️"/>
<Run Text="Continuous shuffle"/>
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Horizontal" Margin="5,8,0,0">
<CheckBox x:Name="ContinuousShuffle" Checked="ContinuousShuffle_Checked" Unchecked="ContinuousShuffle_Checked" FocusVisualStyle="{x:Null}">
<TextBlock Text="Enable continuous shuffle" TextWrapping="Wrap"/>
</CheckBox>
</StackPanel>
</GroupBox>
<GroupBox x:Name="AddToQueueGroup" Margin="0,5,0,0" HorizontalAlignment="Stretch">
<GroupBox.Header>
<TextBlock>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<GroupBox DockPanel.Dock="Right" Padding="0,4,0,0" Width="248">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text=""/>
<Run Text="Add to queue"/>
</TextBlock>
</GroupBox.Header>
<StackPanel Margin="5,0,0,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,10,0,0">
<TextBox x:Name="SongNumber" Text="100" Width="35" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBlock Text="Number of songs to add" Margin="5,0,0,5"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2,0,0">
<Button Content="Add to queue" Click="AddToQueue_Clicked" Padding="5, 2" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
<TextBlock x:Name="SearchStatus" Margin="15,3,0,0" FontStyle="Italic" Visibility="Collapsed">
<Run Text="Added "/><Run x:Name="NumberAddedSongs"/><Run Text=" songs to the queue..."/>
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Vertical" Margin="5,5,5,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<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"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<Button Content="Add to queue" Click="AddToQueue_Clicked" Padding="5, 2" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
<TextBlock x:Name="SearchStatus" Margin="15,3,0,0" FontStyle="Italic" Visibility="Collapsed">
<Run Text="Added "/><Run x:Name="NumberAddedSongs"/><Run Text=" songs"/>
</TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
</GroupBox>
</GroupBox>
<GroupBox DockPanel.Dock="Left" Padding="0,4,0,0" Width="248" Margin="0,0,5,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="♾️"/>
<Run Text="Continuous shuffle"/>
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Horizontal" Margin="5,7,5,0">
<CheckBox x:Name="ContinuousShuffle" Checked="ContinuousShuffle_Checked" Unchecked="ContinuousShuffle_Checked" FocusVisualStyle="{x:Null}" VerticalAlignment="Top">
<TextBlock Text="Enable continuous shuffle" TextWrapping="Wrap"/>
</CheckBox>
</StackPanel>
</GroupBox>
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>

View File

@ -10,7 +10,13 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Linq;
using MpcNET.Commands.Queue;
using System.Windows.Input;
using System.Text.RegularExpressions;
namespace unison
{
@ -19,81 +25,61 @@ namespace unison
private MPDHandler _mpd;
bool _continuous = false;
List<string> _songList { get; }
List<string> _genreList { get; }
List<string> _folderList { get; }
List<IFilter> _filters { get; }
public Shuffle()
{
InitializeComponent();
_songList = new();
_genreList = new();
_folderList = new();
_filters = new();
SongFilterNumber.Text = "0";
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
}
private bool IsFilterEmpty()
public void Initialize()
{
if (Song.Text.Length == 0 && Artist.Text.Length == 0 && Album.Text.Length == 0 && Year.Text.Length == 0 && Genre.SelectedIndex == 0 && Directory.Text.Length == 0)
return true;
return false;
}
public bool GetContinuous()
{
return _continuous;
}
public void AddContinuousSongs()
{
if (IsFilterEmpty())
{
// add a completely random song
ContinuousShuffle_AddToQueueRandom();
return;
}
int AddedSongs = 0;
NumberAddedSongs.Text = AddedSongs.ToString();
SearchStatus.Visibility = Visibility.Visible;
HashSet<int> SongIndex = new();
while (SongIndex.Count < 2)
{
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;
ListGenre();
ListFolder();
}
public async void ListGenre()
{
if (Genre.Items.Count == 0)
{
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
List<string> Response = await _mpd.SafelySendCommandAsync(new ListCommand(MpdTags.Genre, null, null));
if (_genreList.Count != 0)
return;
if (Response.Count > 0)
{
Genre.Items.Add("");
foreach (var genre in Response)
Genre.Items.Add(genre);
}
}
List<string> Response = await _mpd.SafelySendCommandAsync(new ListCommand(MpdTags.Genre, null, null));
if (Response == null)
return;
foreach (string genre in Response)
_genreList.Add(genre);
}
public async void ListFolder()
{
if (Directory.Items.Count == 0)
{
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
IEnumerable<IMpdFilePath> Response = await _mpd.SafelySendCommandAsync(new LsInfoCommand(""));
if (_folderList.Count != 0)
return;
if (Response != null)
{
Directory.Items.Add("");
foreach (var directory in Response)
Directory.Items.Add(directory.Name);
}
}
IEnumerable<IMpdFilePath> Response = await _mpd.SafelySendCommandAsync(new LsInfoCommand(""));
if (Response == null)
return;
foreach (IMpdFilePath folder in Response)
_folderList.Add(folder.Name);
}
private bool IsFilterEmpty()
{
if (_filters.Count() == 0)
return true;
return false;
}
private void Window_Closing(object sender, CancelEventArgs e)
@ -109,120 +95,139 @@ namespace unison
helper.EnsureHandle();
}
private bool IsOnMainThread()
{
return App.Current.Dispatcher.Thread == System.Threading.Thread.CurrentThread;
}
private T FindParent<T>(DependencyObject child) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(child);
if (parent == null)
return null;
if (parent is T)
return parent as T;
else
return FindParent<T>(parent);
}
private void AddFilter_Clicked(object sender, RoutedEventArgs e)
{
FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") });
SongFilterNumber.Text = "0";
}
private void RemoveFilter_Clicked(object sender, RoutedEventArgs e)
{
if (FilterPanel.Children.Count > 1)
FilterPanel.Children.Remove(FindParent<ContentPresenter>(sender as Button));
else
Reset_Clicked(null, null);
SongFilterNumber.Text = "0";
}
private void Reset_Clicked(object sender, RoutedEventArgs e)
{
Song.Text = "";
Artist.Text = "";
Album.Text = "";
Year.Text = "";
Genre.SelectedIndex = 0;
Directory.SelectedIndex = 0;
FilterPanel.Children.RemoveRange(0, FilterPanel.Children.Count);
FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") });
SongFilterNumber.Text = "0";
}
private async void ContinuousShuffle_Checked(object sender, RoutedEventArgs e)
private ITag FilterEquivalence_Type(string value)
{
if (ContinuousShuffle.IsChecked == true)
{
AddToQueueGroup.IsEnabled = false;
_continuous = true;
_songList.Clear();
if (!IsFilterEmpty())
{
/*await*/
GetSongsFromFilter();
}
}
else
{
AddToQueueGroup.IsEnabled = true;
_continuous = false;
}
if (value == "Song")
return MpdTags.Title;
else if (value == "Artist")
return MpdTags.Artist;
else if (value == "Album")
return MpdTags.Album;
else if (value == "Year")
return MpdTags.Date;
else if (value == "Genre")
return MpdTags.Genre;
return MpdTags.Title;
}
private async void ContinuousShuffle_AddToQueueRandom()
private FilterOperator FilterEquivalence_Operator(string value)
{
for (int i = 0; i < 2; 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(MpdTags.Title, "", 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);
}
}
SearchStatus.Visibility = Visibility.Collapsed;
if (value == "contains")
return FilterOperator.Contains;
else if (value == "is")
return FilterOperator.Equal;
else if (value == "is not")
return FilterOperator.Different;
return FilterOperator.Equal;
}
private async void AddToQueueRandom()
private async void UpdateFilter_Clicked(object sender, RoutedEventArgs e)
{
int AddedSongs = 0;
NumberAddedSongs.Text = AddedSongs.ToString();
SearchStatus.Visibility = Visibility.Visible;
_filters.Clear();
for (int i = 0; i < int.Parse(SongNumber.Text); i++)
Debug.WriteLine("is on main thread => " + IsOnMainThread());
foreach (ContentPresenter superChild in FilterPanel.Children)
{
// generate random number
int song = new Random().Next(0, _mpd.GetStats().Songs - 1);
ITag tag = MpdTags.Title;
FilterOperator op = FilterOperator.None;
string value = "";
bool isDir = false;
// query random song
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new SearchCommand(MpdTags.Title, "", song, song + 1) });
string Response = await _mpd.SafelySendCommandAsync(commandList);
await Task.Delay(1);
if (Response.Length > 0)
StackPanel stackPanel = VisualTreeHelper.GetChild(superChild, 0) as StackPanel;
foreach (TextBox child in stackPanel.Children.OfType<TextBox>())
{
// 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);
if (child.Name == "FilterValue")
value = child.Text;
}
foreach (ComboBox child in stackPanel.Children.OfType<ComboBox>())
{
if (child.Name == "FilterType")
{
if (child.SelectedItem.ToString() == "Directory")
isDir = true;
else
tag = FilterEquivalence_Type(child.SelectedItem.ToString());
}
AddedSongs++;
NumberAddedSongs.Text = AddedSongs.ToString();
if (child.Name == "FilterOperator")
op = FilterEquivalence_Operator(child.SelectedItem.ToString());
if (child.Name == "FilterList" && child.Visibility == Visibility.Visible)
value = child.SelectedItem.ToString();
}
if (value != "")
{
if (!isDir)
_filters.Add(new FilterTag(tag, value, op));
else
_filters.Add(new FilterBase(value, FilterOperator.None));
await GetSongsFromFilter();
}
}
SearchStatus.Visibility = Visibility.Collapsed;
}
private async Task GetSongsFromFilter()
{
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;
List<IFilter> filtersA = new();
if (Song.Text != "")
filtersA.Add(new FilterTag(MpdTags.Title, Song.Text, FilterOperator.Contains));
if (Artist.Text != "")
filtersA.Add(new FilterTag(MpdTags.Artist, Artist.Text, FilterOperator.Contains));
if (Album.Text != "")
filtersA.Add(new FilterTag(MpdTags.Album, Album.Text, FilterOperator.Contains));
if (Year.Text != "")
filtersA.Add(new FilterTag(MpdTags.Date, Year.Text, FilterOperator.Contains));
if (Genre.Text != "")
filtersA.Add(new FilterTag(MpdTags.Genre, Genre.Text, FilterOperator.Contains));
if (Directory.Text != "")
filtersA.Add(new FilterBase(Directory.Text, FilterOperator.None));
Debug.WriteLine("get songs from filter => before command");
Debug.WriteLine(Directory.Text);
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new SearchCommand(filtersA, 0, song + 1) });
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new SearchCommand(_filters, 0, song + 1) });
string Response = await _mpd.SafelySendCommandAsync(commandList);
Debug.WriteLine(Response);
Debug.WriteLine("get songs from filter => after command");
// create a list of the file url
string[] value = Response.Split(", [file, ");
@ -234,40 +239,185 @@ namespace unison
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);
Debug.WriteLine(filePath);
_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 async void AddToQueueFilter()
private void FilterType_Change(object sender, string Operator, List<string> Listing)
{
await GetSongsFromFilter();
ComboBox comboBox = sender as ComboBox;
StackPanel stackPanel = comboBox.Parent as StackPanel;
foreach (ComboBox child in stackPanel.Children.OfType<ComboBox>())
{
if (child.Name == "FilterOperator")
{
child.ItemsSource = (Array)FindResource(Operator);
child.SelectedItem = child.Items[0];
}
if (child.Name == "FilterList")
{
child.Visibility = Visibility.Visible;
child.ItemsSource = Listing;
child.SelectedItem = child.Items[0];
}
}
foreach (TextBox child in stackPanel.Children.OfType<TextBox>())
{
if (child.Name == "FilterValue")
child.Visibility = Visibility.Collapsed;
}
SongFilterNumber.Text = "0";
}
private void FilterType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string item = e.AddedItems[0].ToString();
if (item == "Genre")
FilterType_Change(sender, "OperatorTypeB", _genreList);
else if (item == "Directory")
FilterType_Change(sender, "OperatorTypeC", _folderList);
else
{
ComboBox combobox = sender as ComboBox;
StackPanel stackpanel = combobox.Parent as StackPanel;
foreach (ComboBox child in stackpanel.Children.OfType<ComboBox>())
{
if (child.Name == "FilterOperator")
{
child.ItemsSource = (Array)FindResource("OperatorTypeA");
child.SelectedItem = child.Items[0];
}
if (child.Name == "FilterList")
child.Visibility = Visibility.Collapsed;
}
foreach (TextBox child in stackpanel.Children.OfType<TextBox>())
{
if (child.Name == "FilterValue")
child.Visibility = Visibility.Visible;
}
}
SongFilterNumber.Text = "0";
}
private void OperatorType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Debug.WriteLine("selection changed => operator type");
SongFilterNumber.Text = "0";
}
private void QueueValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
private void QueueValidationNumber()
{
if (int.Parse(SongNumber.Text) < 1)
SongNumber.Text = "1";
if (int.Parse(SongNumber.Text) > 1000)
SongNumber.Text = "1000";
}
private async void AddToQueue_Clicked(object sender, RoutedEventArgs e)
{
QueueValidationNumber();
if (_mpd.GetStats() == null)
return;
if (IsFilterEmpty())
await AddToQueueRandom(int.Parse(SongNumber.Text)); // @TODO await or not???
else
{
UpdateFilter_Clicked(null, null);
await AddToQueueFilter(int.Parse(SongNumber.Text)); // @TODO await or not???
}
}
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 (int.Parse(SongNumber.Text) > _songList.Count)
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();
@ -278,7 +428,7 @@ namespace unison
else
{
HashSet<int> SongIndex = new();
while (SongIndex.Count < int.Parse(SongNumber.Text))
while (SongIndex.Count < SongNumber)
{
int MaxIndex = new Random().Next(0, _songList.Count - 1);
SongIndex.Add(MaxIndex);
@ -289,19 +439,55 @@ namespace unison
}
SearchStatus.Visibility = Visibility.Collapsed;
}
private void AddToQueue_Clicked(object sender, RoutedEventArgs e)
public bool GetContinuous()
{
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
if (_mpd.GetStats() == null)
return _continuous;
}
public async Task HandleContinuous()
{
if (!GetContinuous())
return;
if (IsFilterEmpty())
AddToQueueRandom();
Debug.WriteLine("is on main thread => " + IsOnMainThread());
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)
{
if (ContinuousShuffle.IsChecked == true)
{
_continuous = true;
_songList.Clear();
if (!IsFilterEmpty())
UpdateFilter_Clicked(null, null);
}
else
AddToQueueFilter();
_continuous = false;
await HandleContinuous();
}
}
}
}