15 Commits
shuffle ... 1.4

25 changed files with 1837 additions and 143 deletions

84
App.config Normal file
View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="unison.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<userSettings>
<unison.Properties.Settings>
<setting name="mpd_host" serializeAs="String">
<value>192.168.0.1</value>
</setting>
<setting name="mpd_port" serializeAs="String">
<value>6600</value>
</setting>
<setting name="mpd_password" serializeAs="String">
<value />
</setting>
<setting name="snapcast_startup" serializeAs="String">
<value>False</value>
</setting>
<setting name="snapcast_window" serializeAs="String">
<value>False</value>
</setting>
<setting name="snapcast_path" serializeAs="String">
<value>snapclient_0.26.0-1_win64</value>
</setting>
<setting name="snapcast_port" serializeAs="String">
<value>1704</value>
</setting>
<setting name="volume_offset" serializeAs="String">
<value>5</value>
</setting>
<setting name="nextTrack_mod" serializeAs="String">
<value>2</value>
</setting>
<setting name="nextTrack_vk" serializeAs="String">
<value>176</value>
</setting>
<setting name="previousTrack_mod" serializeAs="String">
<value>2</value>
</setting>
<setting name="previousTrack_vk" serializeAs="String">
<value>177</value>
</setting>
<setting name="playPause_mod" serializeAs="String">
<value>2</value>
</setting>
<setting name="playPause_vk" serializeAs="String">
<value>179</value>
</setting>
<setting name="volumeUp_mod" serializeAs="String">
<value>2</value>
</setting>
<setting name="volumeUp_vk" serializeAs="String">
<value>175</value>
</setting>
<setting name="volumeDown_mod" serializeAs="String">
<value>2</value>
</setting>
<setting name="volumeDown_vk" serializeAs="String">
<value>174</value>
</setting>
<setting name="volumeMute_mod" serializeAs="String">
<value>2</value>
</setting>
<setting name="volumeMute_vk" serializeAs="String">
<value>173</value>
</setting>
<setting name="showWindow_mod" serializeAs="String">
<value>3</value>
</setting>
<setting name="showWindow_vk" serializeAs="String">
<value>13</value>
</setting>
<setting name="MainWindowTop" serializeAs="String">
<value>100</value>
</setting>
<setting name="MainWindowLeft" serializeAs="String">
<value>100</value>
</setting>
</unison.Properties.Settings>
</userSettings>
</configuration>

View File

@ -9,16 +9,18 @@ namespace unison
private TaskbarIcon _systray; private TaskbarIcon _systray;
private HotkeyHandler _hotkeys; private HotkeyHandler _hotkeys;
private SnapcastHandler _snapcast; private SnapcastHandler _snapcast;
private ShuffleHandler _shuffle;
private MPDHandler _mpd; private MPDHandler _mpd;
private UpdateHandler _updater; private UpdateHandler _updater;
protected override void OnStartup(StartupEventArgs e) protected override void OnStartup(StartupEventArgs e)
{ {
unison.Resources.Resources.Culture = System.Globalization.CultureInfo.CurrentCulture;
//debug language //debug language
//unison.Resources.Resources.Culture = System.Globalization.CultureInfo.GetCultureInfo("fr-FR"); //unison.Resources.Resources.Culture = System.Globalization.CultureInfo.GetCultureInfo("fr-FR");
//unison.Resources.Resources.Culture = System.Globalization.CultureInfo.GetCultureInfo("es-ES"); //unison.Resources.Resources.Culture = System.Globalization.CultureInfo.GetCultureInfo("es-ES");
base.OnStartup(e); base.OnStartup(e);
_mpd = new MPDHandler(); _mpd = new MPDHandler();
@ -30,6 +32,9 @@ namespace unison
_snapcast = new SnapcastHandler(); _snapcast = new SnapcastHandler();
Current.Properties["snapcast"] = _snapcast; Current.Properties["snapcast"] = _snapcast;
_shuffle = new ShuffleHandler();
Current.Properties["shuffle"] = _shuffle;
_updater = new UpdateHandler(); _updater = new UpdateHandler();
Current.Properties["updater"] = _updater; Current.Properties["updater"] = _updater;

View File

@ -1,5 +1,19 @@
# Changelog # Changelog
## v1.4
*Released: 09/12/2022*
* New feature: shuffle system
* New feature: (un)installer script
* New feature: update system
* New feature: update MPD database button
* Restore window position when relaunching the app
* New settings organisation
* Update Emoji.WPF from 0.3.3 to 0.3.4
* Fix: cover images are displaying in all cases
* Fix: querying a large list of radios could hang the app
## v1.3.1 ## v1.3.1
*Released: 03/11/2022* *Released: 03/11/2022*

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Net; using System.Net;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@ -34,7 +35,7 @@ namespace unison
public class MPDHandler public class MPDHandler
{ {
private bool _connected; private bool _connected;
public string _version; private string _version;
private int _currentVolume; private int _currentVolume;
private int _previousVolume; private int _previousVolume;
private bool _currentRandom; private bool _currentRandom;
@ -43,16 +44,17 @@ namespace unison
private bool _currentConsume; private bool _currentConsume;
private double _currentTime; private double _currentTime;
private double _totalTime; private double _totalTime;
private IEnumerable<IMpdFile> _Playlist;
private MpdStatus _currentStatus; private MpdStatus _currentStatus;
private IMpdFile _currentSong; private IMpdFile _currentSong;
private BitmapImage _cover; private BitmapImage _cover;
public Statistics _stats; private readonly Statistics _stats;
private readonly System.Timers.Timer _elapsedTimer; private readonly System.Timers.Timer _elapsedTimer;
private DispatcherTimer _retryTimer; private readonly DispatcherTimer _retryTimer;
bool _isUpdatingStatus = false; private bool _isUpdatingStatus = false;
bool _isUpdatingSong = false; private bool _isUpdatingSong = false;
public IPAddress _ipAddress; public IPAddress _ipAddress;
@ -65,9 +67,11 @@ namespace unison
private MpcConnection _commandConnection; private MpcConnection _commandConnection;
private IPEndPoint _mpdEndpoint; private IPEndPoint _mpdEndpoint;
private CancellationTokenSource _cancelCommand; public CancellationTokenSource _cancelCommand;
private CancellationTokenSource _cancelConnect; private CancellationTokenSource _cancelConnect;
private bool UpdateStarted = false;
public MPDHandler() public MPDHandler()
{ {
Startup(null, null); Startup(null, null);
@ -152,7 +156,7 @@ namespace unison
if (_commandConnection == null || !IsConnected()) if (_commandConnection == null || !IsConnected())
{ {
Trace.WriteLine("[SafelySendCommandAsync] no command connection"); Trace.WriteLine("[SafelySendCommandAsync] no command connection");
return default(T); return default;
} }
try try
@ -174,7 +178,7 @@ namespace unison
Trace.WriteLine($"Sending {command.GetType().Name} failed: {e.Message}"); Trace.WriteLine($"Sending {command.GetType().Name} failed: {e.Message}");
} }
return default(T); return default;
} }
public async void Startup(object sender, EventArgs e) public async void Startup(object sender, EventArgs e)
@ -336,12 +340,15 @@ namespace unison
{ {
try try
{ {
if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options")) if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options") || subsystems.Contains("update"))
{ {
await UpdateStatusAsync(); await UpdateStatusAsync();
if (subsystems.Contains("player")) if (subsystems.Contains("player"))
await UpdateSongAsync(); await UpdateSongAsync();
if (subsystems.Contains("update"))
UpdateDatabaseSync();
} }
} }
catch (Exception e) catch (Exception e)
@ -351,6 +358,30 @@ namespace unison
} }
} }
private void UpdateDatabaseSync()
{
if (!UpdateStarted)
{
Application.Current.Dispatcher.Invoke(() =>
{
MainWindow MainWin = (MainWindow)Application.Current.MainWindow;
MainWin.GetSettings().MPDDatabaseUpdate_Start();
});
UpdateStarted = true;
}
else
{
Application.Current.Dispatcher.Invoke(() =>
{
MainWindow MainWin = (MainWindow)Application.Current.MainWindow;
MainWin.GetSettings().MPDDatabaseUpdate_Stop();
MainWin.UpdateStats();
});
UpdateStarted = false;
}
}
private async Task UpdateStatusAsync() private async Task UpdateStatusAsync()
{ {
if (_connection == null || _isUpdatingStatus) if (_connection == null || _isUpdatingStatus)
@ -405,6 +436,8 @@ namespace unison
} }
_isUpdatingSong = false; _isUpdatingSong = false;
Trace.WriteLine("Updated song");
} }
private async void GetAlbumCover(string path, CancellationToken token) private async void GetAlbumCover(string path, CancellationToken token)
@ -525,13 +558,27 @@ namespace unison
public string GetVersion() => _version; public string GetVersion() => _version;
public Statistics GetStats() => _stats; public Statistics GetStats() => _stats;
public double GetCurrentTime() => _currentTime; public double GetCurrentTime() => _currentTime;
public IEnumerable<IMpdFile> GetPlaylist() => _Playlist;
public bool IsConnected() => _connected; public bool IsConnected() => _connected;
public bool IsPlaying() => _currentStatus?.State == MpdState.Play; public bool IsPlaying() => _currentStatus?.State == MpdState.Play;
public void Prev() => SendCommand(new PreviousCommand()); public bool CanPrevNext = true;
public void Next() => SendCommand(new NextCommand());
public void Prev()
{
if (CanPrevNext && !_isUpdatingSong)
SendCommand(new PreviousCommand());
}
public void Next()
{
if (CanPrevNext && !_isUpdatingSong)
SendCommand(new NextCommand());
}
public void PlayPause() => SendCommand(new PauseResumeCommand()); public void PlayPause() => SendCommand(new PauseResumeCommand());
public void Play(int pos) => SendCommand(new PlayCommand(pos));
public void Random() => SendCommand(new RandomCommand(!_currentRandom)); public void Random() => SendCommand(new RandomCommand(!_currentRandom));
public void Repeat() => SendCommand(new RepeatCommand(!_currentRepeat)); public void Repeat() => SendCommand(new RepeatCommand(!_currentRepeat));
@ -586,27 +633,61 @@ namespace unison
SendCommand(commandList); SendCommand(commandList);
} }
public async Task QueryPlaylist() => _Playlist = await SafelySendCommandAsync(new PlaylistCommand());
public int GetPlaylistCount()
{
if (_Playlist == null)
return 0;
return _Playlist.ToArray().Length;
}
public void UpdateDB() => SendCommand(new UpdateCommand());
private static string FormatTime(TimeSpan time)
{
string FormattedTime = "";
if (time.Days == 1)
FormattedTime += $"{time.Days} {Resources.Resources.Day}, ";
else if (time.Days > 1)
FormattedTime += $"{time.Days} {Resources.Resources.Days}, ";
if (time.Hours == 1)
FormattedTime += $"{time.Hours} {Resources.Resources.Hour}, ";
else
FormattedTime += $"{time.Hours} {Resources.Resources.Hours}, ";
if (time.Minutes == 1)
FormattedTime += $"{time.Minutes} {Resources.Resources.Minute}, ";
else
FormattedTime += $"{time.Minutes} {Resources.Resources.Minutes}, ";
if (time.Seconds == 1)
FormattedTime += $"{time.Seconds} {Resources.Resources.Second}";
else
FormattedTime += $"{time.Seconds} {Resources.Resources.Seconds}";
return FormattedTime;
}
public async void QueryStats() public async void QueryStats()
{ {
Dictionary<string, string> response = await SafelySendCommandAsync(new StatsCommand()); Dictionary<string, string> Response = await SafelySendCommandAsync(new StatsCommand());
if (Response == null)
return;
if (response != null) _stats.Songs = int.Parse(Response["songs"]);
{ _stats.Albums = int.Parse(Response["albums"]);
_stats.Songs = int.Parse(response["songs"]); _stats.Artists = int.Parse(Response["artists"]);
_stats.Albums = int.Parse(response["albums"]);
_stats.Artists = int.Parse(response["artists"]);
TimeSpan time; _stats.Uptime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["uptime"])));
time = TimeSpan.FromSeconds(int.Parse(response["uptime"])); _stats.TotalPlaytime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["db_playtime"])));
_stats.Uptime = time.ToString(@"dd\:hh\:mm\:ss"); _stats.TotalTimePlayed = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["playtime"])));
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(); DateTime date = new DateTime(1970, 1, 1).AddSeconds(int.Parse(Response["db_update"])).ToLocalTime();
_stats.DatabaseUpdate = date.ToString("dd/MM/yyyy @ HH:mm"); string dayOfWeek = Resources.Resources.Culture.DateTimeFormat.GetDayName(date.DayOfWeek);
} _stats.DatabaseUpdate = dayOfWeek + " " + date.ToString("dd/MM/yyyy @ HH:mm");
} }
} }
} }

