19 Commits

Author SHA1 Message Date
f6eb00759a Cleanup 2022-11-13 15:52:27 +01:00
468ec8be07 Cleaning 2022-11-13 15:28:10 +01:00
52b0a6fc85 Bugfix 2022-11-13 14:01:07 +01:00
5a43a1284e Looks like most of shuffle issues are resolved 2022-11-11 17:57:50 +01:00
06207d9791 Merge branch 'main' into shuffle 2022-11-11 01:31:39 +01:00
3b55c79d30 Merge branch 'main' into shuffle 2022-11-03 22:20:37 +01:00
5142477b5e GetPlaylist more cleverly 2022-04-19 01:17:48 +02:00
a6b7ad9c1e Merge branch 'main' into shuffle 2022-04-19 00:23:59 +02:00
1d3515a39d Cleaned things for shuffle, but still has some deadlocks and crashes 2022-04-18 01:02:26 +02:00
792437b839 Update readme and screenshots 2022-04-17 16:04:28 +02:00
5bfa7d3b5b Basic shuffle features, must fix deadlocks 2022-04-17 16:02:09 +02:00
c5e8534af7 Merge branch 'main' into shuffle 2022-04-14 01:08:49 +02:00
5d6e3b6d1e Update MpcNET from 1.3 to 1.4 to use filters 2022-04-14 01:06:33 +02:00
6ad4d9c813 Merge branch 'main' into shuffle 2022-04-07 00:59:00 +02:00
c93a9a326e Better working continuous shuffle 2021-10-07 22:14:09 +02:00
c055c59de7 Handle no response when querying stats 2021-10-07 22:13:18 +02:00
4c71d6a6e0 Rough working shuffle system 2021-10-06 22:54:55 +02:00
3685c369b4 Merge branch 'main' into shuffle 2021-10-05 19:20:00 +02:00
e0d640532c Shuffle window (only visual) 2021-10-05 14:23:23 +02:00
49 changed files with 230 additions and 1454 deletions

View File

@ -1,102 +0,0 @@
<?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>snapcast_0.31</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>10</value>
</setting>
<setting name="MainWindowLeft" serializeAs="String">
<value>10</value>
</setting>
<setting name="RadiosWindowTop" serializeAs="String">
<value>10</value>
</setting>
<setting name="RadiosWindowLeft" serializeAs="String">
<value>1000</value>
</setting>
<setting name="ShuffleWindowTop" serializeAs="String">
<value>10</value>
</setting>
<setting name="ShuffleWindowLeft" serializeAs="String">
<value>330</value>
</setting>
<setting name="SettingsWindowTop" serializeAs="String">
<value>330</value>
</setting>
<setting name="SettingsWindowLeft" serializeAs="String">
<value>600</value>
</setting>
</unison.Properties.Settings>
</userSettings>
</configuration>

View File

@ -15,12 +15,11 @@ namespace unison
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();

View File

@ -1,36 +1,5 @@
# Changelog # Changelog
## v1.5
*Released: 09/06/2025*
* Windows 11 compatibility
* Shuffle button: add "clear queue" and "play queue" buttons
* Hide windows when clicking corresponding buttons, if they were visible
* Fix: add security to QueryStats response to avoid crashes
* Fix: regression in Systray text display
* Fix: clipboard copy crash
* Update .NET (6.0 to 8.0)
* Update Snapcast (0.26.0.1 to 0.31)
* Update MpcNET (1.4.0 to 1.6.6)
* Update RadioBrowser (0.6.1 to 0.7)
* Update AutoUpdater.NET (1.7.6 to 1.9.2)
* Update NotifyIcon.Wpf (1.1.0 to 2.0.1)
## 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

@ -49,12 +49,12 @@ namespace unison
private MpdStatus _currentStatus; private MpdStatus _currentStatus;
private IMpdFile _currentSong; private IMpdFile _currentSong;
private BitmapImage _cover; private BitmapImage _cover;
private readonly Statistics _stats; public Statistics _stats;
private readonly System.Timers.Timer _elapsedTimer; private readonly System.Timers.Timer _elapsedTimer;
private readonly DispatcherTimer _retryTimer; private DispatcherTimer _retryTimer;
private bool _isUpdatingStatus = false; bool _isUpdatingStatus = false;
private bool _isUpdatingSong = false; bool _isUpdatingSong = false;
public IPAddress _ipAddress; public IPAddress _ipAddress;
@ -70,8 +70,6 @@ namespace unison
public 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);
@ -156,7 +154,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; return default(T);
} }
try try
@ -178,7 +176,7 @@ namespace unison
Trace.WriteLine($"Sending {command.GetType().Name} failed: {e.Message}"); Trace.WriteLine($"Sending {command.GetType().Name} failed: {e.Message}");
} }
return default; return default(T);
} }
public async void Startup(object sender, EventArgs e) public async void Startup(object sender, EventArgs e)
@ -340,15 +338,12 @@ namespace unison
{ {
try try
{ {
if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options") || subsystems.Contains("update")) if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options"))
{ {
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)
@ -358,30 +353,6 @@ 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)
@ -436,8 +407,6 @@ 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)
@ -567,13 +536,13 @@ namespace unison
public void Prev() public void Prev()
{ {
if (CanPrevNext && !_isUpdatingSong) if (CanPrevNext)
SendCommand(new PreviousCommand()); SendCommand(new PreviousCommand());
} }
public void Next() public void Next()
{ {
if (CanPrevNext && !_isUpdatingSong) if (CanPrevNext)
SendCommand(new NextCommand()); SendCommand(new NextCommand());
} }
@ -639,36 +608,7 @@ namespace unison
{ {
if (_Playlist == null) if (_Playlist == null)
return 0; return 0;
return _Playlist.ToArray().Length; return _Playlist.ToArray().Count();
}
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()
@ -677,26 +617,20 @@ namespace unison
if (Response == null) if (Response == null)
return; return;
if (Response.ContainsKey("songs")) _stats.Songs = int.Parse(Response["songs"]);
_stats.Songs = int.Parse(Response["songs"]); _stats.Albums = int.Parse(Response["albums"]);
if (Response.ContainsKey("albums")) _stats.Artists = int.Parse(Response["artists"]);
_stats.Albums = int.Parse(Response["albums"]);
if (Response.ContainsKey("artists"))
_stats.Artists = int.Parse(Response["artists"]);
if (Response.ContainsKey("uptime")) TimeSpan time;
_stats.Uptime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["uptime"]))); time = TimeSpan.FromSeconds(int.Parse(Response["uptime"]));
if (Response.ContainsKey("db_playtime")) _stats.Uptime = time.ToString(@"dd\:hh\:mm\:ss");
_stats.TotalPlaytime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["db_playtime"]))); time = TimeSpan.FromSeconds(int.Parse(Response["db_playtime"]));
if (Response.ContainsKey("playtime")) _stats.TotalPlaytime = time.ToString(@"dd\:hh\:mm\:ss");
_stats.TotalTimePlayed = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["playtime"]))); time = TimeSpan.FromSeconds(int.Parse(Response["playtime"]));
_stats.TotalTimePlayed = time.ToString(@"dd\:hh\:mm\:ss");
DateTime date = new DateTime(1970, 1, 1); DateTime date = new DateTime(1970, 1, 1).AddSeconds(int.Parse(Response["db_update"])).ToLocalTime();
if (Response.ContainsKey("db_update")) _stats.DatabaseUpdate = date.ToString("dd/MM/yyyy @ HH:mm");
date = date.AddSeconds(int.Parse(Response["db_update"])).ToLocalTime();
string dayOfWeek = Resources.Resources.Culture.DateTimeFormat.GetDayName(date.DayOfWeek);
_stats.DatabaseUpdate = dayOfWeek + " " + date.ToString("dd/MM/yyyy @ HH:mm");
} }
} }
} }

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
@ -18,8 +17,8 @@ namespace unison
class ShuffleHandler class ShuffleHandler
{ {
private readonly MPDHandler _mpd; private readonly MPDHandler _mpd;
public int AddedSongs = 0; public int AddedSongs = 0;
public List<string> SongList { get; } public List<string> SongList { get; }
public ShuffleHandler() public ShuffleHandler()
@ -35,9 +34,8 @@ namespace unison
SongList.Clear(); SongList.Clear();
int songTotal = _mpd.GetStats().Songs; int song = _mpd.GetStats().Songs;
IEnumerable<IMpdFile> response = await _mpd.SafelySendCommandAsync(new SearchCommand(filter, 0, song + 1));
IEnumerable<IMpdFile> response = await _mpd.SafelySendCommandAsync(new SearchCommand(filter, 0, songTotal + 1));
foreach (IMpdFile file in response) foreach (IMpdFile file in response)
SongList.Add(file.Path); SongList.Add(file.Path);
} }
@ -47,7 +45,7 @@ namespace unison
if (token.IsCancellationRequested) if (token.IsCancellationRequested)
return; return;
int addedSongs = 0; int AddedSongs = 0;
var commandList = new CommandList(); var commandList = new CommandList();
int songTotal = _mpd.GetStats().Songs; int songTotal = _mpd.GetStats().Songs;
@ -56,12 +54,12 @@ namespace unison
{ {
int song = new Random().Next(0, songTotal - 1); int song = new Random().Next(0, songTotal - 1);
commandList.Add(new SearchAddCommand(new FilterTag(MpdTags.Title, "", FilterOperator.Contains), song, song + 1)); commandList.Add(new SearchAddCommand(new FilterTag(MpdTags.Title, "", FilterOperator.Contains), song, song + 1));
addedSongs++; AddedSongs++;
}
// play if stopped or unknown state (no queue managing at the moment, so mandatory) // play if stopped or unknown state (no queue managing at the moment, so mandatory)
if ((_mpd.GetStatus().State != MpdState.Play && _mpd.GetStatus().State != MpdState.Pause)) if (i == 0 && (_mpd.GetStatus().State != MpdState.Play && _mpd.GetStatus().State != MpdState.Pause))
commandList.Add(new PlayCommand(0)); commandList.Add(new PlayCommand(0));
}
await _mpd.SafelySendCommandAsync(commandList); await _mpd.SafelySendCommandAsync(commandList);
} }
@ -83,7 +81,6 @@ namespace unison
AddedSongs++; AddedSongs++;
} }
commandList.Add(new PlayCommand(0));
await _mpd.SafelySendCommandAsync(commandList); await _mpd.SafelySendCommandAsync(commandList);
} }
// more available songs than requested => // more available songs than requested =>
@ -104,7 +101,6 @@ namespace unison
AddedSongs++; AddedSongs++;
} }
commandList.Add(new PlayCommand(0));
await _mpd.SafelySendCommandAsync(commandList); await _mpd.SafelySendCommandAsync(commandList);
} }
} }

