Rough working shuffle system

This commit is contained in:
Théo Marchal 2021-10-06 22:54:55 +02:00
parent 3685c369b4
commit 4c71d6a6e0
4 changed files with 357 additions and 65 deletions

View File

@ -6,6 +6,8 @@ using System.Windows.Threading;
using System.Windows.Interop;
using System.Windows.Input;
using System.Windows.Controls.Primitives;
using MpcNET.Commands.Queue;
using System.Diagnostics;
namespace unison
{
@ -61,9 +63,11 @@ namespace unison
}
_settingsWin.UpdateConnectionStatus();
Connection.Text = $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
_shuffleWin.ListGenre();
}
public void OnSongChanged(object sender, EventArgs e)
public async void OnSongChanged(object sender, EventArgs e)
{
if (_mpd.GetCurrentSong() == null)
return;
@ -109,6 +113,32 @@ namespace unison
_timer.Start();
EndTime.Text = FormatSeconds(_mpd.GetCurrentSong().Time);
}
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 (queueSize < 5)
{
_shuffleWin.AddContinuousSongs();
}
// query queue
// if (queue.SongRemaining < 5)
//{
// // query shuffle songs
//}
}
}
public void OnStatusChanged(object sender, EventArgs e)

View File

@ -44,59 +44,6 @@
</DockPanel>
</TabItem>
<TabItem Header="{x:Static properties:Resources.Stats}">
<DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="📊"/>
<Run Text="{x:Static properties:Resources.Stats}"/>
</TextBlock>
</GroupBox.Header>
<Grid VerticalAlignment="Top">
<TextBlock>
<Run Text="{x:Static properties:Resources.Stats_Songs}"/><Run Text=" "/><Run x:Name="StatSong"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_Albums}"/><Run Text=" "/><Run x:Name="StatAlbum"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_Artists}"/><Run Text=" "/><Run x:Name="StatArtist"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_TotalPlaytime}"/><Run Text=" "/><Run x:Name="StatTotalPlaytime"/><LineBreak/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_Uptime}"/><Run Text=" "/><Run x:Name="StatUptime"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_TotalTimePlayed}"/><Run Text=" "/><Run x:Name="StatTotalTimePlayed"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_LastDatabaseUpdate}"/><Run Text=" "/><Run x:Name="StatDatabaseUpdate"/>
</TextBlock>
</Grid>
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="Snapcast">
<DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<emoji:TextBlock Text="🔊 Snapcast"/>
</GroupBox.Header>
<Grid VerticalAlignment="Top">
<StackPanel>
<StackPanel>
<CheckBox x:Name="SnapcastStartup" Margin="5, 5, 0, 0">
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastLauch}" TextWrapping="Wrap"/>
</CheckBox>
<CheckBox x:Name="SnapcastWindow" Margin="5,2.5,0,0">
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastWindow}" TextWrapping="Wrap"/>
</CheckBox>
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastPort}" TextWrapping="Wrap" Margin="5,5,0,0"/>
<TextBox x:Name="SnapcastPort" MaxLength="5" PreviewTextInput="NumberValidationTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,5,0" HorizontalAlignment="Left"/>
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastPath}" TextWrapping="Wrap" Margin="5,5,0,0"/>
<TextBox x:Name="SnapcastPath" TextWrapping="Wrap" Width="250" Margin="10,2,5,0" HorizontalAlignment="Left"/>
<TextBlock TextWrapping="Wrap" Margin="5,5,0,0" TextAlignment="Left" Width="250">
<Run Text="{x:Static properties:Resources.Settings_SnapcastInfo1}" /><Run Text="{x:Static properties:Resources.Settings_SnapcastInfo2}" FontStyle="Italic" FontWeight="DemiBold" /><Run Text="{x:Static properties:Resources.Settings_SnapcastInfo3}" />
</TextBlock>
<Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,10,0,0" Width="120" Click="SnapcastReset_Clicked"/>
</StackPanel>
</StackPanel>
</Grid>
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="{x:Static properties:Resources.Settings_Shortcuts}">
<DockPanel Margin="8">
@ -149,6 +96,87 @@
</DockPanel>
</TabItem>
<TabItem Header="Snapcast">
<DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<emoji:TextBlock Text="🔊 Snapcast"/>
</GroupBox.Header>
<Grid VerticalAlignment="Top">
<StackPanel>
<StackPanel>
<CheckBox x:Name="SnapcastStartup" Margin="5, 5, 0, 0">
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastLauch}" TextWrapping="Wrap"/>
</CheckBox>
<CheckBox x:Name="SnapcastWindow" Margin="5,2.5,0,0">
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastWindow}" TextWrapping="Wrap"/>
</CheckBox>
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastPort}" TextWrapping="Wrap" Margin="5,5,0,0"/>
<TextBox x:Name="SnapcastPort" MaxLength="5" PreviewTextInput="NumberValidationTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,5,0" HorizontalAlignment="Left"/>
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastPath}" TextWrapping="Wrap" Margin="5,5,0,0"/>
<TextBox x:Name="SnapcastPath" TextWrapping="Wrap" Width="250" Margin="10,2,5,0" HorizontalAlignment="Left"/>
<TextBlock TextWrapping="Wrap" Margin="5,5,0,0" TextAlignment="Left" Width="250">
<Run Text="{x:Static properties:Resources.Settings_SnapcastInfo1}" /><Run Text="{x:Static properties:Resources.Settings_SnapcastInfo2}" FontStyle="Italic" FontWeight="DemiBold" /><Run Text="{x:Static properties:Resources.Settings_SnapcastInfo3}" />
</TextBlock>
<Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,10,0,0" Width="120" Click="SnapcastReset_Clicked"/>
</StackPanel>
</StackPanel>
</Grid>
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="Shuffle">
<DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="🔁 "/>
<Run Text="Shuffle"></Run>
</TextBlock>
</GroupBox.Header>
<Grid MaxWidth="500">
<StackPanel>
<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>
</TextBlock>
</StackPanel>
</Grid>
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="{x:Static properties:Resources.Stats}">
<DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="📊"/>
<Run Text="{x:Static properties:Resources.Stats}"/>
</TextBlock>
</GroupBox.Header>
<Grid VerticalAlignment="Top">
<TextBlock>
<Run Text="{x:Static properties:Resources.Stats_Songs}"/><Run Text=" "/><Run x:Name="StatSong"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_Albums}"/><Run Text=" "/><Run x:Name="StatAlbum"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_Artists}"/><Run Text=" "/><Run x:Name="StatArtist"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_TotalPlaytime}"/><Run Text=" "/><Run x:Name="StatTotalPlaytime"/><LineBreak/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_Uptime}"/><Run Text=" "/><Run x:Name="StatUptime"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_TotalTimePlayed}"/><Run Text=" "/><Run x:Name="StatTotalTimePlayed"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_LastDatabaseUpdate}"/><Run Text=" "/><Run x:Name="StatDatabaseUpdate"/>
</TextBlock>
</Grid>
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="{x:Static properties:Resources.Settings_About}" Height="20" VerticalAlignment="Bottom">
<DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">