View File

@ -75,7 +75,6 @@ namespace unison.Handlers
public async Task<List<StationInfo>> AdvancedSearch(AdvancedSearchOptions options) public async Task<List<StationInfo>> AdvancedSearch(AdvancedSearchOptions options)
{ {
return await _radioBrowser.Search.AdvancedAsync(options); return await _radioBrowser.Search.AdvancedAsync(options);
} }
} }

110
Handlers/ShuffleHandler.cs Normal file
View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using MpcNET;
using MpcNET.Commands.Database;
using MpcNET.Commands.Playback;
using MpcNET.Commands.Queue;
using MpcNET.Commands.Reflection;
using MpcNET.Tags;
using MpcNET.Types;
using MpcNET.Types.Filters;
namespace unison
{
class ShuffleHandler
{
private readonly MPDHandler _mpd;
public int AddedSongs = 0;
public List<string> SongList { get; }
public ShuffleHandler()
{
SongList = new();
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
}
public async Task GetSongsFromFilter(List<IFilter> filter, CancellationToken token)
{
if (token.IsCancellationRequested)
return;
SongList.Clear();
int song = _mpd.GetStats().Songs;
IEnumerable<IMpdFile> response = await _mpd.SafelySendCommandAsync(new SearchCommand(filter, 0, song + 1));
foreach (IMpdFile file in response)
SongList.Add(file.Path);
}
public async Task AddToQueueRandom(int SongNumber, CancellationToken token)
{
if (token.IsCancellationRequested)
return;
int AddedSongs = 0;
var commandList = new CommandList();
int songTotal = _mpd.GetStats().Songs;
for (int i = 0; i < SongNumber; i++)
{
int song = new Random().Next(0, songTotal - 1);
commandList.Add(new SearchAddCommand(new FilterTag(MpdTags.Title, "", FilterOperator.Contains), song, song + 1));
AddedSongs++;
// play if stopped or unknown state (no queue managing at the moment, so mandatory)
if (i == 0 && (_mpd.GetStatus().State != MpdState.Play && _mpd.GetStatus().State != MpdState.Pause))
commandList.Add(new PlayCommand(0));
}
await _mpd.SafelySendCommandAsync(commandList);
}
public async Task AddToQueueFilter(int SongNumber, CancellationToken token)
{
if (token.IsCancellationRequested)
return;
int AddedSongs = 0;
// more (or equal) requested songs than available => add everything
if (SongNumber >= SongList.Count)
{
var commandList = new CommandList();
foreach (string path in SongList)
{
commandList.Add(new AddCommand(path));
AddedSongs++;
}
commandList.Add(new PlayCommand(0));
await _mpd.SafelySendCommandAsync(commandList);
}
// more available songs than requested =>
// we add unique indexes until we reach the requested amount
else
{
HashSet<int> SongIndex = new();
while (SongIndex.Count < SongNumber)
{
int MaxIndex = new Random().Next(0, SongList.Count - 1);
SongIndex.Add(MaxIndex);
}
var commandList = new CommandList();
foreach (int index in SongIndex)
{
commandList.Add(new AddCommand(SongList[index]));
AddedSongs++;
}
commandList.Add(new PlayCommand(0));
await _mpd.SafelySendCommandAsync(commandList);
}
}
}
}

View File