View File

@ -8,7 +8,6 @@ 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)
@ -31,7 +30,7 @@ namespace unison
}); });
} }
public static void UpdateInterface() public 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,4 +1,5 @@
using System.Windows; using System.Diagnostics;
using System.Windows;
using AutoUpdaterDotNET; using AutoUpdaterDotNET;
namespace unison.Handlers namespace unison.Handlers
@ -8,10 +9,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;
private bool _RequestedCheck = false;
public bool UpdateAvailable() => _UpdateAvailable; public bool UpdateAvailable() => _UpdateAvailable;
private bool _RequestedCheck = false;
public UpdateHandler() public UpdateHandler()
{ {
AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent; AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
@ -24,7 +25,7 @@ namespace unison.Handlers
AutoUpdater.Start(xmlFile); AutoUpdater.Start(xmlFile);
} }
private static string CutVersionNumber(string number) private string CutVersionNumber(string number)
{ {
return number.Substring(0, number.LastIndexOf(".")); return number.Substring(0, number.LastIndexOf("."));
} }

View File

@ -1,6 +1,6 @@
#define Name "unison" #define Name "unison"
#define Version "1.5" #define Version "1.3.1"
#define Snapcast "snapcast_0.31" #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"
#define ExeName "unison.exe" #define ExeName "unison.exe"

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<item> <item>
<version>1.5.0.0</version> <version>1.3.1.0</version>
<url>https://github.com/ZetaKebab/unison/releases/download/v1.5/unison-v1.5-setup.exe</url> <url>https://github.com/ZetaKebab/unison/releases/download/v1.3.1/unison-v1.3.1.zip</url>
<changelog>https://raw.githubusercontent.com/ZetaKebab/unison/main/CHANGELOG.md</changelog> <changelog>https://raw.githubusercontent.com/ZetaKebab/unison/main/CHANGELOG.md</changelog>
<mandatory>false</mandatory> <mandatory>false</mandatory>
</item> </item>

View File

