Compare commits
21 Commits
v1.0
...
c93a9a326e
Author | SHA1 | Date | |
---|---|---|---|
c93a9a326e | |||
c055c59de7 | |||
4c71d6a6e0 | |||
3685c369b4 | |||
9bd088fac8 | |||
e0d640532c | |||
0be28ab205 | |||
62a3220f7f | |||
6e4ed82211 | |||
7b2a7bae21 | |||
72d751db71 | |||
e4b63073d8 | |||
d49f3ab030 | |||
62835065c0 | |||
196b93c7f3 | |||
f9a14ee3c0 | |||
7aafa935e1 | |||
2960afd9bd | |||
23098e0ebb | |||
32d3610b07 | |||
fbb65a039a |
@ -28,6 +28,7 @@ namespace unison
|
|||||||
private const uint VK_MEDIA_PLAY_PAUSE = 0xB3;
|
private const uint VK_MEDIA_PLAY_PAUSE = 0xB3;
|
||||||
private const uint VK_VOLUME_UP = 0xAF;
|
private const uint VK_VOLUME_UP = 0xAF;
|
||||||
private const uint VK_VOLUME_DOWN = 0xAE;
|
private const uint VK_VOLUME_DOWN = 0xAE;
|
||||||
|
private const uint VK_VOLUME_MUTE = 0xAD;
|
||||||
private const uint VK_ENTER = 0x0D;
|
private const uint VK_ENTER = 0x0D;
|
||||||
|
|
||||||
private MainWindow _appWindow;
|
private MainWindow _appWindow;
|
||||||
@ -54,6 +55,7 @@ namespace unison
|
|||||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_PLAY_PAUSE);
|
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_PLAY_PAUSE);
|
||||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_UP);
|
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_UP);
|
||||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_DOWN);
|
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_DOWN);
|
||||||
|
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_MUTE);
|
||||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL | MOD_ALT, VK_ENTER);
|
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL | MOD_ALT, VK_ENTER);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,6 +81,9 @@ namespace unison
|
|||||||
case VK_VOLUME_UP:
|
case VK_VOLUME_UP:
|
||||||
_mpd.VolumeUp();
|
_mpd.VolumeUp();
|
||||||
break;
|
break;
|
||||||
|
case VK_VOLUME_MUTE:
|
||||||
|
_mpd.VolumeMute();
|
||||||
|
break;
|
||||||
case VK_MEDIA_PLAY_PAUSE:
|
case VK_MEDIA_PLAY_PAUSE:
|
||||||
_mpd.PlayPause();
|
_mpd.PlayPause();
|
||||||
break;
|
break;
|
||||||
|
@ -12,17 +12,31 @@ using System.Windows.Threading;
|
|||||||
using MpcNET;
|
using MpcNET;
|
||||||
using MpcNET.Commands.Database;
|
using MpcNET.Commands.Database;
|
||||||
using MpcNET.Commands.Playback;
|
using MpcNET.Commands.Playback;
|
||||||
|
using MpcNET.Commands.Queue;
|
||||||
|
using MpcNET.Commands.Reflection;
|
||||||
using MpcNET.Commands.Status;
|
using MpcNET.Commands.Status;
|
||||||
using MpcNET.Message;
|
using MpcNET.Message;
|
||||||
using MpcNET.Types;
|
using MpcNET.Types;
|
||||||
|
|
||||||
namespace unison
|
namespace unison
|
||||||
{
|
{
|
||||||
|
public class Statistics
|
||||||
|
{
|
||||||
|
public int Songs { get; set; }
|
||||||
|
public int Albums { get; set; }
|
||||||
|
public int Artists { get; set; }
|
||||||
|
public string TotalPlaytime { get; set; }
|
||||||
|
public string Uptime { get; set; }
|
||||||
|
public string TotalTimePlayed { get; set; }
|
||||||
|
public string DatabaseUpdate { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
public class MPDHandler
|
public class MPDHandler
|
||||||
{
|
{
|
||||||
private bool _connected;
|
private bool _connected;
|
||||||
public string _version;
|
public string _version;
|
||||||
private int _currentVolume;
|
private int _currentVolume;
|
||||||
|
private int _previousVolume;
|
||||||
private bool _currentRandom;
|
private bool _currentRandom;
|
||||||
private bool _currentRepeat;
|
private bool _currentRepeat;
|
||||||
private bool _currentSingle;
|
private bool _currentSingle;
|
||||||
@ -33,6 +47,7 @@ namespace unison
|
|||||||
private MpdStatus _currentStatus;
|
private MpdStatus _currentStatus;
|
||||||
private IMpdFile _currentSong;
|
private IMpdFile _currentSong;
|
||||||
private BitmapFrame _cover;
|
private BitmapFrame _cover;
|
||||||
|
public Statistics _stats;
|
||||||
private readonly System.Timers.Timer _elapsedTimer;
|
private readonly System.Timers.Timer _elapsedTimer;
|
||||||
private DispatcherTimer _retryTimer;
|
private DispatcherTimer _retryTimer;
|
||||||
|
|
||||||
@ -55,6 +70,8 @@ namespace unison
|
|||||||
|
|
||||||
Initialize(null, null);
|
Initialize(null, null);
|
||||||
|
|
||||||
|
_stats = new Statistics();
|
||||||
|
|
||||||
_retryTimer = new DispatcherTimer();
|
_retryTimer = new DispatcherTimer();
|
||||||
_retryTimer.Interval = TimeSpan.FromSeconds(5);
|
_retryTimer.Interval = TimeSpan.FromSeconds(5);
|
||||||
_retryTimer.Tick += Initialize;
|
_retryTimer.Tick += Initialize;
|
||||||
@ -363,8 +380,15 @@ namespace unison
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
using MemoryStream stream = new MemoryStream(data.ToArray());
|
using MemoryStream stream = new MemoryStream(data.ToArray());
|
||||||
|
try
|
||||||
|
{
|
||||||
_cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
_cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||||
}
|
}
|
||||||
|
catch (System.NotSupportedException)
|
||||||
|
{
|
||||||
|
_cover = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
UpdateCover();
|
UpdateCover();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,6 +431,7 @@ namespace unison
|
|||||||
public MpdStatus GetStatus() => _currentStatus;
|
public MpdStatus GetStatus() => _currentStatus;
|
||||||
public BitmapFrame GetCover() => _cover;
|
public BitmapFrame GetCover() => _cover;
|
||||||
public string GetVersion() => _version;
|
public string GetVersion() => _version;
|
||||||
|
public Statistics GetStats() => _stats;
|
||||||
public double GetCurrentTime() => _currentTime;
|
public double GetCurrentTime() => _currentTime;
|
||||||
|
|
||||||
public bool IsConnected() => _connected;
|
public bool IsConnected() => _connected;
|
||||||
@ -439,5 +464,57 @@ namespace unison
|
|||||||
_currentVolume = 0;
|
_currentVolume = 0;
|
||||||
SetVolume(_currentVolume);
|
SetVolume(_currentVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void VolumeMute()
|
||||||
|
{
|
||||||
|
if (_currentVolume == 0)
|
||||||
|
{
|
||||||
|
_currentVolume = _previousVolume;
|
||||||
|
_previousVolume = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_previousVolume = _currentVolume;
|
||||||
|
_currentVolume = 0;
|
||||||
|
}
|
||||||
|
SetVolume(_currentVolume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearQueue() => SendCommand(new ClearCommand());
|
||||||
|
public void PlayCommand() => SendCommand(new PlayCommand(0));
|
||||||
|
|
||||||
|
public void AddSong(string Uri)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("AddCommand path: " + Uri);
|
||||||
|
SendCommand(new AddCommand(Uri));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearAddAndPlay(string Uri)
|
||||||
|
{
|
||||||
|
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new ClearCommand(), new AddCommand(Uri), new PlayCommand(0) });
|
||||||
|
SendCommand(commandList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void QueryStats()
|
||||||
|
{
|
||||||
|
Dictionary<string, string> Response = await SafelySendCommandAsync(new StatsCommand());
|
||||||
|
if (Response == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_stats.Songs = int.Parse(Response["songs"]);
|
||||||
|
_stats.Albums = int.Parse(Response["albums"]);
|
||||||
|
_stats.Artists = int.Parse(Response["artists"]);
|
||||||
|
|
||||||
|
TimeSpan time;
|
||||||
|
time = TimeSpan.FromSeconds(int.Parse(Response["uptime"]));
|
||||||
|
_stats.Uptime = time.ToString(@"dd\:hh\:mm\:ss");
|
||||||
|
time = TimeSpan.FromSeconds(int.Parse(Response["db_playtime"]));
|
||||||
|
_stats.TotalPlaytime = time.ToString(@"dd\:hh\:mm\:ss");
|
||||||
|
time = TimeSpan.FromSeconds(int.Parse(Response["playtime"]));
|
||||||
|
_stats.TotalTimePlayed = time.ToString(@"dd\:hh\:mm\:ss");
|
||||||
|
|
||||||
|
DateTime date = new DateTime(1970, 1, 1).AddSeconds(int.Parse(Response["db_update"])).ToLocalTime();
|
||||||
|
_stats.DatabaseUpdate = date.ToString("dd/MM/yyyy @ HH:mm");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,7 @@
|
|||||||
* lightweight window that can be toggled with shortcuts
|
* lightweight window that can be toggled with shortcuts
|
||||||
* music control through shortcuts
|
* music control through shortcuts
|
||||||
* [Snapcast](https://mjaggard.github.io/snapcast/) integration
|
* [Snapcast](https://mjaggard.github.io/snapcast/) integration
|
||||||
|
* Radio stations
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
@ -26,6 +27,12 @@ You can control your music at anytime with the shortcuts. They can of course be
|
|||||||
|
|
||||||
The main goal of embedding Snapcast is the ability to listen locally to music when I'm not using my main audio system. The computer running unison can then play music easily.
|
The main goal of embedding Snapcast is the ability to listen locally to music when I'm not using my main audio system. The computer running unison can then play music easily.
|
||||||
|
|
||||||
|
### Radio stations
|
||||||
|
|
||||||
|
Through [Radio-Browser](https://www.radio-browser.info), a community database, you can play radio-streams directly from unison. There are more than 28,000 stations recorded on this service, so it should be a nice way to discover new music and cultures.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Caveats
|
## Caveats
|
||||||
|
|
||||||
### Missing features
|
### Missing features
|
||||||
@ -37,7 +44,6 @@ The main goal of embedding Snapcast is the ability to listen locally to music wh
|
|||||||
### Wanted features
|
### Wanted features
|
||||||
|
|
||||||
* A complete shuffle system based on set criteria, aka a smart playlist.
|
* A complete shuffle system based on set criteria, aka a smart playlist.
|
||||||
* Radio integration.
|
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
|
162
Resources/Resources.Designer.cs
generated
162
Resources/Resources.Designer.cs
generated
@ -69,6 +69,87 @@ namespace unison.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Country.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_Country {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_Country", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Loading stations....
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_Loading {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_Loading", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Name.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to No stations found!.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_NotFound {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_NotFound", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Reset.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_Reset {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_Reset", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Search.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_Search {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_Search", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Search station.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_SearchStation {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_SearchStation", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Tags.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radio_Tags {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radio_Tags", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Radios.
|
||||||
|
/// </summary>
|
||||||
|
public static string Radios {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Radios", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Settings.
|
/// Looks up a localized string similar to Settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -339,6 +420,15 @@ namespace unison.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Volume mute.
|
||||||
|
/// </summary>
|
||||||
|
public static string Settings_VolumeMute {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Settings_VolumeMute", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Volume offset.
|
/// Looks up a localized string similar to Volume offset.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -411,6 +501,78 @@ namespace unison.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Stats.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Albums:.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats_Albums {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats_Albums", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Artists:.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats_Artists {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats_Artists", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Last database update:.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats_LastDatabaseUpdate {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats_LastDatabaseUpdate", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Songs:.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats_Songs {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats_Songs", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Total playtime:.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats_TotalPlaytime {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats_TotalPlaytime", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Total time played:.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats_TotalTimePlayed {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats_TotalTimePlayed", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to MPD uptime:.
|
||||||
|
/// </summary>
|
||||||
|
public static string Stats_Uptime {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Stats_Uptime", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Stop Snapcast.
|
/// Looks up a localized string similar to Stop Snapcast.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -120,6 +120,33 @@
|
|||||||
<data name="Exit" xml:space="preserve">
|
<data name="Exit" xml:space="preserve">
|
||||||
<value>Quitter</value>
|
<value>Quitter</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Radios" xml:space="preserve">
|
||||||
|
<value>Radios</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Country" xml:space="preserve">
|
||||||
|
<value>Pays</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Loading" xml:space="preserve">
|
||||||
|
<value>Recherche de stations...</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Name" xml:space="preserve">
|
||||||
|
<value>Nom</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_NotFound" xml:space="preserve">
|
||||||
|
<value>Aucun station trouvée !</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Reset" xml:space="preserve">
|
||||||
|
<value>Réinitialiser</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Search" xml:space="preserve">
|
||||||
|
<value>Chercher</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_SearchStation" xml:space="preserve">
|
||||||
|
<value>Recherche de station</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Tags" xml:space="preserve">
|
||||||
|
<value>Tags</value>
|
||||||
|
</data>
|
||||||
<data name="Settings" xml:space="preserve">
|
<data name="Settings" xml:space="preserve">
|
||||||
<value>Configuration</value>
|
<value>Configuration</value>
|
||||||
</data>
|
</data>
|
||||||
@ -210,6 +237,9 @@
|
|||||||
<data name="Settings_VolumeDown" xml:space="preserve">
|
<data name="Settings_VolumeDown" xml:space="preserve">
|
||||||
<value>Baisse de volume</value>
|
<value>Baisse de volume</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Settings_VolumeMute" xml:space="preserve">
|
||||||
|
<value>Volume en sourdine</value>
|
||||||
|
</data>
|
||||||
<data name="Settings_VolumeOffset" xml:space="preserve">
|
<data name="Settings_VolumeOffset" xml:space="preserve">
|
||||||
<value>Écart de volume</value>
|
<value>Écart de volume</value>
|
||||||
</data>
|
</data>
|
||||||
@ -234,6 +264,30 @@
|
|||||||
<data name="StartSnapcast" xml:space="preserve">
|
<data name="StartSnapcast" xml:space="preserve">
|
||||||
<value>Démarrer Snapcast</value>
|
<value>Démarrer Snapcast</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Stats" xml:space="preserve">
|
||||||
|
<value>Stats</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Albums" xml:space="preserve">
|
||||||
|
<value>Albums :</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Artists" xml:space="preserve">
|
||||||
|
<value>Artistes :</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_LastDatabaseUpdate" xml:space="preserve">
|
||||||
|
<value>Mise à jour de la base de données :</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Songs" xml:space="preserve">
|
||||||
|
<value>Morceaux :</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_TotalPlaytime" xml:space="preserve">
|
||||||
|
<value>Temps total :</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_TotalTimePlayed" xml:space="preserve">
|
||||||
|
<value>Temps d'écoute écoulé :</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Uptime" xml:space="preserve">
|
||||||
|
<value>MPD lancé depuis : </value>
|
||||||
|
</data>
|
||||||
<data name="StopSnapcast" xml:space="preserve">
|
<data name="StopSnapcast" xml:space="preserve">
|
||||||
<value>Stopper Snapcast</value>
|
<value>Stopper Snapcast</value>
|
||||||
</data>
|
</data>
|
||||||
|
@ -120,6 +120,33 @@
|
|||||||
<data name="Exit" xml:space="preserve">
|
<data name="Exit" xml:space="preserve">
|
||||||
<value>Exit</value>
|
<value>Exit</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Radios" xml:space="preserve">
|
||||||
|
<value>Radios</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Country" xml:space="preserve">
|
||||||
|
<value>Country</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Loading" xml:space="preserve">
|
||||||
|
<value>Loading stations...</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Name" xml:space="preserve">
|
||||||
|
<value>Name</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_NotFound" xml:space="preserve">
|
||||||
|
<value>No stations found!</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Reset" xml:space="preserve">
|
||||||
|
<value>Reset</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Search" xml:space="preserve">
|
||||||
|
<value>Search</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_SearchStation" xml:space="preserve">
|
||||||
|
<value>Search station</value>
|
||||||
|
</data>
|
||||||
|
<data name="Radio_Tags" xml:space="preserve">
|
||||||
|
<value>Tags</value>
|
||||||
|
</data>
|
||||||
<data name="Settings" xml:space="preserve">
|
<data name="Settings" xml:space="preserve">
|
||||||
<value>Settings</value>
|
<value>Settings</value>
|
||||||
</data>
|
</data>
|
||||||
@ -210,6 +237,9 @@
|
|||||||
<data name="Settings_VolumeDown" xml:space="preserve">
|
<data name="Settings_VolumeDown" xml:space="preserve">
|
||||||
<value>Volume down</value>
|
<value>Volume down</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Settings_VolumeMute" xml:space="preserve">
|
||||||
|
<value>Volume mute</value>
|
||||||
|
</data>
|
||||||
<data name="Settings_VolumeOffset" xml:space="preserve">
|
<data name="Settings_VolumeOffset" xml:space="preserve">
|
||||||
<value>Volume offset</value>
|
<value>Volume offset</value>
|
||||||
</data>
|
</data>
|
||||||
@ -234,6 +264,30 @@
|
|||||||
<data name="StartSnapcast" xml:space="preserve">
|
<data name="StartSnapcast" xml:space="preserve">
|
||||||
<value>Start Snapcast</value>
|
<value>Start Snapcast</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Stats" xml:space="preserve">
|
||||||
|
<value>Stats</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Albums" xml:space="preserve">
|
||||||
|
<value>Albums:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Artists" xml:space="preserve">
|
||||||
|
<value>Artists:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_LastDatabaseUpdate" xml:space="preserve">
|
||||||
|
<value>Last database update:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Songs" xml:space="preserve">
|
||||||
|
<value>Songs:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_TotalPlaytime" xml:space="preserve">
|
||||||
|
<value>Total playtime:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_TotalTimePlayed" xml:space="preserve">
|
||||||
|
<value>Total time played:</value>
|
||||||
|
</data>
|
||||||
|
<data name="Stats_Uptime" xml:space="preserve">
|
||||||
|
<value>MPD uptime:</value>
|
||||||
|
</data>
|
||||||
<data name="StopSnapcast" xml:space="preserve">
|
<data name="StopSnapcast" xml:space="preserve">
|
||||||
<value>Stop Snapcast</value>
|
<value>Stop Snapcast</value>
|
||||||
</data>
|
</data>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 67 KiB |
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 50 KiB |
BIN
Screenshots/screen4.png
Normal file
BIN
Screenshots/screen4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
@ -28,7 +28,7 @@
|
|||||||
</GroupBox.Header>
|
</GroupBox.Header>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid x:Name="CurrentSong" Margin="10,0,10,0" VerticalAlignment="Top" MinHeight="80">
|
<Grid x:Name="CurrentSong" Margin="10,0,10,0" VerticalAlignment="Top" MinHeight="80">
|
||||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
|
<StackPanel Orientation="Vertical" VerticalAlignment="Center" MouseDown="MouseDownClipboard">
|
||||||
<TextBlock x:Name="SongTitle" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Normal" FontSize="20" Text="Title"/>
|
<TextBlock x:Name="SongTitle" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Normal" FontSize="20" Text="Title"/>
|
||||||
<TextBlock x:Name="SongArtist" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Bold" FontSize="18" Text="Artist"/>
|
<TextBlock x:Name="SongArtist" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Bold" FontSize="18" Text="Artist"/>
|
||||||
<TextBlock x:Name="SongAlbum" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Normal" FontSize="16" Text="Album"/>
|
<TextBlock x:Name="SongAlbum" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Normal" FontSize="16" Text="Album"/>
|
||||||
@ -110,12 +110,20 @@
|
|||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid x:Name="BottomLayout" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}" Width="Auto" MinHeight="40">
|
<Grid x:Name="BottomLayout" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}" Width="Auto" MinHeight="40">
|
||||||
|
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
|
||||||
<Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="10,0,0,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
|
<Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="10,0,0,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<emoji:TextBlock Text="🔊" Padding="0,0,0,2"/>
|
<emoji:TextBlock Text="🔊" Padding="0,0,0,2"/>
|
||||||
<TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/>
|
<TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button x:Name="Radio" Padding="5, 2" HorizontalAlignment="Left" Click="Radios_Clicked" Margin="5,0,10,0" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<emoji:TextBlock Text="📻" Padding="0,0,0,2"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Radios}" Margin="5, 0, 0, 0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
<Grid x:Name="ConnectionOkIcon" HorizontalAlignment="Center" VerticalAlignment="Center">
|
<Grid x:Name="ConnectionOkIcon" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" VerticalAlignment="Center" HorizontalAlignment="Center" />
|
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" VerticalAlignment="Center" HorizontalAlignment="Center" />
|
||||||
@ -127,12 +135,12 @@
|
|||||||
<TextBlock x:Name="Connection" HorizontalAlignment="Center" Text="Not connected" TextWrapping="Wrap" VerticalAlignment="Top" TextAlignment="Center" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" Margin="5,0,0,0" />
|
<TextBlock x:Name="Connection" HorizontalAlignment="Center" Text="Not connected" TextWrapping="Wrap" VerticalAlignment="Top" TextAlignment="Center" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" Margin="5,0,0,0" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
|
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
|
||||||
<!--<Button x:Name="Shuffle" Padding="5, 2" HorizontalAlignment="Right" Margin="0,0,10,0" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
<Button x:Name="Shuffle" Padding="5, 2" Click="Shuffle_Clicked" Margin="0,0,10,0" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<emoji:TextBlock Text="🔁" Padding="0,0,0,2"/>
|
<emoji:TextBlock Text="🔁" Padding="0,0,0,2"/>
|
||||||
<TextBlock Text="Shuffle" Margin="5, 0, 0, 0"/>
|
<TextBlock Text="Shuffle" Margin="5, 0, 0, 0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Button>-->
|
</Button>
|
||||||
<Button x:Name="Settings" Padding="5, 2" Click="Settings_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}">
|
<Button x:Name="Settings" Padding="5, 2" Click="Settings_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}">
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<emoji:TextBlock Text="🛠️" Padding="0,0,0,2"/>
|
<emoji:TextBlock Text="🛠️" Padding="0,0,0,2"/>
|
||||||
|
@ -6,12 +6,16 @@ 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;
|
||||||
|
|
||||||
namespace unison
|
namespace unison
|
||||||
{
|
{
|
||||||
public partial class MainWindow : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
private readonly Settings _settingsWin;
|
private readonly Settings _settingsWin;
|
||||||
|
private readonly Radios _radiosWin;
|
||||||
|
private readonly Shuffle _shuffleWin;
|
||||||
private readonly DispatcherTimer _timer;
|
private readonly DispatcherTimer _timer;
|
||||||
private readonly MPDHandler _mpd;
|
private readonly MPDHandler _mpd;
|
||||||
|
|
||||||
@ -23,6 +27,8 @@ namespace unison
|
|||||||
WindowState = WindowState.Minimized;
|
WindowState = WindowState.Minimized;
|
||||||
|
|
||||||
_settingsWin = new Settings();
|
_settingsWin = new Settings();
|
||||||
|
_radiosWin = new Radios();
|
||||||
|
_shuffleWin = new Shuffle();
|
||||||
_timer = new DispatcherTimer();
|
_timer = new DispatcherTimer();
|
||||||
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||||
|
|
||||||
@ -57,9 +63,12 @@ namespace unison
|
|||||||
}
|
}
|
||||||
_settingsWin.UpdateConnectionStatus();
|
_settingsWin.UpdateConnectionStatus();
|
||||||
Connection.Text = $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
|
Connection.Text = $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
|
||||||
|
|
||||||
|
_shuffleWin.ListGenre();
|
||||||
|
_shuffleWin.ListFolder();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnSongChanged(object sender, EventArgs e)
|
public async void OnSongChanged(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (_mpd.GetCurrentSong() == null)
|
if (_mpd.GetCurrentSong() == null)
|
||||||
return;
|
return;
|
||||||
@ -68,10 +77,11 @@ namespace unison
|
|||||||
SongTitle.Text = _mpd.GetCurrentSong().Title;
|
SongTitle.Text = _mpd.GetCurrentSong().Title;
|
||||||
else if (_mpd.GetCurrentSong().HasName && _mpd.GetCurrentSong().Name.Length > 0)
|
else if (_mpd.GetCurrentSong().HasName && _mpd.GetCurrentSong().Name.Length > 0)
|
||||||
SongTitle.Text = _mpd.GetCurrentSong().Name;
|
SongTitle.Text = _mpd.GetCurrentSong().Name;
|
||||||
else
|
else if (_mpd.GetCurrentSong().Path != null)
|
||||||
{
|
{
|
||||||
int start = _mpd.GetCurrentSong().Path.LastIndexOf("/") + 1;
|
int start = _mpd.GetCurrentSong().Path.LastIndexOf("/") + 1;
|
||||||
int end = _mpd.GetCurrentSong().Path.LastIndexOf(".");
|
int end = _mpd.GetCurrentSong().Path.LastIndexOf(".");
|
||||||
|
if (start > 0 && end > 0 && end > start)
|
||||||
SongTitle.Text = _mpd.GetCurrentSong().Path.Substring(start, end - start);
|
SongTitle.Text = _mpd.GetCurrentSong().Path.Substring(start, end - start);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +114,32 @@ namespace unison
|
|||||||
_timer.Start();
|
_timer.Start();
|
||||||
EndTime.Text = FormatSeconds(_mpd.GetCurrentSong().Time);
|
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)
|
public void OnStatusChanged(object sender, EventArgs e)
|
||||||
@ -132,6 +168,9 @@ namespace unison
|
|||||||
DefaultState();
|
DefaultState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_mpd.QueryStats();
|
||||||
|
_settingsWin.UpdateStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DefaultState(bool LostConnection = false)
|
private void DefaultState(bool LostConnection = false)
|
||||||
@ -214,6 +253,24 @@ namespace unison
|
|||||||
snapcast.LaunchOrExit();
|
snapcast.LaunchOrExit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Radios_Clicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_radiosWin.Show();
|
||||||
|
_radiosWin.Activate();
|
||||||
|
|
||||||
|
if (_radiosWin.WindowState == WindowState.Minimized)
|
||||||
|
_radiosWin.WindowState = WindowState.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Shuffle_Clicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
_shuffleWin.Show();
|
||||||
|
_shuffleWin.Activate();
|
||||||
|
|
||||||
|
if (_shuffleWin.WindowState == WindowState.Minimized)
|
||||||
|
_shuffleWin.WindowState = WindowState.Normal;
|
||||||
|
}
|
||||||
|
|
||||||
public void Settings_Clicked(object sender, RoutedEventArgs e)
|
public void Settings_Clicked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
_settingsWin.Show();
|
_settingsWin.Show();
|
||||||
@ -254,6 +311,17 @@ namespace unison
|
|||||||
hk.Activate(this);
|
hk.Activate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MouseDownClipboard(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.ClickCount == 2)
|
||||||
|
{
|
||||||
|
string CopyText = SongTitle.Text + " - " + SongArtist.Text + "\n";
|
||||||
|
CopyText += SongAlbum.Text + "\n";
|
||||||
|
CopyText += SongTitle.ToolTip;
|
||||||
|
Clipboard.SetText(CopyText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void InitHwnd()
|
public void InitHwnd()
|
||||||
{
|
{
|
||||||
WindowInteropHelper helper = new(this);
|
WindowInteropHelper helper = new(this);
|
||||||
|
74
Views/Radios.xaml
Normal file
74
Views/Radios.xaml
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<Window x:Class="unison.Radios"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
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:properties="clr-namespace:unison.Resources"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="Radios" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,0">
|
||||||
|
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<TextBlock>
|
||||||
|
<emoji:EmojiInline Text="📻"/>
|
||||||
|
<Run Text="{x:Static properties:Resources.Radio_SearchStation}"/>
|
||||||
|
</TextBlock>
|
||||||
|
</GroupBox.Header>
|
||||||
|
<StackPanel Orientation="Vertical" Margin="5,0,5,0">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<StackPanel Orientation="Vertical">
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Radio_Name}" Margin="0,0,0,5"/>
|
||||||
|
<TextBox x:Name="NameSearch" KeyDown="SearchHandler" Width="200" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Radio_Tags}" Margin="0,0,0,5"/>
|
||||||
|
<TextBox x:Name="TagSearch" KeyDown="SearchHandler" Width="300" Margin="0,4,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Radio_Country}" Margin="0,0,0,5"/>
|
||||||
|
<ComboBox x:Name="CountryList" SelectedIndex="0" KeyDown="SearchHandler" Width="240" ScrollViewer.CanContentScroll="False"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||||
|
<Button Content="{x:Static properties:Resources.Radio_Search}" Click="Search_Clicked" Padding="5, 2"/>
|
||||||
|
<Button Content="{x:Static properties:Resources.Radio_Reset}" Click="Reset_Clicked" Margin="10,0,0,0" Padding="5, 2"/>
|
||||||
|
<TextBlock x:Name="SearchStatus" Margin="15,1,0,0" FontStyle="Italic" />
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</GroupBox>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<Grid Margin="5,10,5,5" MaxHeight="600" MinWidth="800" MaxWidth="800">
|
||||||
|
<Grid.Resources>
|
||||||
|
<DataTemplate x:Key="CountryTemplate">
|
||||||
|
<emoji:TextBlock TextAlignment="Center" Text="{Binding Country}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
</Grid.Resources>
|
||||||
|
<DataGrid Name="RadioListGrid" MouseDoubleClick="Row_DoubleClick" CanUserAddRows="False" CanUserDeleteRows="False"
|
||||||
|
CanUserReorderColumns="False" CanUserResizeRows="False" IsReadOnly="True" SelectionMode="Single"
|
||||||
|
HeadersVisibility="Column" GridLinesVisibility="None" VirtualizingPanel.ScrollUnit="Pixel">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTemplateColumn Header="🏳️" CellTemplate="{StaticResource CountryTemplate}" MinWidth="25" />
|
||||||
|
<DataGridTextColumn Header="{x:Static properties:Resources.Radio_Name}" Binding="{Binding Name}" MinWidth="50"/>
|
||||||
|
<DataGridTextColumn Header="Codec" Binding="{Binding Codec}" MinWidth="47"/>
|
||||||
|
<DataGridTextColumn Header="Bitrate" Binding="{Binding Bitrate}" MinWidth="47"/>
|
||||||
|
<DataGridTextColumn Header="Tags" Binding="{Binding Tags}" MinWidth="50"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
<DataGrid.CellStyle>
|
||||||
|
<Style TargetType="DataGridCell">
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||||||
|
</Style>
|
||||||
|
</DataGrid.CellStyle>
|
||||||
|
</DataGrid>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
181
Views/Radios.xaml.cs
Normal file
181
Views/Radios.xaml.cs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
using RadioBrowser;
|
||||||
|
using RadioBrowser.Models;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace unison
|
||||||
|
{
|
||||||
|
public class CountryListItem
|
||||||
|
{
|
||||||
|
public uint Count { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (Name == "")
|
||||||
|
return "None";
|
||||||
|
return $"{Name} ({Count})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class StationListItem
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Codec { get; set; }
|
||||||
|
public string Tags { get; set; }
|
||||||
|
public int Bitrate { get; set; }
|
||||||
|
public Uri Url { get; set; }
|
||||||
|
|
||||||
|
private string _country;
|
||||||
|
public string Country
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_country.Length == 0)
|
||||||
|
return "🏴☠️";
|
||||||
|
return string.Concat(_country.ToUpper().Select(x => char.ConvertFromUtf32(x + 0x1F1A5))); // return emoji
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_country = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class Radios : Window
|
||||||
|
{
|
||||||
|
private RadioBrowserClient _radioBrowser;
|
||||||
|
private MPDHandler _mpd;
|
||||||
|
|
||||||
|
public Radios()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
_radioBrowser = new RadioBrowserClient();
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void Initialize()
|
||||||
|
{
|
||||||
|
List<NameAndCount> Countries = await _radioBrowser.Lists.GetCountriesAsync();
|
||||||
|
CountryList.Items.Add(new CountryListItem { Name = "", Count = 0 });
|
||||||
|
|
||||||
|
foreach (NameAndCount Country in Countries)
|
||||||
|
{
|
||||||
|
CountryList.Items.Add(new CountryListItem
|
||||||
|
{
|
||||||
|
Name = Country.Name,
|
||||||
|
Count = Country.Stationcount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CleanString(string str)
|
||||||
|
{
|
||||||
|
return str.Replace("\r\n", "").Replace("\n", "").Replace("\r", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SearchAdvanced(string name, string country, string tags)
|
||||||
|
{
|
||||||
|
SearchStatus.Text = unison.Resources.Resources.Radio_Loading;
|
||||||
|
|
||||||
|
List<StationInfo> advancedSearch = await _radioBrowser.Search.AdvancedAsync(new AdvancedSearchOptions
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Country = country,
|
||||||
|
TagList = tags
|
||||||
|
});
|
||||||
|
|
||||||
|
RadioListGrid.Items.Clear();
|
||||||
|
if (advancedSearch.Count > 0)
|
||||||
|
{
|
||||||
|
SearchStatus.Text = "";
|
||||||
|
foreach (StationInfo station in advancedSearch)
|
||||||
|
{
|
||||||
|
RadioListGrid.Items.Add(new StationListItem
|
||||||
|
{
|
||||||
|
Name = CleanString(station.Name),
|
||||||
|
Country = station.CountryCode,
|
||||||
|
Codec = station.Codec,
|
||||||
|
Bitrate = station.Bitrate,
|
||||||
|
Url = station.Url,
|
||||||
|
Tags = string.Join(", ", station.Tags)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
FitToContent();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SearchStatus.Text = unison.Resources.Resources.Radio_NotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FitToContent()
|
||||||
|
{
|
||||||
|
foreach (DataGridColumn column in RadioListGrid.Columns)
|
||||||
|
column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToCells);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Row_DoubleClick(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
DataGrid grid = sender as DataGrid;
|
||||||
|
StationListItem station;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
station = grid.Items[grid.SelectedIndex] as StationListItem;
|
||||||
|
}
|
||||||
|
catch (ArgumentOutOfRangeException)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error: Invalid index.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (station.Url == null)
|
||||||
|
{
|
||||||
|
Debug.WriteLine("Error: Invalid station.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||||
|
_mpd.ClearAddAndPlay(station.Url.AbsoluteUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Search_Clicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
CountryListItem a = (CountryListItem)CountryList.SelectedItem;
|
||||||
|
await SearchAdvanced(NameSearch.Text, a?.Name, TagSearch.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset_Clicked(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
NameSearch.Text = "";
|
||||||
|
TagSearch.Text = "";
|
||||||
|
CountryList.SelectedIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SearchHandler(object sender, KeyEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.Key == Key.Return)
|
||||||
|
Search_Clicked(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closing(object sender, CancelEventArgs e)
|
||||||
|
{
|
||||||
|
e.Cancel = true;
|
||||||
|
WindowState = WindowState.Minimized;
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitHwnd()
|
||||||
|
{
|
||||||
|
WindowInteropHelper helper = new(this);
|
||||||
|
helper.EnsureHandle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,23 +14,21 @@
|
|||||||
<DockPanel Margin="8">
|
<DockPanel Margin="8">
|
||||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||||
<GroupBox.Header>
|
<GroupBox.Header>
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<emoji:EmojiInline Text="📶"/>
|
<emoji:EmojiInline Text="📶"/>
|
||||||
<Run Text="{x:Static properties:Resources.Settings_Connection}"/>
|
<Run Text="{x:Static properties:Resources.Settings_Connection}"/>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
|
||||||
</GroupBox.Header>
|
</GroupBox.Header>
|
||||||
<Grid VerticalAlignment="Top">
|
<Grid VerticalAlignment="Top">
|
||||||
<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" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
|
<TextBox x:Name="MpdHost" KeyDown="ConnectHandler" 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" MaxLength="5" PreviewTextInput="NumberValidationTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
|
<TextBox x:Name="MpdPort" KeyDown="ConnectHandler" 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">
|
||||||
@ -46,13 +44,63 @@
|
|||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
|
|
||||||
|
<TabItem Header="{x:Static properties:Resources.Settings_Shortcuts}">
|
||||||
|
<DockPanel Margin="8">
|
||||||
|
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<TextBlock>
|
||||||
|
<emoji:EmojiInline Text="⌨️ "/>
|
||||||
|
<Run Text="{x:Static properties:Resources.Settings_Shortcuts}"></Run>
|
||||||
|
</TextBlock>
|
||||||
|
</GroupBox.Header>
|
||||||
|
<Grid>
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBox x:Name="VolumeOffset" TextWrapping="Wrap" Width="25" PreviewTextInput="NumberValidationTextBox" Margin="0,2,0,0"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeOffset}" TextWrapping="Wrap" Margin="5,2,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
<Grid MinWidth="300" Margin="0,5,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
<ColumnDefinition/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_NextTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="0" Margin="1"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_PreviousTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="1" Margin="1"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_PlayPause}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="2" Margin="1"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeUp}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="3" Margin="1"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeDown}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="4" Margin="1"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeMute}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="5" Margin="1"/>
|
||||||
|
<TextBlock Text="{x:Static properties:Resources.Settings_ShowWindow}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="6" Margin="1"/>
|
||||||
|
|
||||||
|
<TextBlock Text="ctrl + media_next" TextWrapping="Wrap" Grid.Column="1" Grid.Row="0" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="ctrl + media_prev" TextWrapping="Wrap" Grid.Column="1" Grid.Row="1" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="ctrl + media_play" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="ctrl + volume_up" TextWrapping="Wrap" Grid.Column="1" Grid.Row="3" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="ctrl + volume_down" TextWrapping="Wrap" Grid.Column="1" Grid.Row="4" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="ctrl + volume_mute" TextWrapping="Wrap" Grid.Column="1" Grid.Row="5" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="ctrl + alt + enter" TextWrapping="Wrap" Grid.Column="1" Grid.Row="6" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</GroupBox>
|
||||||
|
</DockPanel>
|
||||||
|
</TabItem>
|
||||||
|
|
||||||
<TabItem Header="Snapcast">
|
<TabItem Header="Snapcast">
|
||||||
<DockPanel Margin="8">
|
<DockPanel Margin="8">
|
||||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||||
<GroupBox.Header>
|
<GroupBox.Header>
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<emoji:TextBlock Text="🔊 Snapcast"/>
|
<emoji:TextBlock Text="🔊 Snapcast"/>
|
||||||
</StackPanel>
|
|
||||||
</GroupBox.Header>
|
</GroupBox.Header>
|
||||||
<Grid VerticalAlignment="Top">
|
<Grid VerticalAlignment="Top">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
@ -78,56 +126,61 @@
|
|||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
|
|
||||||
<TabItem Header="{x:Static properties:Resources.Settings_Shortcuts}">
|
<TabItem Header="Shuffle">
|
||||||
<DockPanel Margin="8">
|
<DockPanel Margin="8">
|
||||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||||
<GroupBox.Header>
|
<GroupBox.Header>
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<emoji:EmojiInline Text="⌨️ "/>
|
<emoji:EmojiInline Text="🔁 "/>
|
||||||
<Run Text="{x:Static properties:Resources.Settings_Shortcuts}"></Run>
|
<Run Text="Shuffle"></Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</StackPanel>
|
|
||||||
</GroupBox.Header>
|
</GroupBox.Header>
|
||||||
<Grid>
|
<Grid MaxWidth="500">
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeOffset}" TextWrapping="Wrap"/>
|
<TextBox TextWrapping="Wrap" Width="25" PreviewTextInput="NumberValidationTextBox" Margin="0,2,0,0"/>
|
||||||
<TextBox x:Name="VolumeOffset" TextWrapping="Wrap" Width="25" PreviewTextInput="NumberValidationTextBox" Margin="8,2,0,0"/>
|
<TextBlock Text="Prevent repetition rate (0-100%)" TextWrapping="Wrap" Margin="5,2,0,0"/>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Grid MinWidth="300" Margin="0,5,0,0">
|
<TextBlock TextWrapping="Wrap" Margin="0,10,0,0">
|
||||||
<Grid.ColumnDefinitions>
|
<Run>The shuffle window allows to add random songs to your queue. Both options take into account the filter.</Run>
|
||||||
<ColumnDefinition/>
|
<Run>If the filter is empty, the entire music library is taken into account.</Run><LineBreak/><LineBreak/>
|
||||||
<ColumnDefinition/>
|
<Run FontWeight="Bold">Continuous shuffle</Run><LineBreak/>
|
||||||
</Grid.ColumnDefinitions>
|
<Run>By enabling this option, unison will automatically add songs to the queue so you never run out of songs to listen to.</Run>
|
||||||
<Grid.RowDefinitions>
|
<LineBreak/><LineBreak/>
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition/>
|
|
||||||
<RowDefinition/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<TextBlock Text="{x:Static properties:Resources.Settings_NextTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="0" Margin="1"/>
|
|
||||||
<TextBlock Text="{x:Static properties:Resources.Settings_PreviousTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="1" Margin="1"/>
|
|
||||||
<TextBlock Text="{x:Static properties:Resources.Settings_PlayPause}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="2" Margin="1"/>
|
|
||||||
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeUp}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="3" Margin="1"/>
|
|
||||||
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeDown}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="4" Margin="1"/>
|
|
||||||
<TextBlock Text="{x:Static properties:Resources.Settings_ShowWindow}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="5" Margin="1"/>
|
|
||||||
|
|
||||||
<TextBlock Text="ctrl + media_next" TextWrapping="Wrap" Grid.Column="1" Grid.Row="0" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
<Run FontWeight="Bold">Add to queue</Run><LineBreak/>
|
||||||
<TextBlock Text="ctrl + media_prev" TextWrapping="Wrap" Grid.Column="1" Grid.Row="1" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
<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 Text="ctrl + media_play" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
</TextBlock>
|
||||||
<TextBlock Text="ctrl + volume_up" TextWrapping="Wrap" Grid.Column="1" Grid.Row="3" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
|
||||||
<TextBlock Text="ctrl + volume_down" TextWrapping="Wrap" Grid.Column="1" Grid.Row="4" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
|
||||||
<TextBlock Text="ctrl + alt + enter" TextWrapping="Wrap" Grid.Column="1" Grid.Row="5" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
|
||||||
</Grid>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
</DockPanel>
|
</DockPanel>
|
||||||
</TabItem>
|
</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">
|
<TabItem Header="{x:Static properties:Resources.Settings_About}" Height="20" VerticalAlignment="Bottom">
|
||||||
<DockPanel Margin="8">
|
<DockPanel Margin="8">
|
||||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||||
@ -144,7 +197,8 @@
|
|||||||
<Run Text="{x:Static properties:Resources.Settings_AboutInfo}" /><LineBreak/>
|
<Run Text="{x:Static properties:Resources.Settings_AboutInfo}" /><LineBreak/>
|
||||||
※ <Hyperlink NavigateUri="https://github.com/Difegue/Stylophone" RequestNavigate="Hyperlink_RequestNavigate">Stylophone</Hyperlink><Run Text="{x:Static properties:Resources.Settings_MpcNET}" /><LineBreak/>
|
※ <Hyperlink NavigateUri="https://github.com/Difegue/Stylophone" RequestNavigate="Hyperlink_RequestNavigate">Stylophone</Hyperlink><Run Text="{x:Static properties:Resources.Settings_MpcNET}" /><LineBreak/>
|
||||||
※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/>
|
※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/>
|
||||||
※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink>
|
※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink><LineBreak/>
|
||||||
|
※ <Hyperlink NavigateUri="https://github.com/tof4/RadioBrowser" RequestNavigate="Hyperlink_RequestNavigate">RadioBrowser</Hyperlink>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
<TextBlock Margin="0,10,0,0">
|
<TextBlock Margin="0,10,0,0">
|
||||||
<Run Text="{x:Static properties:Resources.Settings_SourceCode1}" />
|
<Run Text="{x:Static properties:Resources.Settings_SourceCode1}" />
|
||||||
|
@ -88,6 +88,18 @@ namespace unison
|
|||||||
SnapcastPort.Text = (string)Application.Current.FindResource("snapcastPort");
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
public void SaveSettings()
|
public void SaveSettings()
|
||||||
{
|
{
|
||||||
Properties.Settings.Default.mpd_host = MpdHost.Text;
|
Properties.Settings.Default.mpd_host = MpdHost.Text;
|
||||||
@ -101,6 +113,12 @@ namespace unison
|
|||||||
Properties.Settings.Default.Save();
|
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)
|
private void Window_Closing(object sender, CancelEventArgs e)
|
||||||
{
|
{
|
||||||
e.Cancel = true;
|
e.Cancel = true;
|
||||||
|
102
Views/Shuffle.xaml
Normal file
102
Views/Shuffle.xaml
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<Window x:Class="unison.Shuffle"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
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"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="Shuffle" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,5">
|
||||||
|
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||||
|
<GroupBox.Header>
|
||||||
|
<TextBlock>
|
||||||
|
<emoji:EmojiInline Text="🔡"/>
|
||||||
|
<Run Text="Filter"/>
|
||||||
|
</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>
|
||||||
|
<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"/>
|
||||||
|
</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="False"/>
|
||||||
|
</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">
|
||||||
|
<TextBlock>
|
||||||
|
<Run Text="Number of songs in filter: "/><Run x:Name="SongFilterNumber" FontWeight="Bold"/>
|
||||||
|
</TextBlock>
|
||||||
|
</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">
|
||||||
|
<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>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
294
Views/Shuffle.xaml.cs
Normal file
294
Views/Shuffle.xaml.cs
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
namespace unison
|
||||||
|
{
|
||||||
|
public partial class Shuffle : Window
|
||||||
|
{
|
||||||
|
private MPDHandler _mpd;
|
||||||
|
bool _continuous = false;
|
||||||
|
List<string> _songList { get; }
|
||||||
|
|
||||||
|
public Shuffle()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_songList = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetContinuous()
|
||||||
|
{
|
||||||
|
return _continuous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddContinuousSongs()
|
||||||
|
{
|
||||||
|
if (Song.Text.Length == 0 && Artist.Text.Length == 0 && Album.Text.Length == 0 && Year.Text.Length == 0 && Genre.SelectedIndex == 0)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (Response.Count > 0)
|
||||||
|
{
|
||||||
|
Genre.Items.Add("");
|
||||||
|
foreach (var genre in Response)
|
||||||
|
Genre.Items.Add(genre);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async void ListFolder()
|
||||||
|
{
|
||||||
|
if (Directory.Items.Count == 0)
|
||||||
|
{
|
||||||
|
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||||
|
List<string> Response = await _mpd.SafelySendCommandAsync(new ListFilesCommand());
|
||||||
|
|
||||||
|
if (Response.Count > 0)
|
||||||
|
{
|
||||||
|
Directory.Items.Add("");
|
||||||
|
for (int i = 0; i < Response.Count; i++)
|
||||||
|
if (i % 2 != 1)
|
||||||
|
Directory.Items.Add(Response[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closing(object sender, CancelEventArgs e)
|
||||||
|
{
|
||||||
|
e.Cancel = true;
|
||||||
|
WindowState = WindowState.Minimized;
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitHwnd()
|
||||||
|
{
|
||||||
|
WindowInteropHelper helper = new(this);
|
||||||
|
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();
|
||||||
|
if (Song.Text.Length == 0 && Artist.Text.Length == 0 && Album.Text.Length == 0 && Year.Text.Length == 0 && Genre.SelectedIndex == 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*await*/
|
||||||
|
GetSongsFromFilter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AddToQueueGroup.IsEnabled = true;
|
||||||
|
_continuous = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void ContinuousShuffle_AddToQueueRandom()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
SongFilterPanel.Visibility = Visibility.Visible;
|
||||||
|
|
||||||
|
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, ");
|
||||||
|
|
||||||
|
// there are no song in this filter
|
||||||
|
if (value[0] == "")
|
||||||
|
{
|
||||||
|
SongFilterNumber.Text = _songList.Count.ToString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,11 +25,16 @@
|
|||||||
<Image Width="16" Height="16" emoji:Image.Source="🔊" />
|
<Image Width="16" Height="16" emoji:Image.Source="🔊" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<!--<MenuItem Header="Shuffle" Command="{Binding Shuffle}">
|
<MenuItem Header="{x:Static properties:Resources.Radios}" Command="{Binding Radios}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<Image Width="16" Height="16" emoji:Image.Source="📻" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem Header="Shuffle" Command="{Binding Shuffle}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Image Width="16" Height="16" emoji:Image.Source="🔀" />
|
<Image Width="16" Height="16" emoji:Image.Source="🔀" />
|
||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>-->
|
</MenuItem>
|
||||||
<MenuItem Header="{x:Static properties:Resources.Settings}" Command="{Binding Settings}">
|
<MenuItem Header="{x:Static properties:Resources.Settings}" Command="{Binding Settings}">
|
||||||
<MenuItem.Icon>
|
<MenuItem.Icon>
|
||||||
<Image Width="16" Height="16" emoji:Image.Source="🛠️" />
|
<Image Width="16" Height="16" emoji:Image.Source="🛠️" />
|
||||||
|
@ -59,6 +59,30 @@ namespace unison
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ICommand Radios
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new DelegateCommand
|
||||||
|
{
|
||||||
|
CommandAction = () => ((MainWindow)Application.Current.MainWindow).Radios_Clicked(null, null),
|
||||||
|
CanExecuteFunc = () => true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand Shuffle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new DelegateCommand
|
||||||
|
{
|
||||||
|
CommandAction = () => ((MainWindow)Application.Current.MainWindow).Shuffle_Clicked(null, null),
|
||||||
|
CanExecuteFunc = () => true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ICommand Settings
|
public ICommand Settings
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<ApplicationIcon>Resources\icon-full.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\icon-full.ico</ApplicationIcon>
|
||||||
<Win32Resource></Win32Resource>
|
<Win32Resource></Win32Resource>
|
||||||
<StartupObject>unison.App</StartupObject>
|
<StartupObject>unison.App</StartupObject>
|
||||||
<Version>1.0</Version>
|
<Version>1.2</Version>
|
||||||
<Company />
|
<Company />
|
||||||
<Authors>Théo Marchal</Authors>
|
<Authors>Théo Marchal</Authors>
|
||||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||||
@ -74,6 +74,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Emoji.Wpf" Version="0.3.3" />
|
<PackageReference Include="Emoji.Wpf" Version="0.3.3" />
|
||||||
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
||||||
|
<PackageReference Include="RadioBrowser" Version="0.6.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
Reference in New Issue
Block a user