@ -8,6 +8,7 @@ namespace unison
public class SnapcastHandler public class SnapcastHandler
{ {
private readonly Process _snapcast = new(); private readonly Process _snapcast = new();
public bool HasStarted { get; private set; } public bool HasStarted { get; private set; }
public void OnConnectionChanged(object sender, EventArgs e) public void OnConnectionChanged(object sender, EventArgs e)
@ -30,7 +31,7 @@ namespace unison
}); });
} }
public void UpdateInterface() public static void UpdateInterface()
{ {
TaskbarIcon Systray = (TaskbarIcon)Application.Current.Properties["systray"]; TaskbarIcon Systray = (TaskbarIcon)Application.Current.Properties["systray"];
SystrayViewModel DataContext = Systray.DataContext as SystrayViewModel; SystrayViewModel DataContext = Systray.DataContext as SystrayViewModel;

View File

@ -1,5 +1,4 @@
using System.Diagnostics; using System.Windows;
using System.Windows;
using AutoUpdaterDotNET; using AutoUpdaterDotNET;
namespace unison.Handlers namespace unison.Handlers
@ -9,10 +8,10 @@ namespace unison.Handlers
readonly string xmlFile = "https://raw.githubusercontent.com/ZetaKebab/unison/main/Installer/unison.xml"; readonly string xmlFile = "https://raw.githubusercontent.com/ZetaKebab/unison/main/Installer/unison.xml";
private bool _UpdateAvailable = false; private bool _UpdateAvailable = false;
public bool UpdateAvailable() => _UpdateAvailable;
private bool _RequestedCheck = false; private bool _RequestedCheck = false;
public bool UpdateAvailable() => _UpdateAvailable;
public UpdateHandler() public UpdateHandler()
{ {
AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent; AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
@ -25,7 +24,7 @@ namespace unison.Handlers
AutoUpdater.Start(xmlFile); AutoUpdater.Start(xmlFile);
} }
private string CutVersionNumber(string number) private static string CutVersionNumber(string number)
{ {
return number.Substring(0, number.LastIndexOf(".")); return number.Substring(0, number.LastIndexOf("."));
} }

View File

@ -1,5 +1,5 @@
#define Name "unison" #define Name "unison"
#define Version "1.3.1" #define Version "1.4"
#define Snapcast "snapclient_0.26.0-1_win64" #define Snapcast "snapclient_0.26.0-1_win64"
#define Publisher "Th<54>o Marchal" #define Publisher "Th<54>o Marchal"
#define URL "https://github.com/ZetaKebab/unison" #define URL "https://github.com/ZetaKebab/unison"

View File

@ -12,7 +12,7 @@ namespace unison.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.4.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -286,5 +286,29 @@ namespace unison.Properties {
this["showWindow_vk"] = value; this["showWindow_vk"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("100")]
public double MainWindowTop {
get {
return ((double)(this["MainWindowTop"]));
}
set {
this["MainWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("100")]
public double MainWindowLeft {
get {
return ((double)(this["MainWindowLeft"]));
}
set {
this["MainWindowLeft"] = value;
}
}
} }
} }

View File

@ -68,5 +68,11 @@
<Setting Name="showWindow_vk" Type="System.UInt32" Scope="User"> <Setting Name="showWindow_vk" Type="System.UInt32" Scope="User">
<Value Profile="(Default)">13</Value> <Value Profile="(Default)">13</Value>
</Setting> </Setting>
<Setting Name="MainWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">100</Value>
</Setting>
<Setting Name="MainWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">100</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -60,6 +60,33 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Database.
/// </summary>
public static string Database {
get {
return ResourceManager.GetString("Database", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to day.
/// </summary>
public static string Day {
get {
return ResourceManager.GetString("Day", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to days.
/// </summary>
public static string Days {
get {
return ResourceManager.GetString("Days", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Exit. /// Looks up a localized string similar to Exit.
/// </summary> /// </summary>
@ -69,6 +96,123 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Album.
/// </summary>
public static string FilterType_Album {
get {
return ResourceManager.GetString("FilterType_Album", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Artist.
/// </summary>
public static string FilterType_Artist {
get {
return ResourceManager.GetString("FilterType_Artist", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Directory.
/// </summary>
public static string FilterType_Directory {
get {
return ResourceManager.GetString("FilterType_Directory", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Genre.
/// </summary>
public static string FilterType_Genre {
get {
return ResourceManager.GetString("FilterType_Genre", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Song.
/// </summary>
public static string FilterType_Song {
get {
return ResourceManager.GetString("FilterType_Song", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Year.
/// </summary>
public static string FilterType_Year {
get {
return ResourceManager.GetString("FilterType_Year", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to hour.
/// </summary>
public static string Hour {
get {
return ResourceManager.GetString("Hour", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to hours.
/// </summary>
public static string Hours {
get {
return ResourceManager.GetString("Hours", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to minute.
/// </summary>
public static string Minute {
get {
return ResourceManager.GetString("Minute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to minutes.
/// </summary>
public static string Minutes {
get {
return ResourceManager.GetString("Minutes", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to contains.
/// </summary>
public static string Operator_Contains {
get {
return ResourceManager.GetString("Operator_Contains", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to is.
/// </summary>
public static string Operator_Is {
get {
return ResourceManager.GetString("Operator_Is", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to is not.
/// </summary>
public static string Operator_IsNot {
get {
return ResourceManager.GetString("Operator_IsNot", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Country. /// Looks up a localized string similar to Country.
/// </summary> /// </summary>
@ -150,6 +294,24 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to second.
/// </summary>
public static string Second {
get {
return ResourceManager.GetString("Second", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to seconds.
/// </summary>
public static string Seconds {
get {
return ResourceManager.GetString("Seconds", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Settings. /// Looks up a localized string similar to Settings.
/// </summary> /// </summary>
@ -348,6 +510,51 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to The shuffle window allows to add random songs to your queue. Both options take into account the filter..
/// </summary>
public static string Settings_Shuffle1 {
get {
return ResourceManager.GetString("Settings_Shuffle1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to If the filter is empty, the entire music library is taken into account..
/// </summary>
public static string Settings_Shuffle2 {
get {
return ResourceManager.GetString("Settings_Shuffle2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to The filter is queried each time you use the Add to queue or Continuous shuffle options..
/// </summary>
public static string Settings_Shuffle3 {
get {
return ResourceManager.GetString("Settings_Shuffle3", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Add a fixed number of songs to the queue. For technical reasons, it is limited to 100 random songs without a filter, and to 1000 songs with a filter..
/// </summary>
public static string Settings_Shuffle4 {
get {
return ResourceManager.GetString("Settings_Shuffle4", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to By enabling this option, unison will automatically add songs to the queue so you never run out of songs to listen to..
/// </summary>
public static string Settings_Shuffle5 {
get {
return ResourceManager.GetString("Settings_Shuffle5", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to You can change to your own locally installed version of the Snapcast client with an. /// Looks up a localized string similar to You can change to your own locally installed version of the Snapcast client with an.
/// </summary> /// </summary>
@ -438,6 +645,15 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Update database.
/// </summary>
public static string Settings_UpdateDatabase {
get {
return ResourceManager.GetString("Settings_UpdateDatabase", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Version:. /// Looks up a localized string similar to Version:.
/// </summary> /// </summary>
@ -492,6 +708,132 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Shuffle.
/// </summary>
public static string Shuffle {
get {
return ResourceManager.GetString("Shuffle", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Adding.
/// </summary>
public static string Shuffle_ButtonMessage1 {
get {
return ResourceManager.GetString("Shuffle_ButtonMessage1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to songs....
/// </summary>
public static string Shuffle_ButtonMessage2 {
get {
return ResourceManager.GetString("Shuffle_ButtonMessage2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to done!.
/// </summary>
public static string Shuffle_ButtonMessage3 {
get {
return ResourceManager.GetString("Shuffle_ButtonMessage3", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Continuous shuffle.
/// </summary>
public static string Shuffle_Continuous {
get {
return ResourceManager.GetString("Shuffle_Continuous", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Enable continuous shuffle.
/// </summary>
public static string Shuffle_ContinuousEnable {
get {
return ResourceManager.GetString("Shuffle_ContinuousEnable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Filter.
/// </summary>
public static string Shuffle_Filter {
get {
return ResourceManager.GetString("Shuffle_Filter", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Query filter.
/// </summary>
public static string Shuffle_FilterQuery {
get {
return ResourceManager.GetString("Shuffle_FilterQuery", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Reset.
/// </summary>
public static string Shuffle_FilterReset {
get {
return ResourceManager.GetString("Shuffle_FilterReset", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Number of songs in filter:.
/// </summary>
public static string Shuffle_FilterSongNumber {
get {
return ResourceManager.GetString("Shuffle_FilterSongNumber", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Querying filter....
/// </summary>
public static string Shuffle_Querying1 {
get {
return ResourceManager.GetString("Shuffle_Querying1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to done!.
/// </summary>
public static string Shuffle_Querying2 {
get {
return ResourceManager.GetString("Shuffle_Querying2", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Add to queue.
/// </summary>
public static string Shuffle_Queue {
get {
return ResourceManager.GetString("Shuffle_Queue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Songs to add.
/// </summary>
public static string Shuffle_QueueSongs {
get {
return ResourceManager.GetString("Shuffle_QueueSongs", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Snapcast error. /// Looks up a localized string similar to Snapcast error.
/// </summary> /// </summary>
@ -537,15 +879,6 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Stats.
/// </summary>
public static string Stats {
get {
return ResourceManager.GetString("Stats", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Albums:. /// Looks up a localized string similar to Albums:.
/// </summary> /// </summary>
@ -600,6 +933,24 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Updating database....
/// </summary>
public static string Stats_UpdateDBMessage1 {
get {
return ResourceManager.GetString("Stats_UpdateDBMessage1", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to done!.
/// </summary>
public static string Stats_UpdateDBMessage2 {
get {
return ResourceManager.GetString("Stats_UpdateDBMessage2", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to MPD uptime:. /// Looks up a localized string similar to MPD uptime:.
/// </summary> /// </summary>

View File

@ -117,9 +117,57 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Database" xml:space="preserve">
<value>Base de datos</value>
</data>
<data name="Day" xml:space="preserve">
<value>día</value>
</data>
<data name="Days" xml:space="preserve">
<value>días</value>
</data>
<data name="Exit" xml:space="preserve"> <data name="Exit" xml:space="preserve">
<value>Salir</value> <value>Salir</value>
</data> </data>
<data name="FilterType_Album" xml:space="preserve">
<value>Album</value>
</data>
<data name="FilterType_Artist" xml:space="preserve">
<value>Artista</value>
</data>
<data name="FilterType_Directory" xml:space="preserve">
<value>Directorio</value>
</data>
<data name="FilterType_Genre" xml:space="preserve">
<value>Género</value>
</data>
<data name="FilterType_Song" xml:space="preserve">
<value>Canción</value>
</data>
<data name="FilterType_Year" xml:space="preserve">
<value>Año</value>
</data>
<data name="Hour" xml:space="preserve">
<value>hora</value>
</data>
<data name="Hours" xml:space="preserve">
<value>horas</value>
</data>
<data name="Minute" xml:space="preserve">
<value>minuto</value>
</data>
<data name="Minutes" xml:space="preserve">
<value>minutos</value>
</data>
<data name="Operator_Contains" xml:space="preserve">
<value>contiene</value>
</data>
<data name="Operator_Is" xml:space="preserve">
<value>es</value>
</data>
<data name="Operator_IsNot" xml:space="preserve">
<value>no es</value>
</data>
<data name="Radios" xml:space="preserve"> <data name="Radios" xml:space="preserve">
<value>Radios</value> <value>Radios</value>
</data> </data>
@ -147,6 +195,12 @@
<data name="Radio_Tags" xml:space="preserve"> <data name="Radio_Tags" xml:space="preserve">
<value>Tags</value> <value>Tags</value>
</data> </data>
<data name="Second" xml:space="preserve">
<value>segundo</value>
</data>
<data name="Seconds" xml:space="preserve">
<value>segundos</value>
</data>
<data name="Settings" xml:space="preserve"> <data name="Settings" xml:space="preserve">
<value>Ajustes</value> <value>Ajustes</value>
</data> </data>
@ -213,6 +267,21 @@
<data name="Settings_ShowWindow" xml:space="preserve"> <data name="Settings_ShowWindow" xml:space="preserve">
<value>Mostrar ventana</value> <value>Mostrar ventana</value>
</data> </data>
<data name="Settings_Shuffle1" xml:space="preserve">
<value>La ventana Aleatorio permite agregar canciones aleatorias a la fila. La dos opciones tienen en cuenta el filtro.</value>
</data>
<data name="Settings_Shuffle2" xml:space="preserve">
<value>Si el filtro es vacío, la integralidad de la biblioteca musical se tiene en cuenta.</value>
</data>
<data name="Settings_Shuffle3" xml:space="preserve">
<value>El filtro es buscado cada vez que las opciones Agregar a la fila o Aleatorio continuo son usadas.</value>
</data>
<data name="Settings_Shuffle4" xml:space="preserve">
<value>Agrega un número dado de canciones a la fila. For razones tecnicas, es opción es limitada a 100 canciones aleatorias sin filtro, y a 1000 canciones con filtro.</value>
</data>
<data name="Settings_Shuffle5" xml:space="preserve">
<value>Activando esa opción, unison va a agregar automaticamente canciones a la fila para nunca llegar al cabo de canciones a escuchar.</value>
</data>
<data name="Settings_SnapcastInfo1" xml:space="preserve"> <data name="Settings_SnapcastInfo1" xml:space="preserve">
<value>Puede cambiar a su propia versión instalada localmente del cliente Snapcast con una ruta</value> <value>Puede cambiar a su propia versión instalada localmente del cliente Snapcast con una ruta</value>
</data> </data>
@ -243,6 +312,9 @@
<data name="Settings_SourceCode2" xml:space="preserve"> <data name="Settings_SourceCode2" xml:space="preserve">
<value>aquí</value> <value>aquí</value>
</data> </data>
<data name="Settings_UpdateDatabase" xml:space="preserve">
<value>Actualizar base de datos</value>
</data>
<data name="Settings_Version" xml:space="preserve"> <data name="Settings_Version" xml:space="preserve">
<value>Versión:</value> <value>Versión:</value>
</data> </data>
@ -261,6 +333,48 @@
<data name="ShowWindow" xml:space="preserve"> <data name="ShowWindow" xml:space="preserve">
<value>Mostrar ventana</value> <value>Mostrar ventana</value>
</data> </data>
<data name="Shuffle" xml:space="preserve">
<value>Aleatorio</value>
</data>
<data name="Shuffle_ButtonMessage1" xml:space="preserve">
<value>Agregando</value>
</data>
<data name="Shuffle_ButtonMessage2" xml:space="preserve">
<value>canciones...</value>
</data>
<data name="Shuffle_ButtonMessage3" xml:space="preserve">
<value>¡terminado!</value>
</data>
<data name="Shuffle_Continuous" xml:space="preserve">
<value>Aleatorio continuo</value>
</data>
<data name="Shuffle_ContinuousEnable" xml:space="preserve">
<value>Empezar aleatorio continuo</value>
</data>
<data name="Shuffle_Filter" xml:space="preserve">
<value>Filtro</value>
</data>
<data name="Shuffle_FilterQuery" xml:space="preserve">
<value>Búsqueda de filtro</value>
</data>
<data name="Shuffle_FilterReset" xml:space="preserve">
<value>Reinicializar</value>
</data>
<data name="Shuffle_FilterSongNumber" xml:space="preserve">
<value>Canciones en el filtro:</value>
</data>
<data name="Shuffle_Querying1" xml:space="preserve">
<value>Búsqueda de filtro...</value>
</data>
<data name="Shuffle_Querying2" xml:space="preserve">
<value>¡terminado!</value>
</data>
<data name="Shuffle_Queue" xml:space="preserve">
<value>Agregar a la fila</value>
</data>
<data name="Shuffle_QueueSongs" xml:space="preserve">
<value>Canciones para agregar</value>
</data>
<data name="Snapcast_Popup1" xml:space="preserve"> <data name="Snapcast_Popup1" xml:space="preserve">
<value>Error Snapcast</value> <value>Error Snapcast</value>
</data> </data>
@ -276,9 +390,6 @@
<data name="StartSnapcast" xml:space="preserve"> <data name="StartSnapcast" xml:space="preserve">
<value>Iniciar Snapcast</value> <value>Iniciar Snapcast</value>
</data> </data>
<data name="Stats" xml:space="preserve">
<value>Estadísticas</value>
</data>
<data name="Stats_Albums" xml:space="preserve"> <data name="Stats_Albums" xml:space="preserve">
<value>Álbumes:</value> <value>Álbumes:</value>
</data> </data>
@ -297,6 +408,12 @@
<data name="Stats_TotalTimePlayed" xml:space="preserve"> <data name="Stats_TotalTimePlayed" xml:space="preserve">
<value>Tiempo total jugado:</value> <value>Tiempo total jugado:</value>
</data> </data>
<data name="Stats_UpdateDBMessage1" xml:space="preserve">
<value>Actualizando base de datos...</value>
</data>
<data name="Stats_UpdateDBMessage2" xml:space="preserve">
<value> ¡terminado!</value>
</data>
<data name="Stats_Uptime" xml:space="preserve"> <data name="Stats_Uptime" xml:space="preserve">
<value>Tiempo de actividad de MPD:</value> <value>Tiempo de actividad de MPD:</value>
</data> </data>

View File

@ -117,9 +117,57 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Database" xml:space="preserve">
<value>Base de donnée</value>
</data>
<data name="Day" xml:space="preserve">
<value>jour</value>
</data>
<data name="Days" xml:space="preserve">
<value>jours</value>
</data>
<data name="Exit" xml:space="preserve"> <data name="Exit" xml:space="preserve">
<value>Quitter</value> <value>Quitter</value>
</data> </data>
<data name="FilterType_Album" xml:space="preserve">
<value>Album</value>
</data>
<data name="FilterType_Artist" xml:space="preserve">
<value>Artiste</value>
</data>
<data name="FilterType_Directory" xml:space="preserve">
<value>Dossier</value>
</data>
<data name="FilterType_Genre" xml:space="preserve">
<value>Genre</value>
</data>
<data name="FilterType_Song" xml:space="preserve">
<value>Chanson</value>
</data>
<data name="FilterType_Year" xml:space="preserve">
<value>Année</value>
</data>
<data name="Hour" xml:space="preserve">
<value>heure</value>
</data>
<data name="Hours" xml:space="preserve">
<value>heures</value>
</data>
<data name="Minute" xml:space="preserve">
<value>minute</value>
</data>
<data name="Minutes" xml:space="preserve">
<value>minutes</value>
</data>
<data name="Operator_Contains" xml:space="preserve">
<value>contient</value>
</data>
<data name="Operator_Is" xml:space="preserve">
<value>est</value>
</data>
<data name="Operator_IsNot" xml:space="preserve">
<value>n'est pas</value>
</data>
<data name="Radios" xml:space="preserve"> <data name="Radios" xml:space="preserve">
<value>Radios</value> <value>Radios</value>
</data> </data>
@ -147,6 +195,12 @@
<data name="Radio_Tags" xml:space="preserve"> <data name="Radio_Tags" xml:space="preserve">
<value>Tags</value> <value>Tags</value>
</data> </data>
<data name="Second" xml:space="preserve">
<value>seconde</value>
</data>
<data name="Seconds" xml:space="preserve">
<value>secondes</value>
</data>
<data name="Settings" xml:space="preserve"> <data name="Settings" xml:space="preserve">
<value>Configuration</value> <value>Configuration</value>
</data> </data>
@ -213,6 +267,21 @@
<data name="Settings_ShowWindow" xml:space="preserve"> <data name="Settings_ShowWindow" xml:space="preserve">
<value>Afficher la fenêtre</value> <value>Afficher la fenêtre</value>
</data> </data>
<data name="Settings_Shuffle1" xml:space="preserve">
<value>La fenêtre aléatoire permet d'ajouter des chansons aléatoires à la file. Les deux options prennent en compte le filtre.</value>
</data>
<data name="Settings_Shuffle2" xml:space="preserve">
<value>Si le filtre est vide, l'intégralité de la bibliothèque est prise en compte.</value>
</data>
<data name="Settings_Shuffle3" xml:space="preserve">
<value>Le filtre est recherché à chaque fois que les options Ajouter à la file ou Aléatoire continu sont utilisées.</value>
</data>
<data name="Settings_Shuffle4" xml:space="preserve">
<value>Ajoute un nombre fixe de chansons à la file. Pour des raisons techniques, cette option est limitée à 100 chansons aléatoires sans filtre, et à 1000 chansons avec filtre.</value>
</data>
<data name="Settings_Shuffle5" xml:space="preserve">
<value>En activant cette option, unison va ajouter automatiquement des chansons à la file pour ne jamais arriver à cours de chansons à écouter.</value>
</data>
<data name="Settings_SnapcastInfo1" xml:space="preserve"> <data name="Settings_SnapcastInfo1" xml:space="preserve">
<value>Il est possible de mettre votre version localement installé de Snapcast avec un </value> <value>Il est possible de mettre votre version localement installé de Snapcast avec un </value>
</data> </data>
@ -243,6 +312,9 @@
<data name="Settings_SourceCode2" xml:space="preserve"> <data name="Settings_SourceCode2" xml:space="preserve">
<value>ici</value> <value>ici</value>
</data> </data>
<data name="Settings_UpdateDatabase" xml:space="preserve">
<value>Mettre à jour la base de donnée</value>
</data>
<data name="Settings_Version" xml:space="preserve"> <data name="Settings_Version" xml:space="preserve">
<value>Version :</value> <value>Version :</value>
</data> </data>
@ -261,6 +333,48 @@
<data name="ShowWindow" xml:space="preserve"> <data name="ShowWindow" xml:space="preserve">
<value>Montrer la fenêtre</value> <value>Montrer la fenêtre</value>
</data> </data>
<data name="Shuffle" xml:space="preserve">
<value>Aléatoire</value>
</data>
<data name="Shuffle_ButtonMessage1" xml:space="preserve">
<value>Ajout de</value>
</data>
<data name="Shuffle_ButtonMessage2" xml:space="preserve">
<value>chansons...</value>
</data>
<data name="Shuffle_ButtonMessage3" xml:space="preserve">
<value>fini !</value>
</data>
<data name="Shuffle_Continuous" xml:space="preserve">
<value>Aléatoire continu</value>
</data>
<data name="Shuffle_ContinuousEnable" xml:space="preserve">
<value>Activer le mode aléatoire continu</value>
</data>
<data name="Shuffle_Filter" xml:space="preserve">
<value>Filtre</value>
</data>
<data name="Shuffle_FilterQuery" xml:space="preserve">
<value>Recherche du filtre</value>
</data>
<data name="Shuffle_FilterReset" xml:space="preserve">
<value>Réinitialiser</value>
</data>
<data name="Shuffle_FilterSongNumber" xml:space="preserve">
<value>Nombre de chansons dans le filtre :</value>
</data>
<data name="Shuffle_Querying1" xml:space="preserve">
<value>Recherche du filtre...</value>
</data>
<data name="Shuffle_Querying2" xml:space="preserve">
<value>fini !</value>
</data>
<data name="Shuffle_Queue" xml:space="preserve">
<value>Ajouter à la file</value>
</data>
<data name="Shuffle_QueueSongs" xml:space="preserve">
<value>Chansons à ajouter</value>
</data>
<data name="Snapcast_Popup1" xml:space="preserve"> <data name="Snapcast_Popup1" xml:space="preserve">
<value>Erreur Snapcast</value> <value>Erreur Snapcast</value>
</data> </data>
@ -276,9 +390,6 @@
<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"> <data name="Stats_Albums" xml:space="preserve">
<value>Albums :</value> <value>Albums :</value>
</data> </data>
@ -297,6 +408,12 @@
<data name="Stats_TotalTimePlayed" xml:space="preserve"> <data name="Stats_TotalTimePlayed" xml:space="preserve">
<value>Temps d'écoute écoulé :</value> <value>Temps d'écoute écoulé :</value>
</data> </data>
<data name="Stats_UpdateDBMessage1" xml:space="preserve">
<value>Mise à jour de la base de donnée...</value>
</data>
<data name="Stats_UpdateDBMessage2" xml:space="preserve">
<value> fini !</value>
</data>
<data name="Stats_Uptime" xml:space="preserve"> <data name="Stats_Uptime" xml:space="preserve">
<value>MPD lancé depuis :</value> <value>MPD lancé depuis :</value>
</data> </data>

View File

@ -117,9 +117,57 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Database" xml:space="preserve">
<value>Database</value>
</data>
<data name="Day" xml:space="preserve">
<value>day</value>
</data>
<data name="Days" xml:space="preserve">
<value>days</value>
</data>
<data name="Exit" xml:space="preserve"> <data name="Exit" xml:space="preserve">
<value>Exit</value> <value>Exit</value>
</data> </data>
<data name="FilterType_Album" xml:space="preserve">
<value>Album</value>
</data>
<data name="FilterType_Artist" xml:space="preserve">
<value>Artist</value>
</data>
<data name="FilterType_Directory" xml:space="preserve">
<value>Directory</value>
</data>
<data name="FilterType_Genre" xml:space="preserve">
<value>Genre</value>
</data>
<data name="FilterType_Song" xml:space="preserve">
<value>Song</value>
</data>
<data name="FilterType_Year" xml:space="preserve">
<value>Year</value>
</data>
<data name="Hour" xml:space="preserve">
<value>hour</value>
</data>
<data name="Hours" xml:space="preserve">
<value>hours</value>
</data>
<data name="Minute" xml:space="preserve">
<value>minute</value>
</data>
<data name="Minutes" xml:space="preserve">
<value>minutes</value>
</data>
<data name="Operator_Contains" xml:space="preserve">
<value>contains</value>
</data>
<data name="Operator_Is" xml:space="preserve">
<value>is</value>
</data>
<data name="Operator_IsNot" xml:space="preserve">
<value>is not</value>
</data>
<data name="Radios" xml:space="preserve"> <data name="Radios" xml:space="preserve">
<value>Radios</value> <value>Radios</value>
</data> </data>
@ -147,6 +195,12 @@
<data name="Radio_Tags" xml:space="preserve"> <data name="Radio_Tags" xml:space="preserve">
<value>Tags</value> <value>Tags</value>
</data> </data>
<data name="Second" xml:space="preserve">
<value>second</value>
</data>
<data name="Seconds" xml:space="preserve">
<value>seconds</value>
</data>
<data name="Settings" xml:space="preserve"> <data name="Settings" xml:space="preserve">
<value>Settings</value> <value>Settings</value>
</data> </data>
@ -213,6 +267,21 @@
<data name="Settings_ShowWindow" xml:space="preserve"> <data name="Settings_ShowWindow" xml:space="preserve">
<value>Show window</value> <value>Show window</value>
</data> </data>
<data name="Settings_Shuffle1" xml:space="preserve">
<value>The shuffle window allows to add random songs to your queue. Both options take into account the filter.</value>
</data>
<data name="Settings_Shuffle2" xml:space="preserve">
<value>If the filter is empty, the entire music library is taken into account.</value>
</data>
<data name="Settings_Shuffle3" xml:space="preserve">
<value>The filter is queried each time you use the Add to queue or Continuous shuffle options.</value>
</data>
<data name="Settings_Shuffle4" xml:space="preserve">
<value>Add a fixed number of songs to the queue. For technical reasons, it is limited to 100 random songs without a filter, and to 1000 songs with a filter.</value>
</data>
<data name="Settings_Shuffle5" xml:space="preserve">
<value>By enabling this option, unison will automatically add songs to the queue so you never run out of songs to listen to.</value>
</data>
<data name="Settings_SnapcastInfo1" xml:space="preserve"> <data name="Settings_SnapcastInfo1" xml:space="preserve">
<value>You can change to your own locally installed version of the Snapcast client with an</value> <value>You can change to your own locally installed version of the Snapcast client with an</value>
</data> </data>
@ -243,6 +312,9 @@
<data name="Settings_SourceCode2" xml:space="preserve"> <data name="Settings_SourceCode2" xml:space="preserve">
<value>here</value> <value>here</value>
</data> </data>
<data name="Settings_UpdateDatabase" xml:space="preserve">
<value>Update database</value>
</data>
<data name="Settings_Version" xml:space="preserve"> <data name="Settings_Version" xml:space="preserve">
<value>Version:</value> <value>Version:</value>
</data> </data>
@ -261,6 +333,48 @@
<data name="ShowWindow" xml:space="preserve"> <data name="ShowWindow" xml:space="preserve">
<value>Show window</value> <value>Show window</value>
</data> </data>
<data name="Shuffle" xml:space="preserve">
<value>Shuffle</value>
</data>
<data name="Shuffle_ButtonMessage1" xml:space="preserve">
<value>Adding</value>
</data>
<data name="Shuffle_ButtonMessage2" xml:space="preserve">
<value>songs...</value>
</data>
<data name="Shuffle_ButtonMessage3" xml:space="preserve">
<value>done!</value>
</data>
<data name="Shuffle_Continuous" xml:space="preserve">
<value>Continuous shuffle</value>
</data>
<data name="Shuffle_ContinuousEnable" xml:space="preserve">
<value>Enable continuous shuffle</value>
</data>
<data name="Shuffle_Filter" xml:space="preserve">
<value>Filter</value>
</data>
<data name="Shuffle_FilterQuery" xml:space="preserve">
<value>Query filter</value>
</data>
<data name="Shuffle_FilterReset" xml:space="preserve">
<value>Reset</value>
</data>
<data name="Shuffle_FilterSongNumber" xml:space="preserve">
<value>Number of songs in filter:</value>
</data>
<data name="Shuffle_Querying1" xml:space="preserve">
<value>Querying filter...</value>
</data>
<data name="Shuffle_Querying2" xml:space="preserve">
<value>done!</value>
</data>
<data name="Shuffle_Queue" xml:space="preserve">
<value>Add to queue</value>
</data>
<data name="Shuffle_QueueSongs" xml:space="preserve">
<value>Songs to add</value>
</data>
<data name="Snapcast_Popup1" xml:space="preserve"> <data name="Snapcast_Popup1" xml:space="preserve">
<value>Snapcast error</value> <value>Snapcast error</value>
</data> </data>
@ -276,9 +390,6 @@
<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"> <data name="Stats_Albums" xml:space="preserve">
<value>Albums:</value> <value>Albums:</value>
</data> </data>
@ -297,6 +408,12 @@
<data name="Stats_TotalTimePlayed" xml:space="preserve"> <data name="Stats_TotalTimePlayed" xml:space="preserve">
<value>Total time played:</value> <value>Total time played:</value>
</data> </data>
<data name="Stats_UpdateDBMessage1" xml:space="preserve">
<value>Updating database...</value>
</data>
<data name="Stats_UpdateDBMessage2" xml:space="preserve">
<value>done!</value>
</data>
<data name="Stats_Uptime" xml:space="preserve"> <data name="Stats_Uptime" xml:space="preserve">
<value>MPD uptime:</value> <value>MPD uptime:</value>
</data> </data>

View File

@ -6,7 +6,7 @@
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
xmlns:properties="clr-namespace:unison.Resources" xmlns:properties="clr-namespace:unison.Resources"
mc:Ignorable="d" mc:Ignorable="d"
Title="unison" Closing="Window_Closing" Icon="/Resources/icon-full.ico" ResizeMode="CanMinimize" SizeToContent="WidthAndHeight"> Title="unison" Closing="Window_Closing" LocationChanged="Window_LocationChanged" Icon="/Resources/icon-full.ico" ResizeMode="CanMinimize" SizeToContent="WidthAndHeight">
<Window.Resources> <Window.Resources>
<Style TargetType="Border" x:Key="UnselectedButton"> <Style TargetType="Border" x:Key="UnselectedButton">
@ -17,7 +17,7 @@
</Style> </Style>
</Window.Resources> </Window.Resources>
<Grid Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}" MinHeight="270" MinWidth="650"> <Grid Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}" MinHeight="270" MinWidth="700">
<Grid x:Name="TopLayout" Margin="10,0,10,0" VerticalAlignment="Stretch" Width="Auto" Height="Auto"> <Grid x:Name="TopLayout" Margin="10,0,10,0" VerticalAlignment="Stretch" Width="Auto" Height="Auto">
<Grid x:Name="Display" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="225,0,0,0" Height="Auto" Width="Auto"> <Grid x:Name="Display" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="225,0,0,0" Height="Auto" Width="Auto">
<GroupBox Height="220" VerticalAlignment="Center"> <GroupBox Height="220" VerticalAlignment="Center">
@ -111,14 +111,15 @@
</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"> <StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,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="Shuffle" Padding="5, 2" Click="Shuffle_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" Margin="0,0,10,0">
<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 Text="{x:Static properties:Resources.Shuffle}" 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}" IsEnabled="False">
<Button x:Name="Radio" Padding="5, 2" HorizontalAlignment="Left" Click="Radios_Clicked" 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 Text="{x:Static properties:Resources.Radios}" Margin="5, 0, 0, 0"/> <TextBlock Text="{x:Static properties:Resources.Radios}" Margin="5, 0, 0, 0"/>
@ -136,12 +137,13 @@
<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="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="0,0,10,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 Text="Shuffle" 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="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"/>

View File

@ -6,7 +6,9 @@ using System.Windows.Threading;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using unison.Handlers; using System.Diagnostics;
using System.Data;
using MpcNET.Commands.Playback;
namespace unison namespace unison
{ {
@ -14,18 +16,24 @@ namespace unison
{ {
private readonly Settings _settingsWin; private readonly Settings _settingsWin;
private readonly Radios _radiosWin; private readonly Radios _radiosWin;
private readonly Shuffle _shuffleWin;
private readonly DispatcherTimer _timer; private readonly DispatcherTimer _timer;
private readonly MPDHandler _mpd; private readonly MPDHandler _mpd;
public Settings GetSettings() => _settingsWin;
public MainWindow() public MainWindow()
{ {
InitHwnd(); InitHwnd();
InitializeComponent(); InitializeComponent();
DefaultState(true); DefaultState(true);
WindowState = WindowState.Minimized; WindowState = WindowState.Minimized;
Top = Properties.Settings.Default.MainWindowTop;
Left = Properties.Settings.Default.MainWindowLeft;
_settingsWin = new Settings(); _settingsWin = new Settings();
_radiosWin = new Radios(); _radiosWin = new Radios();
_shuffleWin = new Shuffle();
_timer = new DispatcherTimer(); _timer = new DispatcherTimer();
_mpd = (MPDHandler)Application.Current.Properties["mpd"]; _mpd = (MPDHandler)Application.Current.Properties["mpd"];
@ -43,16 +51,26 @@ namespace unison
TimeSlider.Value = _mpd.GetCurrentTime() / _mpd.GetCurrentSong().Time * 100; TimeSlider.Value = _mpd.GetCurrentTime() / _mpd.GetCurrentSong().Time * 100;
} }
public void UpdateStats()
{
_mpd.QueryStats();
_settingsWin.UpdateStats();
}
public void OnConnectionChanged(object sender, EventArgs e) public void OnConnectionChanged(object sender, EventArgs e)
{ {
if (_mpd.IsConnected()) if (_mpd.IsConnected())
{ {
UpdateStats();
ConnectionOkIcon.Visibility = Visibility.Visible; ConnectionOkIcon.Visibility = Visibility.Visible;
ConnectionFailIcon.Visibility = Visibility.Collapsed; ConnectionFailIcon.Visibility = Visibility.Collapsed;
Snapcast.IsEnabled = true; Snapcast.IsEnabled = true;
if (_radiosWin.IsConnected()) if (_radiosWin.IsConnected())
Radio.IsEnabled = true; Radio.IsEnabled = true;
_shuffleWin.Initialize();
} }
else else
{ {
@ -68,7 +86,7 @@ namespace unison
Connection.Text = $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}"; Connection.Text = $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
} }
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;
@ -114,6 +132,19 @@ namespace unison
_timer.Start(); _timer.Start();
EndTime.Text = FormatSeconds(_mpd.GetCurrentSong().Time); EndTime.Text = FormatSeconds(_mpd.GetCurrentSong().Time);
} }
Trace.WriteLine("Song changed called!");
if (_shuffleWin.GetContinuous())
{
// force consume: there's no other way to be sure
// that we don't get to the end of the queue with nothing to play
_mpd.SendCommand(new ConsumeCommand(true));
_mpd.CanPrevNext = false;
await _shuffleWin.HandleContinuous();
_mpd.CanPrevNext = true;
}
} }
public void OnStatusChanged(object sender, EventArgs e) public void OnStatusChanged(object sender, EventArgs e)
@ -142,9 +173,6 @@ namespace unison
DefaultState(); DefaultState();
} }
} }
_mpd.QueryStats();
_settingsWin.UpdateStats();
} }
private void DefaultState(bool LostConnection = false) private void DefaultState(bool LostConnection = false)
@ -203,12 +231,12 @@ namespace unison
border.Style = b ? (Style)Resources["SelectedButton"] : (Style)Resources["UnselectedButton"]; border.Style = b ? (Style)Resources["SelectedButton"] : (Style)Resources["UnselectedButton"];
} }
public string FormatSeconds(int time) public static string FormatSeconds(int time)
{ {
TimeSpan timespan = TimeSpan.FromSeconds(time); TimeSpan timespan = TimeSpan.FromSeconds(time);
return timespan.ToString(@"mm\:ss"); return timespan.ToString(@"mm\:ss");
} }
public string FormatSeconds(double time) public static string FormatSeconds(double time)
{ {
TimeSpan timespan = TimeSpan.FromSeconds(time); TimeSpan timespan = TimeSpan.FromSeconds(time);
return timespan.ToString(@"mm\:ss"); return timespan.ToString(@"mm\:ss");
@ -217,7 +245,6 @@ namespace unison
public void Pause_Clicked(object sender, RoutedEventArgs e) => _mpd.PlayPause(); public void Pause_Clicked(object sender, RoutedEventArgs e) => _mpd.PlayPause();
public void Previous_Clicked(object sender, RoutedEventArgs e) => _mpd.Prev(); public void Previous_Clicked(object sender, RoutedEventArgs e) => _mpd.Prev();
public void Next_Clicked(object sender, RoutedEventArgs e) => _mpd.Next(); public void Next_Clicked(object sender, RoutedEventArgs e) => _mpd.Next();
public void Random_Clicked(object sender, RoutedEventArgs e) => _mpd.Random(); public void Random_Clicked(object sender, RoutedEventArgs e) => _mpd.Random();
public void Repeat_Clicked(object sender, RoutedEventArgs e) => _mpd.Repeat(); public void Repeat_Clicked(object sender, RoutedEventArgs e) => _mpd.Repeat();
public void Single_Clicked(object sender, RoutedEventArgs e) => _mpd.Single(); public void Single_Clicked(object sender, RoutedEventArgs e) => _mpd.Single();
@ -239,6 +266,15 @@ namespace unison
_radiosWin.WindowState = WindowState.Normal; _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();
@ -307,5 +343,12 @@ namespace unison
WindowState = WindowState.Minimized; WindowState = WindowState.Minimized;
Hide(); Hide();
} }
private void Window_LocationChanged(object sender, EventArgs e)
{
Properties.Settings.Default.MainWindowTop = Top;
Properties.Settings.Default.MainWindowLeft = Left;
Properties.Settings.Default.Save();
}
} }
} }

View File

@ -1,7 +1,7 @@
using System.Diagnostics; using System;
using System.Diagnostics;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Controls; using System.Windows.Controls;
@ -15,7 +15,7 @@ namespace unison
public partial class Radios : Window public partial class Radios : Window
{ {
private MPDHandler _mpd; private MPDHandler _mpd;
RadioHandler _radio; private RadioHandler _radio;
public bool IsConnected() => _radio.IsConnected(); public bool IsConnected() => _radio.IsConnected();

View File

@ -17,7 +17,7 @@
</x:Array> </x:Array>
</Window.Resources> </Window.Resources>
<Grid> <Grid MinWidth="390">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TabControl Margin="10"> <TabControl Margin="10">
<TabItem Header="MPD"> <TabItem Header="MPD">
@ -33,18 +33,18 @@
<StackPanel> <StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{x:Static properties:Resources.Settings_Host}" TextWrapping="Wrap" Margin="5,0,0,0"/> <TextBlock Text="{x:Static properties:Resources.Settings_Host}" TextWrapping="Wrap" Margin="5,0,0,0"/>
<TextBox x:Name="MpdHost" KeyDown="ConnectHandler" TextChanged="MpdConnectTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/> <TextBox x:Name="MpdHost" KeyDown="ConnectHandler" TextChanged="MpdConnectTextBox" TextWrapping="Wrap" Margin="10,2,0,0"/>
</StackPanel> </StackPanel>
<StackPanel Margin="0,5,0,0"> <StackPanel Margin="0,5,0,0">
<TextBlock Text="{x:Static properties:Resources.Settings_Port}" TextWrapping="Wrap" Margin="5,0,0,0"/> <TextBlock Text="{x:Static properties:Resources.Settings_Port}" TextWrapping="Wrap" Margin="5,0,0,0"/>
<TextBox x:Name="MpdPort" KeyDown="ConnectHandler" PreviewTextInput="NumberValidationTextBox" MaxLength="5" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/> <TextBox x:Name="MpdPort" KeyDown="ConnectHandler" PreviewTextInput="NumberValidationTextBox" MaxLength="5" TextWrapping="Wrap" 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_Password}" TextWrapping="Wrap" Margin="5,0,0,0"/> <TextBlock Text="{x:Static properties:Resources.Settings_Password}" TextWrapping="Wrap" Margin="5,0,0,0"/>
<PasswordBox x:Name="MpdPassword" KeyDown="ConnectHandler" Width="250" Margin="10,2,0,0"/> <PasswordBox x:Name="MpdPassword" KeyDown="ConnectHandler" Margin="10,2,0,0"/>
<TextBlock Text="{x:Static properties:Resources.Settings_ConnectionPasswordInfo}" TextWrapping="Wrap" Margin="10,5,0,0" MaxWidth="250" HorizontalAlignment="Left"/> <TextBlock Text="{x:Static properties:Resources.Settings_ConnectionPasswordInfo}" TextWrapping="Wrap" Margin="10,5,0,0" MaxWidth="390"/>
</StackPanel> </StackPanel>
<TextBlock TextWrapping="Wrap" Margin="5,10,0,0"> <TextBlock TextWrapping="Wrap" Margin="5,10,0,0">
@ -52,37 +52,7 @@
<Run x:Name="ConnectionStatus" Text="{x:Static properties:Resources.Settings_ConnectionStatusOffline}"/> <Run x:Name="ConnectionStatus" Text="{x:Static properties:Resources.Settings_ConnectionStatusOffline}"/>
</TextBlock> </TextBlock>
<Button x:Name="ConnectButton" Content="{x:Static properties:Resources.Settings_ConnectButton}" Margin="0,10,0,0" Width="120" Click="MPDConnect_Clicked"/> <Button x:Name="ConnectButton" Content="{x:Static properties:Resources.Settings_ConnectButton}" Margin="0,10,0,0" Width="120" Click="MPDConnect_Clicked" />
</StackPanel>
</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> </StackPanel>
</Grid> </Grid>
</GroupBox> </GroupBox>
@ -132,7 +102,7 @@
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}"> <Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/> <TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
</Button> </Button>
</StackPanel> </StackPanel>
@ -140,7 +110,7 @@
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}"> <Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/> <TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
</Button> </Button>
</StackPanel> </StackPanel>
@ -148,7 +118,7 @@
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}"> <Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/> <TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
</Button> </Button>
</StackPanel> </StackPanel>
@ -156,7 +126,7 @@
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}"> <Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/> <TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
</Button> </Button>
</StackPanel> </StackPanel>
@ -164,7 +134,7 @@
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}"> <Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/> <TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
</Button> </Button>
</StackPanel> </StackPanel>
@ -172,7 +142,7 @@
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}"> <Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/> <TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
</Button> </Button>
</StackPanel> </StackPanel>
@ -180,7 +150,7 @@
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox> <ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}"> <Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/> <TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
</Button> </Button>
</StackPanel> </StackPanel>
</Grid> </Grid>
@ -190,24 +160,84 @@
</StackPanel> </StackPanel>
<Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,10,0,0" Width="120" Click="ShortcutsReset_Clicked"/> <Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,10,0,0" Width="120" Click="ShortcutsReset_Clicked"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</GroupBox> </GroupBox>
</DockPanel> </DockPanel>
</TabItem> </TabItem>
<TabItem Header="{x:Static properties:Resources.Stats}"> <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" Margin="10,2,5,0"/>
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastPath}" TextWrapping="Wrap" Margin="5,5,0,0"/>
<TextBox x:Name="SnapcastPath" TextWrapping="Wrap" Margin="10,2,5,0"/>
<TextBlock TextWrapping="Wrap" Margin="5,5,0,0" TextAlignment="Left" Width="390">
<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}" Width="120" Click="SnapcastReset_Clicked" Margin="0,5,0,0" BorderThickness="1,1,1,1"/>
</StackPanel>
</StackPanel>
</Grid>
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="{x:Static properties:Resources.Shuffle}">
<DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="🔁 "/>
<Run Text="{x:Static properties:Resources.Shuffle}"></Run>
</TextBlock>
</GroupBox.Header>
<Grid MaxWidth="500">
<StackPanel>
<TextBlock TextWrapping="Wrap">
<Run Text="{x:Static properties:Resources.Settings_Shuffle1}"></Run>
<Run Text="{x:Static properties:Resources.Settings_Shuffle2}"></Run><LineBreak/>
<Run Text="{x:Static properties:Resources.Settings_Shuffle3}"></Run><LineBreak/><LineBreak/>
<Run FontWeight="Bold" Text="{x:Static properties:Resources.Shuffle_Queue}"></Run><LineBreak/>
<Run Text="{x:Static properties:Resources.Settings_Shuffle4}"></Run>
<LineBreak/><LineBreak/>
<Run FontWeight="Bold" Text="{x:Static properties:Resources.Shuffle_Continuous}"></Run><LineBreak/>
<Run Text="{x:Static properties:Resources.Settings_Shuffle5}"></Run>
</TextBlock>
</StackPanel>
</Grid>
</GroupBox>
</DockPanel>
</TabItem>
<TabItem Header="{x:Static properties:Resources.Database}" 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">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="📊"/> <emoji:EmojiInline Text="📊"/>
<Run Text="{x:Static properties:Resources.Stats}"/> <Run Text="{x:Static properties:Resources.Database}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
<Grid VerticalAlignment="Top"> <StackPanel>
<TextBlock>
<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_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_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_Artists}"/><Run Text=" "/><Run x:Name="StatArtist"/><LineBreak/>
@ -215,8 +245,15 @@
<Run Text="{x:Static properties:Resources.Stats_Uptime}"/><Run Text=" "/><Run x:Name="StatUptime"/><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_TotalTimePlayed}"/><Run Text=" "/><Run x:Name="StatTotalTimePlayed"/><LineBreak/>
<Run Text="{x:Static properties:Resources.Stats_LastDatabaseUpdate}"/><Run Text=" "/><Run x:Name="StatDatabaseUpdate"/> <Run Text="{x:Static properties:Resources.Stats_LastDatabaseUpdate}"/><Run Text=" "/><Run x:Name="StatDatabaseUpdate"/>
</TextBlock> </TextBlock>
</Grid> </Grid>
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<Button x:Name="UpdateDatabaseButton" Content="{x:Static properties:Resources.Settings_UpdateDatabase}" Click="MPDDatabaseUpdate_Clicked" MinWidth="120" Padding="3,1,3,1" FocusVisualStyle="{x:Null}"/>
<TextBlock x:Name="UpdateDBMessage" Text="{x:Static properties:Resources.Stats_UpdateDBMessage1}" Margin="15,3,0,0" FontStyle="Italic" Visibility="Collapsed" />
<TextBlock x:Name="UpdateDBMessage2" Text="{x:Static properties:Resources.Stats_UpdateDBMessage2}" Margin="3,3,0,0" FontStyle="Italic" Visibility="Collapsed" />
</StackPanel>
</StackPanel>
</GroupBox> </GroupBox>
</DockPanel> </DockPanel>
</TabItem> </TabItem>

View File

@ -1,4 +1,5 @@
using System.ComponentModel; using System;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -11,6 +12,7 @@ using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Interop; using System.Windows.Interop;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Threading;
using unison.Handlers; using unison.Handlers;
namespace unison namespace unison
@ -37,8 +39,7 @@ namespace unison
} }
} }
HotkeyHandler _hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"]; readonly HotkeyHandler _hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"];
public Settings() public Settings()
{ {
@ -74,11 +75,13 @@ namespace unison
{ {
ConnectionStatus.Text = $"{unison.Resources.Resources.Settings_ConnectionStatusConnected} {mpd.GetVersion()}."; ConnectionStatus.Text = $"{unison.Resources.Resources.Settings_ConnectionStatusConnected} {mpd.GetVersion()}.";
ConnectButton.IsEnabled = false; ConnectButton.IsEnabled = false;
UpdateDatabaseButton.IsEnabled = true;
} }
else else
{ {
ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusOffline; ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusOffline;
ConnectButton.IsEnabled = true; ConnectButton.IsEnabled = true;
UpdateDatabaseButton.IsEnabled = false;
} }
} }
@ -102,7 +105,7 @@ namespace unison
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{ {
ProcessStartInfo psi = new(e.Uri.AbsoluteUri); ProcessStartInfo psi = new ProcessStartInfo(e.Uri.AbsoluteUri);
psi.UseShellExecute = true; psi.UseShellExecute = true;
Process.Start(psi); Process.Start(psi);
e.Handled = true; e.Handled = true;
@ -146,6 +149,37 @@ namespace unison
MPDConnect_Clicked(null, null); MPDConnect_Clicked(null, null);
} }
private void MPDDatabaseUpdate_Clicked(object sender, RoutedEventArgs e)
{
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
if (mpd.IsConnected())
mpd.UpdateDB();
}
private static void TimedText(TextBlock textBlock, int time)
{
DispatcherTimer Timer = new DispatcherTimer();
Timer.Interval = TimeSpan.FromSeconds(time);
Timer.Tick += (sender, args) =>
{
Timer.Stop();
textBlock.Visibility = Visibility.Collapsed;
};
Timer.Start();
}
public void MPDDatabaseUpdate_Start()
{
UpdateDBMessage.Visibility = Visibility.Visible;
}
public void MPDDatabaseUpdate_Stop()
{
UpdateDBMessage2.Visibility = Visibility.Visible;
TimedText(UpdateDBMessage, 2);
TimedText(UpdateDBMessage2, 2);
}
private void CheckUpdates(object sender, RoutedEventArgs e) private void CheckUpdates(object sender, RoutedEventArgs e)
{ {
UpdateHandler updater = (UpdateHandler)Application.Current.Properties["updater"]; UpdateHandler updater = (UpdateHandler)Application.Current.Properties["updater"];
@ -348,7 +382,7 @@ namespace unison
HotkeyChanged(); HotkeyChanged();
} }
private HotkeyHandler.VK GetVirtualKey(Key key) private static HotkeyHandler.VK GetVirtualKey(Key key)
{ {
foreach (object value in System.Enum.GetValues(typeof(HotkeyHandler.VK))) foreach (object value in System.Enum.GetValues(typeof(HotkeyHandler.VK)))
{ {
@ -358,7 +392,7 @@ namespace unison
return HotkeyHandler.VK.None; return HotkeyHandler.VK.None;
} }
private HotkeyHandler.MOD GetMOD(string str) private static HotkeyHandler.MOD GetMOD(string str)
{ {
foreach (object value in System.Enum.GetValues(typeof(HotkeyHandler.MOD))) foreach (object value in System.Enum.GetValues(typeof(HotkeyHandler.MOD)))
{ {
@ -390,12 +424,12 @@ namespace unison
InitializeShortcuts(); InitializeShortcuts();
} }
private uint GetMod(StackPanel stackPanel) private static uint GetMod(StackPanel stackPanel)
{ {
return (uint)(GetMOD(stackPanel.Children.OfType<ComboBox>().First().SelectedItem.ToString()) | GetMOD(stackPanel.Children.OfType<ComboBox>().Last().SelectedItem.ToString())); return (uint)(GetMOD(stackPanel.Children.OfType<ComboBox>().First().SelectedItem.ToString()) | GetMOD(stackPanel.Children.OfType<ComboBox>().Last().SelectedItem.ToString()));
} }
private uint GetVk(StackPanel stackPanel) private static uint GetVk(StackPanel stackPanel)
{ {
Button button = stackPanel.Children.OfType<Button>().First(); Button button = stackPanel.Children.OfType<Button>().First();
TextBlock textBlock = (TextBlock)button.Content; TextBlock textBlock = (TextBlock)button.Content;

119
Views/Shuffle.xaml Normal file
View File

@ -0,0 +1,119 @@
<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:properties="clr-namespace:unison.Resources" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d"
Title="Shuffle" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<Window.Resources>
<x:Array x:Key="FilterType" Type="sys:String">
<x:Static Member="properties:Resources.FilterType_Song"/>
<x:Static Member="properties:Resources.FilterType_Artist"/>
<x:Static Member="properties:Resources.FilterType_Album"/>
<x:Static Member="properties:Resources.FilterType_Year"/>
<x:Static Member="properties:Resources.FilterType_Genre"/>
<x:Static Member="properties:Resources.FilterType_Directory"/>
</x:Array>
<x:Array x:Key="OperatorTypeA" Type="sys:String">
<x:Static Member="properties:Resources.Operator_Contains"/>
<x:Static Member="properties:Resources.Operator_Is"/>
<x:Static Member="properties:Resources.Operator_IsNot"/>
</x:Array>
<x:Array x:Key="OperatorTypeB" Type="sys:String">
<x:Static Member="properties:Resources.Operator_Is"/>
<x:Static Member="properties:Resources.Operator_IsNot"/>
</x:Array>
<x:Array x:Key="OperatorTypeC" Type="sys:String">
<x:Static Member="properties:Resources.Operator_Is"/>
</x:Array>
<DataTemplate x:Key="FilterPanel">
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<ComboBox x:Name="FilterType" SelectionChanged="FilterType_SelectionChanged" ItemsSource="{StaticResource FilterType}" SelectedIndex="0" Width="100" ScrollViewer.CanContentScroll="False" FocusVisualStyle="{x:Null}"/>
<ComboBox x:Name="FilterOperator" SelectionChanged="OperatorType_SelectionChanged" ItemsSource="{StaticResource OperatorTypeA}" SelectedIndex="0" Width="80" ScrollViewer.CanContentScroll="False" Margin="5,0,0,0" FocusVisualStyle="{x:Null}"/>
<ComboBox x:Name="FilterList" SelectedIndex="0" Width="240" Visibility="Collapsed" ScrollViewer.CanContentScroll="False" Margin="5,0,0,0" FocusVisualStyle="{x:Null}"/>
<TextBox x:Name="FilterValue" KeyUp="QueryFilterHandler" Width="240" Margin="5,0,0,0"/>
<Button Content="-" Padding="5, 2" Click="RemoveFilter_Clicked" Width="20" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}" Margin="5,0,0,0"/>
<Button Content="+" Padding="5, 2" Click="AddFilter_Clicked" Width="20" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}" Margin="5,0,0,0"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,5">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="🔡"/>
<Run Text="{x:Static properties:Resources.Shuffle_Filter}"/>
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Vertical" Margin="5,0,5,0">
<StackPanel x:Name="FilterPanel">
<ContentPresenter ContentTemplate="{StaticResource FilterPanel}"/>
</StackPanel>
<StackPanel x:Name="SongFilterPanel" Margin="0,10,0,0">
<TextBlock>
<Run Text="{x:Static properties:Resources.Shuffle_FilterSongNumber}"/><Run Text=" "/><Run x:Name="SongFilterNumber" FontWeight="Bold"/>
</TextBlock>
</StackPanel>
<StackPanel Margin="0,5,0,0">
<StackPanel Orientation="Horizontal" Margin="0,5,0,5">
<Button Content="{x:Static properties:Resources.Shuffle_FilterQuery}" Click="UpdateFilter_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}" Margin="0,0,10,0"/>
<Button Content="{x:Static properties:Resources.Shuffle_FilterReset}" Click="Reset_Clicked" Padding="5, 2" VerticalAlignment="Bottom" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
<TextBlock x:Name="QueryFilterText" Text="{x:Static properties:Resources.Shuffle_Querying1}" Margin="15,3,0,0" FontStyle="Italic" Visibility="Collapsed" />
<TextBlock x:Name="QueryFilterText2" Text="{x:Static properties:Resources.Shuffle_Querying2}" Margin="3,3,0,0" FontStyle="Italic" Visibility="Collapsed" />
</StackPanel>
</StackPanel>
</StackPanel>
</GroupBox>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<GroupBox DockPanel.Dock="Right" Padding="0,4,0,0" Width="300">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text=""/>
<Run Text="{x:Static properties:Resources.Shuffle_Queue}"/>
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Vertical" Margin="5,5,5,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Text="{x:Static properties:Resources.Shuffle_QueueSongs}" Margin="0,0,5,5"/>
<TextBox x:Name="SongNumber" KeyUp="AddToQueueHandler" PreviewTextInput="QueueValidationTextBox" MaxLength="4" Text="15" Width="35" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<Button Content="{x:Static properties:Resources.Shuffle_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="{x:Static properties:Resources.Shuffle_ButtonMessage1}"/><Run Text=" "/><Run x:Name="NumberAddedSongs"/><Run Text=" "/><Run Text="{x:Static properties:Resources.Shuffle_ButtonMessage2}"/>
</TextBlock>
<TextBlock x:Name="SearchStatus2" Text="{x:Static properties:Resources.Shuffle_ButtonMessage3}" Margin="3,3,0,0" FontStyle="Italic" Visibility="Collapsed"/>
</StackPanel>
</StackPanel>
</GroupBox>
<GroupBox DockPanel.Dock="Left" Padding="0,4,0,0" Width="260" Margin="0,0,5,0">
<GroupBox.Header>
<TextBlock>
<emoji:EmojiInline Text="♾️"/>
<Run Text="{x:Static properties:Resources.Shuffle_Continuous}"/>
</TextBlock>
</GroupBox.Header>
<StackPanel Orientation="Horizontal" Margin="5,7,5,0">
<CheckBox x:Name="ContinuousShuffle" Checked="ContinuousShuffle_Checked" Unchecked="ContinuousShuffle_Checked" FocusVisualStyle="{x:Null}" VerticalAlignment="Top">
<TextBlock Text="{x:Static properties:Resources.Shuffle_ContinuousEnable}" TextWrapping="Wrap"/>
</CheckBox>
</StackPanel>
</GroupBox>
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>
</Window>

422
Views/Shuffle.xaml.cs Normal file
View File

@ -0,0 +1,422 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Input;
using System.Windows.Threading;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using MpcNET.Commands.Database;
using MpcNET.Tags;
using MpcNET.Types;
using MpcNET.Types.Filters;
namespace unison
{
public partial class Shuffle : Window
{
private readonly MPDHandler _mpd;
private readonly ShuffleHandler _shuffle;
List<string> GenreList { get; }
List<string> FolderList { get; }
List<IFilter> Filters { get; }
bool _continuous = false;
public Shuffle()
{
InitializeComponent();
GenreList = new();
FolderList = new();
Filters = new();
SongFilterNumber.Text = "0";
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
_shuffle = (ShuffleHandler)Application.Current.Properties["shuffle"];
}
public void Initialize()
{
ListGenre(_mpd._cancelCommand.Token);
ListFolder(_mpd._cancelCommand.Token);
}
private async void ListGenre(CancellationToken token)
{
if (GenreList.Count != 0)
return;
if (token.IsCancellationRequested)
return;
List<string> Response = await _mpd.SafelySendCommandAsync(new ListCommand(MpdTags.Genre, null, null));
if (Response == null)
return;
foreach (string genre in Response)
GenreList.Add(genre);
}
private async void ListFolder(CancellationToken token)
{
if (FolderList.Count != 0)
return;
if (token.IsCancellationRequested)
return;
IEnumerable<IMpdFilePath> Response = await _mpd.SafelySendCommandAsync(new LsInfoCommand(""));
if (Response == null)
return;
foreach (IMpdFilePath folder in Response)
FolderList.Add(folder.Name);
}
private bool IsFilterEmpty()
{
if (Filters.Count == 0)
return true;
return false;
}
private T FindParent<T>(DependencyObject child) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(child);
if (parent == null)
return null;
if (parent is T)
return parent as T;
else
return FindParent<T>(parent);
}
private void AddFilter_Clicked(object sender, RoutedEventArgs e)
{
FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") });
SongFilterNumber.Text = "0";
}
private void RemoveFilter_Clicked(object sender, RoutedEventArgs e)
{
if (FilterPanel.Children.Count > 1)
FilterPanel.Children.Remove(FindParent<ContentPresenter>(sender as Button));
else
Reset_Clicked(null, null);
SongFilterNumber.Text = "0";
}
private void Reset_Clicked(object sender, RoutedEventArgs e)
{
FilterPanel.Children.RemoveRange(0, FilterPanel.Children.Count);
FilterPanel.Children.Add(new ContentPresenter { ContentTemplate = (DataTemplate)FindResource("FilterPanel") });
SongFilterNumber.Text = "0";
_shuffle.SongList.Clear();
}
private static ITag FilterEquivalence_Type(string value)
{
if (value == unison.Resources.Resources.FilterType_Song)
return MpdTags.Title;
else if (value == unison.Resources.Resources.FilterType_Artist)
return MpdTags.Artist;
else if (value == unison.Resources.Resources.FilterType_Album)
return MpdTags.Album;
else if (value == unison.Resources.Resources.FilterType_Year)
return MpdTags.Date;
else if (value == unison.Resources.Resources.FilterType_Genre)
return MpdTags.Genre;
return MpdTags.Title;
}
private static FilterOperator FilterEquivalence_Operator(string value)
{
if (value == unison.Resources.Resources.Operator_Contains)
return FilterOperator.Contains;
else if (value == unison.Resources.Resources.Operator_Is)
return FilterOperator.Equal;
else if (value == unison.Resources.Resources.Operator_IsNot)
return FilterOperator.Different;
return FilterOperator.Equal;
}
private void FilterType_Change(object sender, string Operator, List<string> Listing)
{
ComboBox comboBox = sender as ComboBox;
StackPanel stackPanel = comboBox.Parent as StackPanel;
foreach (ComboBox child in stackPanel.Children.OfType<ComboBox>())
{
if (child.Name == "FilterOperator")
{
child.ItemsSource = (Array)FindResource(Operator);
child.SelectedItem = child.Items[0];
}
if (child.Name == "FilterList")
{
child.Visibility = Visibility.Visible;
child.ItemsSource = Listing;
child.SelectedItem = child.Items[0];
}
}
foreach (TextBox child in stackPanel.Children.OfType<TextBox>())
{
if (child.Name == "FilterValue")
child.Visibility = Visibility.Collapsed;
}
SongFilterNumber.Text = "0";
}
private void FilterType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string item = e.AddedItems[0].ToString();
if (item == unison.Resources.Resources.FilterType_Genre)
FilterType_Change(sender, "OperatorTypeB", GenreList);
else if (item == unison.Resources.Resources.FilterType_Directory)
FilterType_Change(sender, "OperatorTypeC", FolderList);
else
{
ComboBox combobox = sender as ComboBox;
StackPanel stackpanel = combobox.Parent as StackPanel;
foreach (ComboBox child in stackpanel.Children.OfType<ComboBox>())
{
if (child.Name == "FilterOperator")
{
child.ItemsSource = (Array)FindResource("OperatorTypeA");
child.SelectedItem = child.Items[0];
}
if (child.Name == "FilterList")
child.Visibility = Visibility.Collapsed;
}
foreach (TextBox child in stackpanel.Children.OfType<TextBox>())
{
if (child.Name == "FilterValue")
child.Visibility = Visibility.Visible;
}
}
SongFilterNumber.Text = "0";
}
private void OperatorType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SongFilterNumber.Text = "0";
}
private async void UpdateFilter_Clicked(object sender, RoutedEventArgs e)
{
QueryFilterText.Visibility = Visibility.Visible;
await UpdateFilter();
QueryFilterText2.Visibility = Visibility.Visible;
TimedText(QueryFilterText, 1);
TimedText(QueryFilterText2, 1);
}
private static void TimedText(TextBlock textBlock, int time)
{
DispatcherTimer Timer = new DispatcherTimer();
Timer.Interval = TimeSpan.FromSeconds(time);
Timer.Tick += (sender, args) =>
{
Timer.Stop();
textBlock.Visibility = Visibility.Collapsed;
};
Timer.Start();
}
private async Task UpdateFilter()
{
Filters.Clear();
foreach (ContentPresenter superChild in FilterPanel.Children)
{
ITag tag = MpdTags.Title;
FilterOperator op = FilterOperator.None;
string value = "";
bool isDir = false;
StackPanel stackPanel = VisualTreeHelper.GetChild(superChild, 0) as StackPanel;
foreach (TextBox child in stackPanel.Children.OfType<TextBox>())
{
if (child.Name == "FilterValue")
value = child.Text;
}
foreach (ComboBox child in stackPanel.Children.OfType<ComboBox>())
{
if (child.Name == "FilterType")
{
if (child.SelectedItem.ToString() == unison.Resources.Resources.FilterType_Directory)
isDir = true;
else
tag = FilterEquivalence_Type(child.SelectedItem.ToString());
}
if (child.Name == "FilterOperator")
op = FilterEquivalence_Operator(child.SelectedItem.ToString());
if (child.Name == "FilterList" && child.Visibility == Visibility.Visible)
value = child.SelectedItem.ToString();
}
if (value != "")
{
if (!isDir)
Filters.Add(new FilterTag(tag, value, op));
else
Filters.Add(new FilterBase(value, FilterOperator.None));
await Task.Run(async () =>
{
await _shuffle.GetSongsFromFilter(Filters, _mpd._cancelCommand.Token);
});
SongFilterPanel.Visibility = Visibility.Visible;
SongFilterNumber.Text = _shuffle.SongList.Count.ToString();
}
}
}
private void QueryFilterHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
UpdateFilter_Clicked(null, null);
}
private void QueueValidationTextBox(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
private void QueueValidationNumber()
{
int Number;
try
{
Number = int.Parse(SongNumber.Text);
}
catch (Exception)
{
return;
}
if (Number < 1)
SongNumber.Text = "1";
if (IsFilterEmpty())
{
if (Number > 100)
SongNumber.Text = "100";
}
else
{
if (Number > 1000)
SongNumber.Text = "1000";
}
}
private async void AddToQueue()
{
if (_mpd.GetStats() == null)
return;
await UpdateFilter();
QueueValidationNumber();
NumberAddedSongs.Text = SongNumber.Text;
SearchStatus.Visibility = Visibility.Visible;
int Num = int.Parse(SongNumber.Text);
await AddToQueue_Internal(Num);
SearchStatus2.Visibility = Visibility.Visible;
TimedText(SearchStatus, 2);
TimedText(SearchStatus2, 2);
}
private async Task AddToQueue_Internal(int Num)
{
if (IsFilterEmpty())
{
await Task.Run(async () =>
{
await _shuffle.AddToQueueRandom(Num, _mpd._cancelCommand.Token);
});
}
else
{
await Task.Run(async () =>
{
await _shuffle.AddToQueueFilter(Num, _mpd._cancelCommand.Token);
});
}
}
private void AddToQueueHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
AddToQueue();
}
private void AddToQueue_Clicked(object sender, RoutedEventArgs e)
{
AddToQueue();
}
public bool GetContinuous()
{
return _continuous;
}
public async Task HandleContinuous()
{
if (!_continuous)
return;
int PlaylistLength = _mpd.GetStatus().PlaylistLength;
int Num = 50 - PlaylistLength;
if (PlaylistLength > 25)
return;
await UpdateFilter();
await AddToQueue_Internal(Num);
}
private async void ContinuousShuffle_Checked(object sender, RoutedEventArgs e)
{
if (ContinuousShuffle.IsChecked == true)
_continuous = true;
else
_continuous = false;
if (_mpd.GetStatus().PlaylistLength < 10)
await HandleContinuous();
}
private void Window_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
WindowState = WindowState.Minimized;
Hide();
}
public void InitHwnd()
{
WindowInteropHelper helper = new(this);
helper.EnsureHandle();
}
}
}

View File

@ -30,11 +30,11 @@
<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="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="🛠️" />

View File

@ -32,7 +32,7 @@ namespace unison
CanExecuteFunc = () => true CanExecuteFunc = () => true
}; };
public string SnapcastText public static string SnapcastText
{ {
get get
{ {
@ -59,7 +59,7 @@ namespace unison
} }
} }
public ICommand Radios public static ICommand Radios
{ {
get get
{ {
@ -71,7 +71,19 @@ namespace unison
} }
} }
public ICommand Settings public static ICommand Shuffle
{
get
{
return new DelegateCommand
{
CommandAction = () => ((MainWindow)Application.Current.MainWindow).Shuffle_Clicked(null, null),
CanExecuteFunc = () => true
};
}
}
public static ICommand Settings
{ {
get get
{ {

View File

@ -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.3.1</Version> <Version>1.4</Version>
<Company /> <Company />
<Authors>Théo Marchal</Authors> <Authors>Théo Marchal</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
@ -53,7 +53,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.6" /> <PackageReference Include="Autoupdater.NET.Official" Version="1.7.6" />
<PackageReference Include="Emoji.Wpf" Version="0.3.3" /> <PackageReference Include="Emoji.Wpf" Version="0.3.4" />
<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" /> <PackageReference Include="RadioBrowser" Version="0.6.1" />
<PackageReference Include="MpcNET" Version="1.4.0" /> <PackageReference Include="MpcNET" Version="1.4.0" />