@ -1,4 +1,4 @@
MIT License Copyright (c) 2025 Théo Marchal MIT License Copyright (c) 2022 Théo Marchal
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -8,7 +8,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<Platform>Any CPU</Platform> <Platform>Any CPU</Platform>
<PublishDir>publish\</PublishDir> <PublishDir>publish\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol> <PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<SelfContained>false</SelfContained> <SelfContained>false</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile> <PublishSingleFile>true</PublishSingleFile>

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", "17.13.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.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())));
@ -85,7 +85,7 @@ namespace unison.Properties {
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("snapcast_0.31")] [global::System.Configuration.DefaultSettingValueAttribute("snapclient_0.26.0-1_win64")]
public string snapcast_path { public string snapcast_path {
get { get {
return ((string)(this["snapcast_path"])); return ((string)(this["snapcast_path"]));
@ -286,101 +286,5 @@ namespace unison.Properties {
this["showWindow_vk"] = value; this["showWindow_vk"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("10")]
public double MainWindowTop {
get {
return ((double)(this["MainWindowTop"]));
}
set {
this["MainWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("10")]
public double MainWindowLeft {
get {
return ((double)(this["MainWindowLeft"]));
}
set {
this["MainWindowLeft"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("10")]
public double RadiosWindowTop {
get {
return ((double)(this["RadiosWindowTop"]));
}
set {
this["RadiosWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1000")]
public double RadiosWindowLeft {
get {
return ((double)(this["RadiosWindowLeft"]));
}
set {
this["RadiosWindowLeft"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("10")]
public double ShuffleWindowTop {
get {
return ((double)(this["ShuffleWindowTop"]));
}
set {
this["ShuffleWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("330")]
public double ShuffleWindowLeft {
get {
return ((double)(this["ShuffleWindowLeft"]));
}
set {
this["ShuffleWindowLeft"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("330")]
public double SettingsWindowTop {
get {
return ((double)(this["SettingsWindowTop"]));
}
set {
this["SettingsWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("600")]
public double SettingsWindowLeft {
get {
return ((double)(this["SettingsWindowLeft"]));
}
set {
this["SettingsWindowLeft"] = value;
}
}
} }
} }

View File

@ -18,7 +18,7 @@
<Value Profile="(Default)">False</Value> <Value Profile="(Default)">False</Value>
</Setting> </Setting>
<Setting Name="snapcast_path" Type="System.String" Scope="User"> <Setting Name="snapcast_path" Type="System.String" Scope="User">
<Value Profile="(Default)">snapcast_0.31</Value> <Value Profile="(Default)">snapclient_0.26.0-1_win64</Value>
</Setting> </Setting>
<Setting Name="snapcast_port" Type="System.Int32" Scope="User"> <Setting Name="snapcast_port" Type="System.Int32" Scope="User">
<Value Profile="(Default)">1704</Value> <Value Profile="(Default)">1704</Value>
@ -68,29 +68,5 @@
<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)">10</Value>
</Setting>
<Setting Name="MainWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="RadiosWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="RadiosWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">1000</Value>
</Setting>
<Setting Name="ShuffleWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="ShuffleWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">330</Value>
</Setting>
<Setting Name="SettingsWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">330</Value>
</Setting>
<Setting Name="SettingsWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">600</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -1,14 +1,13 @@
# unison # unison
![Main window](Screenshots/screen1.png) ![Main window](Screenshots/screen-mainwindow.png)
**unison** is a very simple [Music Player Daemon (MPD)](https://www.musicpd.org/) daemon client with the following goals: **unison** is a very simple [Music Player Daemon (MPD)](https://www.musicpd.org/) daemon client with the following goals:
* lightweight window that can be toggled with shortcuts * lightweight window that can be toggled with shortcuts
* music control through rebindable shortcuts * music control through rebindable shortcuts
* [Snapcast](https://github.com/badaix/snapcast) integration * [Snapcast](https://github.com/badaix/snapcast) integration
* radio stations * Radio stations
* basic shuffle system
## Features ## Features
@ -16,13 +15,25 @@
By default, unison works as a daemon in the taskbar system tray. You can display the main window when needed at any time with a shortcut. By default, unison works as a daemon in the taskbar system tray. You can display the main window when needed at any time with a shortcut.
![Systray](Screenshots/screen2.png) ![Systray](Screenshots/screen-systray.png)
### Shortcuts ### Shortcuts
You can control your music at anytime with the shortcuts. They are usable system-wide, even if the window is not visible. They are of course fully rebindable. You can control your music at anytime with the shortcuts. They are usable system-wide, even if the window is not visible. They are of course fully rebindable.
![Settings => shortcuts](Screenshots/screen3.png) ![Settings => shortcuts](Screenshots/screen-shortcuts.png)
### Shuffle panel
One of unison's main feature is a complete shuffle system based on criterias, aka a smart playlist.
You have two options:
* **Add to queue** allows you to add a defined number of songs to the queue.
* **Continuous shuffle** allows you, as long as the program is running, to automatically add songs to the queue.
Each of these options work with filters, but if none are selected, it is based on the entire library.
![Shuffle panel](Screenshots/screen-shuffle.png)
### Snapcast ### Snapcast
@ -32,22 +43,14 @@ Embedding a Snapcast client allows to listen to music on multiple devices. For e
Through [Radio-Browser](https://www.radio-browser.info), a community database, you can play radio streams directly from unison. There are more than 28,000 stations recorded on this service, so it is a nice way to discover new music and cultures. Through [Radio-Browser](https://www.radio-browser.info), a community database, you can play radio streams directly from unison. There are more than 28,000 stations recorded on this service, so it is a nice way to discover new music and cultures.
![Radio stations](Screenshots/screen4.png) ![Radio stations](Screenshots/screen-radio.png)
### Shuffle system ## Planned features
A shuffle system is available, allowing you to only use unison as a (very basic) music manager. You can use filters to have a tailored shuffle experience. * Playlist, queue and library management
* More options for the shuffle panel
![Shuffle system](Screenshots/screen5.png) * Dark mode
## Translations ## Translations
unison is translated in English, French and Spanish. You can contribute if you want! unison is translated in English, French and Spanish. You can contribute if you want!
## The future
This project started as a simple daemon to control my music with shortcuts, and a Snapcast player. With time, I added a few useful features, while learning C# and WPF. I wanted to make a complete implementation of MPD features (library, playlist, queue management, etc). However, this will probably not happen. Indeed, I have been waiting a few years for Microsoft to settle on a modern implementation of its user interface framework. But it is still not there, and I do not want to spend time for a technology that might die too quickly. There's no coherence in Windows' interfaces, and it is not really motivating. I also want to be able to play Snapcast from iOS, so I think I will most likely invest (natively) my time in this platform for the foreseeable future.
If I were to continue, the first thing to do would be to make a single window interface, by making a real MVVP implementation, and not one depending on windows like I did here, by iterating on a single program that has become a bit too big to not separate logic from visual.
I still plan on maintaining this app in a working state, as its working great as is. But I do not intend, for the time being at least, to make dramatic changes as I would have liked to.

View File

@ -60,42 +60,6 @@ namespace unison.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Clear queue.
/// </summary>
public static string Clear_Queue {
get {
return ResourceManager.GetString("Clear_Queue", resourceCulture);
}
}
/// <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>
@ -105,141 +69,6 @@ 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>
/// Looks up a localized string similar to Play queue.
/// </summary>
public static string Play_Queue {
get {
return ResourceManager.GetString("Play_Queue", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Queue management.
/// </summary>
public static string Queue_Management {
get {
return ResourceManager.GetString("Queue_Management", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Country. /// Looks up a localized string similar to Country.
/// </summary> /// </summary>
@ -321,24 +150,6 @@ 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>
@ -537,51 +348,6 @@ 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>
@ -672,15 +438,6 @@ 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>
@ -735,132 +492,6 @@ 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>
@ -906,6 +537,15 @@ 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>
@ -960,24 +600,6 @@ 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,66 +117,9 @@
<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="Clear_Queue" xml:space="preserve">
<value>Borrar cola</value>
</data>
<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="Play_Queue" xml:space="preserve">
<value>Reproducir cola</value>
</data>
<data name="Queue_Management" xml:space="preserve">
<value>Gestión de cola</value>
</data>
<data name="Radios" xml:space="preserve"> <data name="Radios" xml:space="preserve">
<value>Radios</value> <value>Radios</value>
</data> </data>
@ -204,12 +147,6 @@
<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>
@ -276,21 +213,6 @@
<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>
@ -321,9 +243,6 @@
<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>
@ -342,48 +261,6 @@
<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>
@ -399,6 +276,9 @@
<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>
@ -417,12 +297,6 @@
<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,66 +117,9 @@
<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="Clear_Queue" xml:space="preserve">
<value>Effacer la file</value>
</data>
<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="Play_Queue" xml:space="preserve">
<value>Jouer la file</value>
</data>
<data name="Queue_Management" xml:space="preserve">
<value>Gestion de file</value>
</data>
<data name="Radios" xml:space="preserve"> <data name="Radios" xml:space="preserve">
<value>Radios</value> <value>Radios</value>
</data> </data>
@ -204,12 +147,6 @@
<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>
@ -276,21 +213,6 @@
<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>
@ -321,9 +243,6 @@
<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>
@ -342,48 +261,6 @@
<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>
@ -399,6 +276,9 @@
<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>
@ -417,12 +297,6 @@
<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,66 +117,9 @@
<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="Clear_Queue" xml:space="preserve">
<value>Clear queue</value>
</data>
<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="Play_Queue" xml:space="preserve">
<value>Play queue</value>
</data>
<data name="Queue_Management" xml:space="preserve">
<value>Queue management</value>
</data>
<data name="Radios" xml:space="preserve"> <data name="Radios" xml:space="preserve">
<value>Radios</value> <value>Radios</value>
</data> </data>
@ -204,12 +147,6 @@
<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>
@ -276,21 +213,6 @@
<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>
@ -321,9 +243,6 @@
<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>
@ -342,48 +261,6 @@
<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>
@ -399,6 +276,9 @@
<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>
@ -417,12 +297,6 @@
<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

@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=System.Runtime"> xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=System.Runtime">
<system:String x:Key="snapcastPath">snapcast_0.31</system:String> <system:String x:Key="snapcastPath">snapclient_0.26.0-1_win64</system:String>
<system:String x:Key="snapcastPort">1704</system:String> <system:String x:Key="snapcastPort">1704</system:String>
<system:String x:Key="playButton">&#xedb4;</system:String> <system:String x:Key="playButton">&#xedb4;</system:String>

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

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" LocationChanged="Window_LocationChanged" Icon="/Resources/icon-full.ico" ResizeMode="CanMinimize" SizeToContent="WidthAndHeight"> Title="unison" Closing="Window_Closing" 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="700" Margin="0,10,0,0"> <Grid Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}" MinHeight="270" MinWidth="650">
<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">
@ -114,14 +114,14 @@
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,0"> <StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,0">
<Button x:Name="Shuffle" Padding="5, 2" Click="Shuffle_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" Margin="0,0,10,0"> <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">
<TextBlock Text="🔁" Margin="0,0,0,2"/> <emoji:TextBlock Text="🔁" Padding="0,0,0,2"/>
<TextBlock Text="{x:Static properties:Resources.Shuffle}" Margin="5, 0, 0, 0"/> <TextBlock Text="Shuffle" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button x:Name="Radio" Padding="5, 2" HorizontalAlignment="Left" Click="Radios_Clicked" 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">
<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"/>
</StackPanel> </StackPanel>
</Button> </Button>
@ -139,14 +139,14 @@
<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="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"> <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">
<TextBlock Text="🔊" Padding="0,0,0,2"/> <emoji:TextBlock Text="🔊" Padding="0,0,0,2"/>
<TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/> <TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button x:Name="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">
<TextBlock Text="🛠️" Padding="0,0,0,2"/> <emoji:TextBlock Text="🛠️" Padding="0,0,0,2"/>
<TextBlock Text="{x:Static properties:Resources.Settings}" Margin="5, 0, 0, 0"/> <TextBlock Text="{x:Static properties:Resources.Settings}" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>

View File

@ -7,8 +7,6 @@ using System.Windows.Interop;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Diagnostics; using System.Diagnostics;
using System.Data;
using MpcNET.Commands.Playback;
namespace unison namespace unison
{ {
@ -17,11 +15,6 @@ namespace unison
private readonly Settings _settingsWin; private readonly Settings _settingsWin;
private readonly Radios _radiosWin; private readonly Radios _radiosWin;
private readonly Shuffle _shuffleWin; private readonly Shuffle _shuffleWin;
public Settings GetSettings() => _settingsWin;
public Radios GetRadios() => _radiosWin;
public Shuffle GetShuffle() => _shuffleWin;
private readonly DispatcherTimer _timer; private readonly DispatcherTimer _timer;
private readonly MPDHandler _mpd; private readonly MPDHandler _mpd;
@ -30,12 +23,8 @@ namespace unison
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(); _shuffleWin = new Shuffle();
@ -56,17 +45,12 @@ 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(); _mpd.QueryStats();
_settingsWin.UpdateStats();
ConnectionOkIcon.Visibility = Visibility.Visible; ConnectionOkIcon.Visibility = Visibility.Visible;
ConnectionFailIcon.Visibility = Visibility.Collapsed; ConnectionFailIcon.Visibility = Visibility.Collapsed;
@ -142,10 +126,6 @@ namespace unison
if (_shuffleWin.GetContinuous()) 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; _mpd.CanPrevNext = false;
await _shuffleWin.HandleContinuous(); await _shuffleWin.HandleContinuous();
_mpd.CanPrevNext = true; _mpd.CanPrevNext = true;
@ -236,12 +216,12 @@ namespace unison
border.Style = b ? (Style)Resources["SelectedButton"] : (Style)Resources["UnselectedButton"]; border.Style = b ? (Style)Resources["SelectedButton"] : (Style)Resources["UnselectedButton"];
} }
public static string FormatSeconds(int time) public 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 static string FormatSeconds(double time) public string FormatSeconds(double time)
{ {
TimeSpan timespan = TimeSpan.FromSeconds(time); TimeSpan timespan = TimeSpan.FromSeconds(time);
return timespan.ToString(@"mm\:ss"); return timespan.ToString(@"mm\:ss");
@ -264,47 +244,29 @@ namespace unison
public void Radios_Clicked(object sender, RoutedEventArgs e) public void Radios_Clicked(object sender, RoutedEventArgs e)
{ {
_radiosWin.Show();
_radiosWin.Activate();
if (_radiosWin.WindowState == WindowState.Minimized) if (_radiosWin.WindowState == WindowState.Minimized)
{
_radiosWin.Show();
_radiosWin.Activate();
_radiosWin.WindowState = WindowState.Normal; _radiosWin.WindowState = WindowState.Normal;
}
else if (_radiosWin.WindowState == WindowState.Normal)
{
_radiosWin.Hide();
_radiosWin.WindowState = WindowState.Minimized;
}
} }
public void Shuffle_Clicked(object sender, RoutedEventArgs e) public void Shuffle_Clicked(object sender, RoutedEventArgs e)
{ {
_shuffleWin.Show();
_shuffleWin.Activate();
if (_shuffleWin.WindowState == WindowState.Minimized) if (_shuffleWin.WindowState == WindowState.Minimized)
{
_shuffleWin.Show();
_shuffleWin.Activate();
_shuffleWin.WindowState = WindowState.Normal; _shuffleWin.WindowState = WindowState.Normal;
}
else if (_shuffleWin.WindowState == WindowState.Normal)
{
_shuffleWin.Hide();
_shuffleWin.WindowState = WindowState.Minimized;
}
} }
public void Settings_Clicked(object sender, RoutedEventArgs e) public void Settings_Clicked(object sender, RoutedEventArgs e)
{ {
_settingsWin.Show();
_settingsWin.Activate();
if (_settingsWin.WindowState == WindowState.Minimized) if (_settingsWin.WindowState == WindowState.Minimized)
{
_settingsWin.Show();
_settingsWin.Activate();
_settingsWin.WindowState = WindowState.Normal; _settingsWin.WindowState = WindowState.Normal;
}
else if (_settingsWin.WindowState == WindowState.Normal)
{
_settingsWin.Hide();
_settingsWin.WindowState = WindowState.Minimized;
}
} }
private void TimeSlider_DragStarted(object sender, DragStartedEventArgs e) private void TimeSlider_DragStarted(object sender, DragStartedEventArgs e)
@ -350,7 +312,7 @@ namespace unison
string CopyText = SongTitle.Text + " - " + SongArtist.Text + "\n"; string CopyText = SongTitle.Text + " - " + SongArtist.Text + "\n";
CopyText += SongAlbum.Text + "\n"; CopyText += SongAlbum.Text + "\n";
CopyText += SongTitle.ToolTip; CopyText += SongTitle.ToolTip;
Clipboard.SetDataObject(CopyText); Clipboard.SetText(CopyText);
} }
} }
@ -366,12 +328,5 @@ 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

@ -6,15 +6,15 @@
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="Radios" Closing="Window_Closing" LocationChanged="Window_LocationChanged" SizeToContent="WidthAndHeight" ResizeMode="NoResize"> Title="Radios" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<Grid Margin="0,5,0,0"> <Grid>
<StackPanel> <StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,0"> <StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,0">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="📻"/> <emoji:EmojiInline Text="📻"/>
<Run Text="{x:Static properties:Resources.Radio_SearchStation}"/> <Run Text="{x:Static properties:Resources.Radio_SearchStation}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>

View File

@ -15,7 +15,7 @@ namespace unison
public partial class Radios : Window public partial class Radios : Window
{ {
private MPDHandler _mpd; private MPDHandler _mpd;
private RadioHandler _radio; RadioHandler _radio;
public bool IsConnected() => _radio.IsConnected(); public bool IsConnected() => _radio.IsConnected();
@ -23,12 +23,6 @@ namespace unison
{ {
InitializeComponent(); InitializeComponent();
Initialize(); Initialize();
InitHwnd();
WindowState = WindowState.Minimized;
Top = Properties.Settings.Default.RadiosWindowTop;
Left = Properties.Settings.Default.RadiosWindowLeft;
} }
public async void Initialize() public async void Initialize()
@ -175,12 +169,5 @@ namespace unison
WindowInteropHelper helper = new(this); WindowInteropHelper helper = new(this);
helper.EnsureHandle(); helper.EnsureHandle();
} }
private void Window_LocationChanged(object sender, EventArgs e)
{
Properties.Settings.Default.RadiosWindowTop = Top;
Properties.Settings.Default.RadiosWindowLeft = Left;
Properties.Settings.Default.Save();
}
} }
} }

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:sys="clr-namespace:System;assembly=System.Runtime" xmlns:properties="clr-namespace:unison.Resources" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" mc:Ignorable="d"
Title="{x:Static properties:Resources.Settings}" Closing="Window_Closing" LocationChanged="Window_LocationChanged" ResizeMode="CanMinimize" Icon="/Resources/icon-full.ico" WindowStyle="ToolWindow" SizeToContent="WidthAndHeight"> Closing="Window_Closing" Title="{x:Static properties:Resources.Settings}" ResizeMode="CanMinimize" Icon="/Resources/icon-full.ico" WindowStyle="ToolWindow" SizeToContent="WidthAndHeight">
<Window.Resources> <Window.Resources>
<x:Array x:Key="ShortcutItems" Type="sys:String"> <x:Array x:Key="ShortcutItems" Type="sys:String">
@ -17,7 +17,7 @@
</x:Array> </x:Array>
</Window.Resources> </Window.Resources>
<Grid MinWidth="390"> <Grid>
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<TabControl Margin="10"> <TabControl Margin="10">
<TabItem Header="MPD"> <TabItem Header="MPD">
@ -25,7 +25,7 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="📶"/> <emoji:EmojiInline Text="📶"/>
<Run Text="{x:Static properties:Resources.Settings_Connection}"/> <Run Text="{x:Static properties:Resources.Settings_Connection}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
@ -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" Margin="10,2,0,0"/> <TextBox x:Name="MpdHost" KeyDown="ConnectHandler" TextChanged="MpdConnectTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
</StackPanel> </StackPanel>
<StackPanel Margin="0,5,0,0"> <StackPanel Margin="0,5,0,0">
<TextBlock Text="{x:Static properties:Resources.Settings_Port}" TextWrapping="Wrap" Margin="5,0,0,0"/> <TextBlock Text="{x:Static properties:Resources.Settings_Port}" TextWrapping="Wrap" Margin="5,0,0,0"/>
<TextBox x:Name="MpdPort" KeyDown="ConnectHandler" PreviewTextInput="NumberValidationTextBox" MaxLength="5" TextWrapping="Wrap" Margin="10,2,0,0"/> <TextBox x:Name="MpdPort" KeyDown="ConnectHandler" PreviewTextInput="NumberValidationTextBox" MaxLength="5" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
</StackPanel> </StackPanel>
<StackPanel Margin="0,5,0,0"> <StackPanel Margin="0,5,0,0">
<TextBlock Text="{x:Static properties:Resources.Settings_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" Margin="10,2,0,0"/> <PasswordBox x:Name="MpdPassword" KeyDown="ConnectHandler" Width="250" Margin="10,2,0,0"/>
<TextBlock Text="{x:Static properties:Resources.Settings_ConnectionPasswordInfo}" TextWrapping="Wrap" Margin="10,5,0,0" MaxWidth="390"/> <TextBlock Text="{x:Static properties:Resources.Settings_ConnectionPasswordInfo}" TextWrapping="Wrap" Margin="10,5,0,0" MaxWidth="250" HorizontalAlignment="Left"/>
</StackPanel> </StackPanel>
<TextBlock TextWrapping="Wrap" Margin="5,10,0,0"> <TextBlock TextWrapping="Wrap" Margin="5,10,0,0">
@ -52,7 +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> </StackPanel>
</Grid> </Grid>
</GroupBox> </GroupBox>
@ -64,7 +64,7 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="⌨️"/> <emoji:EmojiInline Text="⌨️ "/>
<Run Text="{x:Static properties:Resources.Settings_Shortcuts}"></Run> <Run Text="{x:Static properties:Resources.Settings_Shortcuts}"></Run>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
@ -170,7 +170,7 @@
<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>
<Run Text="🔊 Snapcast"/> <emoji:TextBlock Text="🔊 Snapcast"/>
</GroupBox.Header> </GroupBox.Header>
<Grid VerticalAlignment="Top"> <Grid VerticalAlignment="Top">
<StackPanel> <StackPanel>
@ -182,13 +182,13 @@
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastWindow}" TextWrapping="Wrap"/> <TextBlock Text="{x:Static properties:Resources.Settings_SnapcastWindow}" TextWrapping="Wrap"/>
</CheckBox> </CheckBox>
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastPort}" TextWrapping="Wrap" Margin="5,5,0,0"/> <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"/> <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"/> <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"/> <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="390"> <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}" /> <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> </TextBlock>
<Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Width="120" Click="SnapcastReset_Clicked" Margin="0,5,0,0" BorderThickness="1,1,1,1"/> <Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,10,0,0" Width="120" Click="SnapcastReset_Clicked"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
@ -196,28 +196,27 @@
</DockPanel> </DockPanel>
</TabItem> </TabItem>
<TabItem Header="{x:Static properties:Resources.Shuffle}"> <TabItem Header="Shuffle">
<DockPanel Margin="8"> <DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="🔁"/> <emoji:EmojiInline Text="🔁 "/>
<Run Text="{x:Static properties:Resources.Shuffle}"/> <Run Text="Shuffle"></Run>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
<Grid MaxWidth="500"> <Grid MaxWidth="500">
<StackPanel> <StackPanel>
<TextBlock TextWrapping="Wrap"> <TextBlock TextWrapping="Wrap">
<Run Text="{x:Static properties:Resources.Settings_Shuffle1}"></Run> <Run>The shuffle window allows to add random songs to your queue. Both options take into account the filter.</Run>
<Run Text="{x:Static properties:Resources.Settings_Shuffle2}"></Run><LineBreak/> <Run>If the filter is empty, the entire music library is taken into account.</Run><LineBreak/><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 FontWeight="Bold">Add to queue</Run><LineBreak/>
<Run Text="{x:Static properties:Resources.Settings_Shuffle4}"></Run> <Run>Add a fixed number of songs to the queue. It can take a long time to add more than 100 songs, so the option is limited to 1000 songs.</Run>
<LineBreak/><LineBreak/> <LineBreak/><LineBreak/>
<Run FontWeight="Bold" Text="{x:Static properties:Resources.Shuffle_Continuous}"></Run><LineBreak/> <Run FontWeight="Bold">Continuous shuffle</Run><LineBreak/>
<Run Text="{x:Static properties:Resources.Settings_Shuffle5}"></Run> <Run>By enabling this option, unison will automatically add songs to the queue so you never run out of songs to listen to.</Run>
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
</Grid> </Grid>
@ -225,19 +224,17 @@
</DockPanel> </DockPanel>
</TabItem> </TabItem>
<TabItem Header="{x:Static properties:Resources.Database}" Height="20" VerticalAlignment="Bottom"> <TabItem Header="{x:Static properties:Resources.Stats}">
<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>
<Run Text="📊"/> <emoji:EmojiInline Text="📊"/>
<Run Text="{x:Static properties:Resources.Database}"/> <Run Text="{x:Static properties:Resources.Stats}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
<StackPanel> <Grid VerticalAlignment="Top">
<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/>
@ -245,15 +242,8 @@
<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>
@ -262,7 +252,7 @@
<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>
<Run Text="🎶 unison"/> <emoji:TextBlock Text="🎶 unison"/>
</GroupBox.Header> </GroupBox.Header>
<Grid VerticalAlignment="Top"> <Grid VerticalAlignment="Top">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
@ -281,7 +271,7 @@
※ <Hyperlink NavigateUri="https://github.com/Difegue/MpcNET" RequestNavigate="Hyperlink_RequestNavigate">MpcNET</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/Difegue/MpcNET" RequestNavigate="Hyperlink_RequestNavigate">MpcNET</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://git.sr.ht/~youkai/RadioBrowser.NET" RequestNavigate="Hyperlink_RequestNavigate">RadioBrowser</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/tof4/RadioBrowser" RequestNavigate="Hyperlink_RequestNavigate">RadioBrowser</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/ravibpatel/AutoUpdater.NET" RequestNavigate="Hyperlink_RequestNavigate">AutoUpdater.NET</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/ravibpatel/AutoUpdater.NET" RequestNavigate="Hyperlink_RequestNavigate">AutoUpdater.NET</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/badaix/snapcast" RequestNavigate="Hyperlink_RequestNavigate">Snapcast</Hyperlink> ※ <Hyperlink NavigateUri="https://github.com/badaix/snapcast" RequestNavigate="Hyperlink_RequestNavigate">Snapcast</Hyperlink>
</TextBlock> </TextBlock>
@ -308,7 +298,7 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0" Margin="0,10,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0" Margin="0,10,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="📝"/> <emoji:EmojiInline Text="📝 "/>
<Run Text="{x:Static properties:Resources.Settings_License}" /> <Run Text="{x:Static properties:Resources.Settings_License}" />
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>

View File

@ -1,5 +1,4 @@
using System; using System.ComponentModel;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -12,7 +11,6 @@ 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
@ -39,19 +37,18 @@ namespace unison
} }
} }
readonly HotkeyHandler _hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"]; HotkeyHandler _hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"];
public Settings() public Settings()
{ {
InitHwnd(); InitHwnd();
InitializeComponent(); InitializeComponent();
Initialize();
DataContext = this; DataContext = this;
WindowState = WindowState.Minimized; WindowState = WindowState.Minimized;
Top = Properties.Settings.Default.SettingsWindowTop; Initialize();
Left = Properties.Settings.Default.SettingsWindowLeft;
} }
void Initialize() void Initialize()
@ -77,13 +74,11 @@ 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;
} }
} }
@ -107,7 +102,7 @@ namespace unison
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{ {
ProcessStartInfo psi = new ProcessStartInfo(e.Uri.AbsoluteUri); ProcessStartInfo psi = new(e.Uri.AbsoluteUri);
psi.UseShellExecute = true; psi.UseShellExecute = true;
Process.Start(psi); Process.Start(psi);
e.Handled = true; e.Handled = true;
@ -151,37 +146,6 @@ 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"];
@ -209,13 +173,6 @@ namespace unison
helper.EnsureHandle(); helper.EnsureHandle();
} }
private void Window_LocationChanged(object sender, EventArgs e)
{
Properties.Settings.Default.SettingsWindowTop = Top;
Properties.Settings.Default.SettingsWindowLeft = Left;
Properties.Settings.Default.Save();
}
public void SaveSettings() public void SaveSettings()
{ {
Properties.Settings.Default.mpd_host = MpdHost.Text; Properties.Settings.Default.mpd_host = MpdHost.Text;
@ -391,7 +348,7 @@ namespace unison
HotkeyChanged(); HotkeyChanged();
} }
private static HotkeyHandler.VK GetVirtualKey(Key key) private 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)))
{ {
@ -401,7 +358,7 @@ namespace unison
return HotkeyHandler.VK.None; return HotkeyHandler.VK.None;
} }
private static HotkeyHandler.MOD GetMOD(string str) private 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)))
{ {
@ -433,12 +390,12 @@ namespace unison
InitializeShortcuts(); InitializeShortcuts();
} }
private static uint GetMod(StackPanel stackPanel) private 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 static uint GetVk(StackPanel stackPanel) private 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;

View File

@ -4,30 +4,30 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
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:sys="clr-namespace:System;assembly=System.Runtime" xmlns:local="clr-namespace:unison" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" mc:Ignorable="d"
Title="Shuffle" Closing="Window_Closing" LocationChanged="Window_LocationChanged" SizeToContent="WidthAndHeight" ResizeMode="NoResize"> Title="Shuffle" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<Window.Resources> <Window.Resources>
<x:Array x:Key="FilterType" Type="sys:String"> <x:Array x:Key="FilterType" Type="sys:String">
<x:Static Member="properties:Resources.FilterType_Song"/> <sys:String>Song</sys:String>
<x:Static Member="properties:Resources.FilterType_Artist"/> <sys:String>Artist</sys:String>
<x:Static Member="properties:Resources.FilterType_Album"/> <sys:String>Album</sys:String>
<x:Static Member="properties:Resources.FilterType_Year"/> <sys:String>Year</sys:String>
<x:Static Member="properties:Resources.FilterType_Genre"/> <sys:String>Genre</sys:String>
<x:Static Member="properties:Resources.FilterType_Directory"/> <sys:String>Directory</sys:String>
</x:Array> </x:Array>
<x:Array x:Key="OperatorTypeA" Type="sys:String"> <x:Array x:Key="OperatorTypeA" Type="sys:String">
<x:Static Member="properties:Resources.Operator_Contains"/> <sys:String>contains</sys:String>
<x:Static Member="properties:Resources.Operator_Is"/> <sys:String>is</sys:String>
<x:Static Member="properties:Resources.Operator_IsNot"/> <sys:String>is not</sys:String>
</x:Array> </x:Array>
<x:Array x:Key="OperatorTypeB" Type="sys:String"> <x:Array x:Key="OperatorTypeB" Type="sys:String">
<x:Static Member="properties:Resources.Operator_Is"/> <sys:String>is</sys:String>
<x:Static Member="properties:Resources.Operator_IsNot"/> <sys:String>is not</sys:String>
</x:Array> </x:Array>
<x:Array x:Key="OperatorTypeC" Type="sys:String"> <x:Array x:Key="OperatorTypeC" Type="sys:String">
<x:Static Member="properties:Resources.Operator_Is"/> <sys:String>is</sys:String>
</x:Array> </x:Array>
<DataTemplate x:Key="FilterPanel"> <DataTemplate x:Key="FilterPanel">
@ -42,14 +42,14 @@
</DataTemplate> </DataTemplate>
</Window.Resources> </Window.Resources>
<Grid Margin="0,5,0,0"> <Grid>
<StackPanel> <StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,5"> <StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,5">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="🔡"/> <emoji:EmojiInline Text="🔡"/>
<Run Text="{x:Static properties:Resources.Shuffle_Filter}"/> <Run Text="Filter"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
<StackPanel Orientation="Vertical" Margin="5,0,5,0"> <StackPanel Orientation="Vertical" Margin="5,0,5,0">
@ -60,16 +60,15 @@
<StackPanel x:Name="SongFilterPanel" Margin="0,10,0,0"> <StackPanel x:Name="SongFilterPanel" Margin="0,10,0,0">
<TextBlock> <TextBlock>
<Run Text="{x:Static properties:Resources.Shuffle_FilterSongNumber}"/><Run Text=" "/><Run x:Name="SongFilterNumber" FontWeight="Bold"/> <Run Text="Number of songs in filter: "/><Run x:Name="SongFilterNumber" FontWeight="Bold"/>
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<StackPanel Margin="0,5,0,0"> <StackPanel Margin="0,5,0,0">
<StackPanel Orientation="Horizontal" Margin="0,5,0,5"> <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="Query filter" 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}"/> <Button Content="Reset" 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="QueryFilterText" Text="Querying filter..." 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> </StackPanel>
</StackPanel> </StackPanel>
@ -77,43 +76,37 @@
<StackPanel Orientation="Horizontal" Margin="0,5,0,0"> <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<GroupBox DockPanel.Dock="Right" Padding="0,4,0,0" Width="300"> <GroupBox DockPanel.Dock="Right" Padding="0,4,0,0" Width="248">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="🧾"/> <emoji:EmojiInline Text=""/>
<Run Text="{x:Static properties:Resources.Queue_Management}"/> <Run Text="Add to queue"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
<StackPanel Orientation="Vertical" Margin="5,5,5,0"> <StackPanel Orientation="Vertical" Margin="5,5,5,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Text="{x:Static properties:Resources.Shuffle_QueueSongs}" Margin="0,0,5,5"/> <TextBlock Text="Songs to add" Margin="0,0,5,5"/>
<TextBox x:Name="SongNumber" KeyUp="AddToQueueHandler" PreviewTextInput="QueueValidationTextBox" MaxLength="4" Text="15" Width="35" HorizontalAlignment="Left" VerticalAlignment="Top"/> <TextBox x:Name="SongNumber" KeyUp="AddToQueueHandler" PreviewTextInput="QueueValidationTextBox" MaxLength="4" Text="15" Width="35" HorizontalAlignment="Left" VerticalAlignment="Top"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,5,0,0"> <StackPanel Orientation="Horizontal" Margin="0,5,0,0">
<Button Content="{x:Static properties:Resources.Shuffle_Queue}" Click="AddToQueue_Clicked" Padding="5, 2" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/> <Button Content="Add to queue" Click="AddToQueue_Clicked" Padding="5, 2" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
<TextBlock x:Name="SearchStatus" Margin="15,3,0,0" FontStyle="Italic" Visibility="Collapsed"> <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}"/> <Run Text="Adding "/><Run x:Name="NumberAddedSongs"/><Run Text=" songs..."/>
</TextBlock> </TextBlock>
<TextBlock x:Name="SearchStatus2" Text="{x:Static properties:Resources.Shuffle_ButtonMessage3}" Margin="3,3,0,0" FontStyle="Italic" Visibility="Collapsed"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,15,0,0">
<Button Content="{x:Static properties:Resources.Clear_Queue}" Click="ClearQueue_Clicked" Padding="5, 2" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
<Button Content="{x:Static properties:Resources.Play_Queue}" Click="StartQueue_Clicked" Padding="5, 2" Margin="5, 0" HorizontalAlignment="Left" FocusVisualStyle="{x:Null}"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</GroupBox> </GroupBox>
<GroupBox DockPanel.Dock="Left" Padding="0,4,0,0" Width="260" Margin="0,0,5,0"> <GroupBox DockPanel.Dock="Left" Padding="0,4,0,0" Width="248" Margin="0,0,5,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<Run Text="♾️"/> <emoji:EmojiInline Text="♾️"/>
<Run Text="{x:Static properties:Resources.Shuffle_Continuous}"/> <Run Text="Continuous shuffle"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
<StackPanel Orientation="Horizontal" Margin="5,7,5,0"> <StackPanel Orientation="Horizontal" Margin="5,7,5,0">
<CheckBox x:Name="ContinuousShuffle" Checked="ContinuousShuffle_Checked" Unchecked="ContinuousShuffle_Checked" FocusVisualStyle="{x:Null}" VerticalAlignment="Top"> <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"/> <TextBlock Text="Enable continuous shuffle" TextWrapping="Wrap"/>
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
</GroupBox> </GroupBox>