View File

@ -14,8 +14,8 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="🔁"/>
<Run Text="Shuffle"/>
<emoji:EmojiInline Text="🔡"/>
<Run Text="Filter"/>
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Vertical" Margin="5,0,5,0">
@ -41,20 +41,52 @@
<TextBox x:Name="Year" Width="240"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<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"/>
</StackPanel>
<Button Content="Reset" Click="Reset_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="20,0,0,0" FocusVisualStyle="{x:Null}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="0,8,0,0">
<TextBlock Text="Number of songs to add" Margin="0,0,0,5"/>
<TextBox x:Name="SongNumber" Text="100" Width="45" Margin="5,0,0,0"/>
<StackPanel x:Name="SongFilterPanel" Margin="0,5,0,0" Visibility="Collapsed">
<TextBlock>
<Run Text="Number of songs in filter: "/><Run x:Name="SongFilterNumber" FontWeight="Bold"/>
</TextBlock>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<Button Content="Add to queue" Click="AddToQueue_Clicked" Padding="5, 2"/>
<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">
<TextBlock Text="Enable continuous shuffle" TextWrapping="Wrap"/>
</CheckBox>
</StackPanel>
</GroupBox>
<GroupBox x:Name="AddToQueueGroup" Margin="0,5,0,0" HorizontalAlignment="Stretch">
<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>
</StackPanel>
</StackPanel>
</GroupBox>

View File