View File

@ -32,18 +32,11 @@ namespace unison
public Shuffle() public Shuffle()
{ {
InitializeComponent(); InitializeComponent();
InitHwnd();
GenreList = new(); GenreList = new();
FolderList = new(); FolderList = new();
Filters = new(); Filters = new();
SongFilterNumber.Text = "0"; SongFilterNumber.Text = "0";
WindowState = WindowState.Minimized;
Top = Properties.Settings.Default.ShuffleWindowTop;
Left = Properties.Settings.Default.ShuffleWindowLeft;
_mpd = (MPDHandler)Application.Current.Properties["mpd"]; _mpd = (MPDHandler)Application.Current.Properties["mpd"];
_shuffle = (ShuffleHandler)Application.Current.Properties["shuffle"]; _shuffle = (ShuffleHandler)Application.Current.Properties["shuffle"];
} }
@ -54,7 +47,7 @@ namespace unison
ListFolder(_mpd._cancelCommand.Token); ListFolder(_mpd._cancelCommand.Token);
} }
private async void ListGenre(CancellationToken token) public async void ListGenre(CancellationToken token)
{ {
if (GenreList.Count != 0) if (GenreList.Count != 0)
return; return;
@ -62,7 +55,7 @@ namespace unison
if (token.IsCancellationRequested) if (token.IsCancellationRequested)
return; return;
List<string> Response = await _mpd.SafelySendCommandAsync(new ListCommand(MpdTags.Genre, null, null, null)); List<string> Response = await _mpd.SafelySendCommandAsync(new ListCommand(MpdTags.Genre, null, null));
if (Response == null) if (Response == null)
return; return;
@ -71,7 +64,7 @@ namespace unison
GenreList.Add(genre); GenreList.Add(genre);
} }
private async void ListFolder(CancellationToken token) public async void ListFolder(CancellationToken token)
{ {
if (FolderList.Count != 0) if (FolderList.Count != 0)
return; return;
@ -90,7 +83,7 @@ namespace unison
private bool IsFilterEmpty() private bool IsFilterEmpty()
{ {
if (Filters.Count == 0) if (Filters.Count() == 0)
return true; return true;
return false; return false;
} }
@ -131,28 +124,28 @@ namespace unison
_shuffle.SongList.Clear(); _shuffle.SongList.Clear();
} }
private static ITag FilterEquivalence_Type(string value) private ITag FilterEquivalence_Type(string value)
{ {
if (value == unison.Resources.Resources.FilterType_Song) if (value == "Song")
return MpdTags.Title; return MpdTags.Title;
else if (value == unison.Resources.Resources.FilterType_Artist) else if (value == "Artist")
return MpdTags.Artist; return MpdTags.Artist;
else if (value == unison.Resources.Resources.FilterType_Album) else if (value == "Album")
return MpdTags.Album; return MpdTags.Album;
else if (value == unison.Resources.Resources.FilterType_Year) else if (value == "Year")
return MpdTags.Date; return MpdTags.Date;
else if (value == unison.Resources.Resources.FilterType_Genre) else if (value == "Genre")
return MpdTags.Genre; return MpdTags.Genre;
return MpdTags.Title; return MpdTags.Title;
} }
private static FilterOperator FilterEquivalence_Operator(string value) private FilterOperator FilterEquivalence_Operator(string value)
{ {
if (value == unison.Resources.Resources.Operator_Contains) if (value == "contains")
return FilterOperator.Contains; return FilterOperator.Contains;
else if (value == unison.Resources.Resources.Operator_Is) else if (value == "is")
return FilterOperator.Equal; return FilterOperator.Equal;
else if (value == unison.Resources.Resources.Operator_IsNot) else if (value == "is not")
return FilterOperator.Different; return FilterOperator.Different;
return FilterOperator.Equal; return FilterOperator.Equal;
} }
@ -187,9 +180,9 @@ namespace unison
private void FilterType_SelectionChanged(object sender, SelectionChangedEventArgs e) private void FilterType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
string item = e.AddedItems[0].ToString(); string item = e.AddedItems[0].ToString();
if (item == unison.Resources.Resources.FilterType_Genre) if (item == "Genre")
FilterType_Change(sender, "OperatorTypeB", GenreList); FilterType_Change(sender, "OperatorTypeB", GenreList);
else if (item == unison.Resources.Resources.FilterType_Directory) else if (item == "Directory")
FilterType_Change(sender, "OperatorTypeC", FolderList); FilterType_Change(sender, "OperatorTypeC", FolderList);
else else
{ {
@ -224,14 +217,10 @@ namespace unison
{ {
QueryFilterText.Visibility = Visibility.Visible; QueryFilterText.Visibility = Visibility.Visible;
await UpdateFilter(); await UpdateFilter();
QueryFilterText2.Visibility = Visibility.Visible;
TimedText(QueryFilterText, 1); TimedText(QueryFilterText, 1);
TimedText(QueryFilterText2, 1);
} }
private static void TimedText(TextBlock textBlock, int time) private void TimedText(TextBlock textBlock, int time)
{ {
DispatcherTimer Timer = new DispatcherTimer(); DispatcherTimer Timer = new DispatcherTimer();
Timer.Interval = TimeSpan.FromSeconds(time); Timer.Interval = TimeSpan.FromSeconds(time);
@ -265,7 +254,7 @@ namespace unison
{ {
if (child.Name == "FilterType") if (child.Name == "FilterType")
{ {
if (child.SelectedItem.ToString() == unison.Resources.Resources.FilterType_Directory) if (child.SelectedItem.ToString() == "Directory")
isDir = true; isDir = true;
else else
tag = FilterEquivalence_Type(child.SelectedItem.ToString()); tag = FilterEquivalence_Type(child.SelectedItem.ToString());
@ -341,16 +330,18 @@ namespace unison
await UpdateFilter(); await UpdateFilter();
QueueValidationNumber(); QueueValidationNumber();
// TODO
// Added => Adding songs...
// to
// Added X songs! (display for 5 seconds)
NumberAddedSongs.Text = SongNumber.Text; NumberAddedSongs.Text = SongNumber.Text;
SearchStatus.Visibility = Visibility.Visible; SearchStatus.Visibility = Visibility.Visible;
int Num = int.Parse(SongNumber.Text); int Num = int.Parse(SongNumber.Text);
await AddToQueue_Internal(Num); await AddToQueue_Internal(Num);
SearchStatus2.Visibility = Visibility.Visible;
TimedText(SearchStatus, 2); TimedText(SearchStatus, 2);
TimedText(SearchStatus2, 2);
} }
private async Task AddToQueue_Internal(int Num) private async Task AddToQueue_Internal(int Num)
@ -382,16 +373,6 @@ namespace unison
AddToQueue(); AddToQueue();
} }
public void ClearQueue_Clicked(object sender, RoutedEventArgs e)
{
_mpd.ClearQueue();
}
public void StartQueue_Clicked(object sender, RoutedEventArgs e)
{
_mpd.PlayCommand();
}
public bool GetContinuous() public bool GetContinuous()
{ {
return _continuous; return _continuous;
@ -403,9 +384,8 @@ namespace unison
return; return;
int PlaylistLength = _mpd.GetStatus().PlaylistLength; int PlaylistLength = _mpd.GetStatus().PlaylistLength;
int Num = 50 - PlaylistLength; int Num = 10 - PlaylistLength;
if (Num < 1)
if (PlaylistLength > 25)
return; return;
await UpdateFilter(); await UpdateFilter();
@ -435,12 +415,5 @@ namespace unison
WindowInteropHelper helper = new(this); WindowInteropHelper helper = new(this);
helper.EnsureHandle(); helper.EnsureHandle();
} }
private void Window_LocationChanged(object sender, EventArgs e)
{
Properties.Settings.Default.ShuffleWindowTop = Top;
Properties.Settings.Default.ShuffleWindowLeft = Left;
Properties.Settings.Default.Save();
}
} }
} }

View File

@ -3,7 +3,6 @@ using System.Windows.Input;
using System.Reflection; using System.Reflection;
using System.ComponentModel; using System.ComponentModel;
using System; using System;
using System.Diagnostics;
namespace unison namespace unison
{ {
@ -50,7 +49,7 @@ namespace unison
{ {
CommandAction = () => CommandAction = () =>
{ {
Application.Current.Dispatcher.BeginInvoke(new Action(() => OnPropertyChanged(SnapcastText))); Application.Current.Dispatcher.BeginInvoke(new Action(() => OnPropertyChanged("SnapcastText")));
SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"]; SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"];
snapcast.LaunchOrExit(); snapcast.LaunchOrExit();
@ -60,7 +59,7 @@ namespace unison
} }
} }
public static ICommand Radios public ICommand Radios
{ {
get get
{ {
@ -72,7 +71,7 @@ namespace unison
} }
} }
public static ICommand Shuffle public ICommand Shuffle
{ {
get get
{ {
@ -84,7 +83,7 @@ namespace unison
} }
} }
public static ICommand Settings public ICommand Settings
{ {
get get
{ {

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,12 +2,12 @@
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<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.5</Version> <Version>1.4</Version>
<Company /> <Company />
<Authors>Théo Marchal</Authors> <Authors>Théo Marchal</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
@ -15,7 +15,6 @@
<RepositoryUrl>https://github.com/ZetaKebab/unison</RepositoryUrl> <RepositoryUrl>https://github.com/ZetaKebab/unison</RepositoryUrl>
<Copyright>Théo Marchal</Copyright> <Copyright>Théo Marchal</Copyright>
<PackageIconUrl /> <PackageIconUrl />
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -53,11 +52,11 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.9.2" /> <PackageReference Include="Autoupdater.NET.Official" Version="1.7.6" />
<PackageReference Include="Emoji.Wpf" Version="0.3.4" /> <PackageReference Include="Emoji.Wpf" Version="0.3.3" />
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="2.0.1" /> <PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
<PackageReference Include="RadioBrowser" Version="0.7.0" /> <PackageReference Include="RadioBrowser" Version="0.6.1" />
<PackageReference Include="MpcNET" Version="1.6.6" /> <PackageReference Include="MpcNET" Version="1.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -91,25 +90,25 @@
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None> </None>
<None Update="snapcast_0.31\FLAC.dll"> <None Update="snapclient_0.26.0-1_win64\FLAC.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="snapcast_0.31\ogg.dll"> <None Update="snapclient_0.26.0-1_win64\ogg.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="snapcast_0.31\opus.dll"> <None Update="snapclient_0.26.0-1_win64\opus.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="snapcast_0.31\README.txt"> <None Update="snapclient_0.26.0-1_win64\README.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="snapcast_0.31\snapclient.exe"> <None Update="snapclient_0.26.0-1_win64\snapclient.exe">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="snapcast_0.31\soxr.dll"> <None Update="snapclient_0.26.0-1_win64\soxr.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Update="snapcast_0.31\vorbis.dll"> <None Update="snapclient_0.26.0-1_win64\vorbis.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>