@ -1,16 +1,71 @@
using System.ComponentModel;
using MpcNET;
using MpcNET.Commands.Database;
using MpcNET.Commands.Reflection;
using MpcNET.Tags;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
// https://mpd.readthedocs.io/en/stable/protocol.html#binary
namespace unison
{
public partial class Shuffle : Window
{
//private MPDHandler _mpd;
private MPDHandler _mpd;
bool _continuous = false;
List<string> _songList { get; }
public Shuffle()
{
InitializeComponent();
_songList = new();
}
public bool GetContinuous()
{
return _continuous;
}
public void AddContinuousSongs()
{
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;
}
public async void ListGenre()
{
if (Genre.Items.Count == 0)
{
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
Genre.Items.Add("");
List<string> Response = await _mpd.SafelySendCommandAsync(new ListCommand(MpdTags.Genre, null, null));
foreach (var genre in Response)
Genre.Items.Add(genre);
}
}
public async void ListFolder()
{
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
//await _mpd.SafelySendCommandAsync(new )
}
private void Window_Closing(object sender, CancelEventArgs e)
@ -26,9 +81,156 @@ namespace unison
helper.EnsureHandle();
}
private void Reset_Clicked(object sender, RoutedEventArgs e)
{
Song.Text = "";
Artist.Text = "";
Album.Text = "";
Year.Text = "";
Genre.SelectedIndex = 0;
}
private async void ContinuousShuffle_Checked(object sender, RoutedEventArgs e)
{
if (ContinuousShuffle.IsChecked == true)
{
AddToQueueGroup.IsEnabled = false;
_continuous = true;
_songList.Clear();
await GetSongsFromFilter();
}
else
{
AddToQueueGroup.IsEnabled = true;
_continuous = false;
}
}
// search "((title contains ''))" window 38669:38670
// listfiles
private async void AddToQueueRandom()
{
int AddedSongs = 0;
NumberAddedSongs.Text = AddedSongs.ToString();
SearchStatus.Visibility = Visibility.Visible;
for (int i = 0; i < int.Parse(SongNumber.Text); 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);
AddedSongs++;
NumberAddedSongs.Text = AddedSongs.ToString();
}
}
SearchStatus.Visibility = Visibility.Collapsed;
}
private async Task GetSongsFromFilter()
{
_songList.Clear();
int song = _mpd.GetStats().Songs;
List<KeyValuePair<ITag, string>> filters = new();
filters.Add(new KeyValuePair<ITag, string>(MpdTags.Title, Song.Text));
filters.Add(new KeyValuePair<ITag, string>(MpdTags.Artist, Artist.Text));
filters.Add(new KeyValuePair<ITag, string>(MpdTags.Album, Album.Text));
filters.Add(new KeyValuePair<ITag, string>(MpdTags.Date, Year.Text));
filters.Add(new KeyValuePair<ITag, string>(MpdTags.Genre, Genre.Text));
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new SearchCommand(filters, 0, song + 1) });
string Response = await _mpd.SafelySendCommandAsync(commandList);
// create a list of the file url
string[] value = Response.Split(", [file, ");
foreach (string file in value)
{
int start = 0;
int end = file.IndexOf("],");
string filePath = file.Substring(start, end - start);
_songList.Add(filePath);
}
// 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
foreach (var a in _songList)
Debug.WriteLine(a);
Debug.WriteLine("number of songs found: " + _songList.Count);
Debug.WriteLine("number of songs requested: " + int.Parse(SongNumber.Text));
}
private async void AddToQueueFilter()
{
await GetSongsFromFilter();
int AddedSongs = 0;
NumberAddedSongs.Text = AddedSongs.ToString();
SearchStatus.Visibility = Visibility.Visible;
// more requested songs than available => add everything
if (int.Parse(SongNumber.Text) > _songList.Count)
{
foreach (string path in _songList)
{
await Task.Delay(1);
_mpd.AddSong(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 < int.Parse(SongNumber.Text))
{
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;
}
private void AddToQueue_Clicked(object sender, RoutedEventArgs e)
{
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
if (_mpd.GetStats() == null)
return;
if (Song.Text.Length == 0 && Artist.Text.Length == 0 && Album.Text.Length == 0 && Year.Text.Length == 0 && Genre.SelectedIndex == 0)
AddToQueueRandom();
else
AddToQueueFilter();
}
}
}