Compare commits
43 Commits
2c8696155a
...
v1.3
Author | SHA1 | Date | |
---|---|---|---|
b074a4e975 | |||
53d7668fd6 | |||
124b263499 | |||
3f74b1a4e7 | |||
ded5908ca2 | |||
519fe4968e | |||
f95b884d16 | |||
4136c13d5b | |||
0ab1afc2f8 | |||
3b59e51368 | |||
c7a93c2d82 | |||
43350aed36 | |||
301309737b | |||
ab46442108 | |||
3c1aad7d27 | |||
d1b6d97fe8 | |||
15db118dd6 | |||
67315f90b0 | |||
6d75f88927 | |||
7fdf349c28 | |||
9a5f686dde | |||
b209cb2556 | |||
6e1f43bed0 | |||
9bd088fac8 | |||
0be28ab205 | |||
62a3220f7f | |||
6e4ed82211 | |||
7b2a7bae21 | |||
72d751db71 | |||
e4b63073d8 | |||
d49f3ab030 | |||
62835065c0 | |||
196b93c7f3 | |||
f9a14ee3c0 | |||
7aafa935e1 | |||
2960afd9bd | |||
23098e0ebb | |||
32d3610b07 | |||
fbb65a039a | |||
4d180871ca | |||
b87bff54c4 | |||
be8cef35d3 | |||
c568a957f7 |
1
App.xaml
@ -8,6 +8,7 @@
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="Views/Systray.xaml" />
|
||||
<ResourceDictionary Source="Resources/Resources.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style x:Key="RepeatButtonTransparent" TargetType="{x:Type RepeatButton}">
|
||||
|
10
App.xaml.cs
@ -1,4 +1,5 @@
|
||||
using System.Windows;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using Hardcodet.Wpf.TaskbarNotification;
|
||||
|
||||
namespace unison
|
||||
@ -12,6 +13,11 @@ namespace unison
|
||||
|
||||
protected override void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
//debug language
|
||||
//unison.Resources.Resources.Culture = CultureInfo.GetCultureInfo("fr-FR");
|
||||
//unison.Resources.Resources.Culture = CultureInfo.GetCultureInfo("es-ES");
|
||||
|
||||
|
||||
base.OnStartup(e);
|
||||
|
||||
_mpd = new MPDHandler();
|
||||
@ -33,7 +39,7 @@ namespace unison
|
||||
{
|
||||
_systray.Dispose();
|
||||
_snapcast.LaunchOrExit(true);
|
||||
_hotkeys.RemoveHotKeys();
|
||||
_hotkeys.RemoveHotkeys();
|
||||
base.OnExit(e);
|
||||
}
|
||||
}
|
||||
|
43
CHANGELOG.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Changelog
|
||||
|
||||
## v1.3
|
||||
|
||||
*Released: 18/04/2022*
|
||||
|
||||
* New feature: add support for readpicture, aka embedded cover art
|
||||
* New feature: add support for MPD password
|
||||
* Spanish translation
|
||||
* Trim album release date
|
||||
* Cover icon when a radio is playing
|
||||
* Update MpcNET package from 1.3 to 1.4
|
||||
* Fix: Snapcast not working with hostname
|
||||
* Fix: some radios crash
|
||||
* Fix: disable/enable radios and Snapcast with connection
|
||||
|
||||
## v1.2
|
||||
|
||||
*Released: 07/04/2022*
|
||||
|
||||
* New feature: support for custom shortcuts
|
||||
* New feature: MPD stats
|
||||
* Add GitHub repository link in settings
|
||||
* MpcNET NuGet package integration
|
||||
* Update Snapcast from v0.25 to v0.26
|
||||
* Fix: hostname supported for connection
|
||||
* Fix: crash when using invalid IP or Hostname
|
||||
* Fix: crash when no internet connection when using RadioBrowser
|
||||
|
||||
## v1.1
|
||||
|
||||
*Released: 04/10/2021*
|
||||
|
||||
* Radio browser
|
||||
* Mute shortcut
|
||||
* Enter key works in settings textboxes
|
||||
* Share current song by double-clicking
|
||||
|
||||
## v1.0
|
||||
|
||||
*Released: 03/09/2021*
|
||||
|
||||
* First release of unison
|
@ -15,20 +15,168 @@ namespace unison
|
||||
|
||||
private const int HOTKEY_ID = 9000;
|
||||
|
||||
// modifiers
|
||||
private const uint MOD_NONE = 0x0000;
|
||||
private const uint MOD_ALT = 0x0001;
|
||||
private const uint MOD_CONTROL = 0x0002;
|
||||
private const uint MOD_SHIFT = 0x0004;
|
||||
private const uint MOD_WIN = 0x0008;
|
||||
public enum MOD : int
|
||||
{
|
||||
None = 0x0000,
|
||||
Alt = 0x0001,
|
||||
Control = 0x0002,
|
||||
Shift = 0x0004,
|
||||
//Win = 0x0008
|
||||
};
|
||||
|
||||
// reference => https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
||||
private const uint VK_MEDIA_PREV_TRACK = 0xB1;
|
||||
private const uint VK_MEDIA_NEXT_TRACK = 0xB0;
|
||||
private const uint VK_MEDIA_PLAY_PAUSE = 0xB3;
|
||||
private const uint VK_VOLUME_UP = 0xAF;
|
||||
private const uint VK_VOLUME_DOWN = 0xAE;
|
||||
private const uint VK_ENTER = 0x0D;
|
||||
public enum VK : int
|
||||
{
|
||||
None = 0x00,
|
||||
Back = 0x08,
|
||||
Tab = 0x09,
|
||||
Clear = 0x0c,
|
||||
Return = 0x0d,
|
||||
Menu = 0x12,
|
||||
Pause = 0x13,
|
||||
Capital = 0x14,
|
||||
Kana = 0x15,
|
||||
Hangul = 0x15,
|
||||
Junja = 0x17,
|
||||
Final = 0x18,
|
||||
Hanja = 0x19,
|
||||
Kanji = 0x19,
|
||||
Escape = 0x1b,
|
||||
Convert = 0x1c,
|
||||
NonConvert = 0x1d,
|
||||
Accept = 0x1e,
|
||||
ModeChange = 0x1f,
|
||||
Space = 0x20,
|
||||
Prior = 0x21,
|
||||
Next = 0x22,
|
||||
End = 0x23,
|
||||
Home = 0x24,
|
||||
Left = 0x25,
|
||||
Up = 0x26,
|
||||
Right = 0x27,
|
||||
Down = 0x28,
|
||||
Select = 0x29,
|
||||
Print = 0x2a,
|
||||
Execute = 0x2b,
|
||||
Snapshot = 0x2c,
|
||||
Insert = 0x2d,
|
||||
Delete = 0x2e,
|
||||
Help = 0x2f,
|
||||
NumRow0 = 0x30,
|
||||
NumRow1 = 0x31,
|
||||
NumRow2 = 0x32,
|
||||
NumRow3 = 0x33,
|
||||
NumRow4 = 0x34,
|
||||
NumRow5 = 0x35,
|
||||
NumRow6 = 0x36,
|
||||
NumRow7 = 0x37,
|
||||
NumRow8 = 0x38,
|
||||
NumRow9 = 0x39,
|
||||
A = 0x41,
|
||||
B = 0x42,
|
||||
C = 0x43,
|
||||
D = 0x44,
|
||||
E = 0x45,
|
||||
F = 0x46,
|
||||
G = 0x47,
|
||||
H = 0x48,
|
||||
I = 0x49,
|
||||
J = 0x4a,
|
||||
K = 0x4b,
|
||||
L = 0x4c,
|
||||
M = 0x4d,
|
||||
N = 0x4e,
|
||||
O = 0x4f,
|
||||
P = 0x50,
|
||||
Q = 0x51,
|
||||
R = 0x52,
|
||||
S = 0x53,
|
||||
T = 0x54,
|
||||
U = 0x55,
|
||||
V = 0x56,
|
||||
W = 0x57,
|
||||
X = 0x58,
|
||||
Y = 0x59,
|
||||
Z = 0x5a,
|
||||
Apps = 0x5d,
|
||||
Sleep = 0x5f,
|
||||
NumPad0 = 0x60,
|
||||
NumPad1 = 0x61,
|
||||
NumPad2 = 0x62,
|
||||
NumPad3 = 0x63,
|
||||
NumPad4 = 0x64,
|
||||
NumPad5 = 0x65,
|
||||
NumPad6 = 0x66,
|
||||
NumPad7 = 0x67,
|
||||
NumPad8 = 0x68,
|
||||
NumPad9 = 0x69,
|
||||
Multiply = 0x6a,
|
||||
Add = 0x6b,
|
||||
Separator = 0x6c,
|
||||
Subtract = 0x6d,
|
||||
Decimal = 0x6e,
|
||||
Divide = 0x6f,
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
F4 = 0x73,
|
||||
F5 = 0x74,
|
||||
F6 = 0x75,
|
||||
F7 = 0x76,
|
||||
F8 = 0x77,
|
||||
F9 = 0x78,
|
||||
F10 = 0x79,
|
||||
F11 = 0x7a,
|
||||
F12 = 0x7b,
|
||||
F13 = 0x7c,
|
||||
F14 = 0x7d,
|
||||
F15 = 0x7e,
|
||||
F16 = 0x7f,
|
||||
F17 = 0x80,
|
||||
F18 = 0x81,
|
||||
F19 = 0x82,
|
||||
F20 = 0x83,
|
||||
F21 = 0x84,
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
NumLock = 0x90,
|
||||
Scroll = 0x91,
|
||||
LMenu = 0xa4,
|
||||
RMenu = 0xa5,
|
||||
BrowserBack = 0xa6,
|
||||
BrowserForward = 0xa7,
|
||||
BrowserRefresh = 0xa8,
|
||||
BrowserStop = 0xa9,
|
||||
BrowserSearch = 0xaa,
|
||||
BrowserFavorites = 0xab,
|
||||
BrowserHome = 0xac,
|
||||
VolumeMute = 0xad,
|
||||
VolumeDown = 0xae,
|
||||
VolumeUp = 0xaf,
|
||||
MediaNextTrack = 0xb0,
|
||||
MediaPreviousTrack = 0xb1,
|
||||
MediaStop = 0xb2,
|
||||
MediaPlayPause = 0xb3,
|
||||
LaunchMail = 0xb4,
|
||||
LaunchMediaSelect = 0xb5,
|
||||
LaunchApp1 = 0xb6,
|
||||
LaunchApp2 = 0xb7,
|
||||
};
|
||||
|
||||
public struct HotkeyPair
|
||||
{
|
||||
public MOD mod;
|
||||
public VK vk;
|
||||
|
||||
public HotkeyPair(MOD _mod, VK _vk) { mod = _mod; vk = _vk; }
|
||||
|
||||
public uint GetMOD() { return (uint)mod; }
|
||||
public uint GetVK() { return (uint)vk; }
|
||||
|
||||
public void SetMOD(MOD modmod) { mod = modmod; }
|
||||
public void SetVK(VK vkvk) { vk = vkvk; }
|
||||
}
|
||||
|
||||
private MainWindow _appWindow;
|
||||
private readonly MPDHandler _mpd;
|
||||
@ -36,9 +184,31 @@ namespace unison
|
||||
private IntPtr _windowHandle;
|
||||
private HwndSource _source;
|
||||
|
||||
public HotkeyPair _NextTrack;
|
||||
public HotkeyPair _PreviousTrack;
|
||||
public HotkeyPair _PlayPause;
|
||||
public HotkeyPair _VolumeUp;
|
||||
public HotkeyPair _VolumeDown;
|
||||
public HotkeyPair _VolumeMute;
|
||||
public HotkeyPair _ShowWindow;
|
||||
public HotkeyPair[] _Shortcuts;
|
||||
|
||||
public HotkeyHandler()
|
||||
{
|
||||
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
Initialize();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_NextTrack = new HotkeyPair((MOD)Properties.Settings.Default.nextTrack_mod, (VK)Properties.Settings.Default.nextTrack_vk);
|
||||
_PreviousTrack = new HotkeyPair((MOD)Properties.Settings.Default.previousTrack_mod, (VK)Properties.Settings.Default.previousTrack_vk);
|
||||
_PlayPause = new HotkeyPair((MOD)Properties.Settings.Default.playPause_mod, (VK)Properties.Settings.Default.playPause_vk);
|
||||
_VolumeUp = new HotkeyPair((MOD)Properties.Settings.Default.volumeUp_mod, (VK)Properties.Settings.Default.volumeUp_vk);
|
||||
_VolumeDown = new HotkeyPair((MOD)Properties.Settings.Default.volumeDown_mod, (VK)Properties.Settings.Default.volumeDown_vk);
|
||||
_VolumeMute = new HotkeyPair((MOD)Properties.Settings.Default.volumeMute_mod, (VK)Properties.Settings.Default.volumeMute_vk);
|
||||
_ShowWindow = new HotkeyPair((MOD)Properties.Settings.Default.showWindow_mod, (VK)Properties.Settings.Default.showWindow_vk);
|
||||
_Shortcuts = new HotkeyPair[] { _NextTrack, _PreviousTrack, _PlayPause, _VolumeUp, _VolumeDown, _VolumeMute, _ShowWindow };
|
||||
}
|
||||
|
||||
public void Activate(Window win)
|
||||
@ -47,17 +217,28 @@ namespace unison
|
||||
{
|
||||
_windowHandle = new WindowInteropHelper(win).Handle;
|
||||
_source = HwndSource.FromHwnd(_windowHandle);
|
||||
_source.AddHook(HwndHook);
|
||||
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_PREV_TRACK);
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_NEXT_TRACK);
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_MEDIA_PLAY_PAUSE);
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_UP);
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL, VK_VOLUME_DOWN);
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, MOD_CONTROL | MOD_ALT, VK_ENTER);
|
||||
AddHotkeys();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddHotkeys()
|
||||
{
|
||||
_source.AddHook(HwndHook);
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, _NextTrack.GetMOD(), _NextTrack.GetVK());
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, _PreviousTrack.GetMOD(), _PreviousTrack.GetVK());
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, _PlayPause.GetMOD(), _PlayPause.GetVK());
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, _VolumeUp.GetMOD(), _VolumeUp.GetVK());
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, _VolumeDown.GetMOD(), _VolumeDown.GetVK());
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, _VolumeMute.GetMOD(), _VolumeMute.GetVK());
|
||||
RegisterHotKey(_windowHandle, HOTKEY_ID, _ShowWindow.GetMOD(), _ShowWindow.GetVK());
|
||||
}
|
||||
|
||||
public void RemoveHotkeys()
|
||||
{
|
||||
_source.RemoveHook(HwndHook);
|
||||
UnregisterHotKey(_windowHandle, HOTKEY_ID);
|
||||
}
|
||||
|
||||
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||
{
|
||||
const int WM_HOTKEY = 0x0312;
|
||||
@ -65,58 +246,48 @@ namespace unison
|
||||
if (msg == WM_HOTKEY && wParam.ToInt32() == HOTKEY_ID)
|
||||
{
|
||||
uint vkey = ((uint)lParam >> 16) & 0xFFFF;
|
||||
switch (vkey)
|
||||
{
|
||||
case VK_MEDIA_NEXT_TRACK:
|
||||
_mpd.Next();
|
||||
break;
|
||||
case VK_MEDIA_PREV_TRACK:
|
||||
_mpd.Prev();
|
||||
break;
|
||||
case VK_VOLUME_DOWN:
|
||||
_mpd.VolumeDown();
|
||||
break;
|
||||
case VK_VOLUME_UP:
|
||||
_mpd.VolumeUp();
|
||||
break;
|
||||
case VK_MEDIA_PLAY_PAUSE:
|
||||
_mpd.PlayPause();
|
||||
break;
|
||||
case VK_ENTER:
|
||||
if (_appWindow == null)
|
||||
_appWindow = (MainWindow)Application.Current.MainWindow;
|
||||
|
||||
if (_appWindow.WindowState == WindowState.Minimized)
|
||||
if (vkey == _NextTrack.GetVK())
|
||||
_mpd.Next();
|
||||
else if (vkey == _PreviousTrack.GetVK())
|
||||
_mpd.Prev();
|
||||
else if (vkey == _PlayPause.GetVK())
|
||||
_mpd.PlayPause();
|
||||
else if (vkey == _VolumeUp.GetVK())
|
||||
_mpd.VolumeUp();
|
||||
else if (vkey == _VolumeDown.GetVK())
|
||||
_mpd.VolumeDown();
|
||||
else if (vkey == _VolumeMute.GetVK())
|
||||
_mpd.VolumeMute();
|
||||
else if (vkey == _ShowWindow.GetVK())
|
||||
{
|
||||
if (_appWindow == null)
|
||||
_appWindow = (MainWindow)Application.Current.MainWindow;
|
||||
|
||||
if (_appWindow.WindowState == WindowState.Minimized)
|
||||
{
|
||||
_appWindow.Show();
|
||||
_appWindow.Activate();
|
||||
_appWindow.WindowState = WindowState.Normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_appWindow.IsActive)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
_appWindow.WindowState = WindowState.Minimized;
|
||||
}
|
||||
else // not minimized but not in front
|
||||
{
|
||||
_appWindow.Show();
|
||||
_appWindow.Activate();
|
||||
_appWindow.WindowState = WindowState.Normal;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_appWindow.IsActive)
|
||||
{
|
||||
_appWindow.Hide();
|
||||
_appWindow.WindowState = WindowState.Minimized;
|
||||
}
|
||||
else // not minimized but not in front
|
||||
{
|
||||
_appWindow.Show();
|
||||
_appWindow.Activate();
|
||||
_appWindow.WindowState = WindowState.Normal;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
public void RemoveHotKeys()
|
||||
{
|
||||
_source.RemoveHook(HwndHook);
|
||||
UnregisterHotKey(_windowHandle, HOTKEY_ID);
|
||||
}
|
||||
}
|
||||
}
|
@ -8,9 +8,11 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Threading;
|
||||
using MpcNET;
|
||||
using MpcNET.Commands.Database;
|
||||
using MpcNET.Commands.Playback;
|
||||
using MpcNET.Commands.Queue;
|
||||
using MpcNET.Commands.Reflection;
|
||||
using MpcNET.Commands.Status;
|
||||
using MpcNET.Message;
|
||||
@ -18,11 +20,23 @@ using MpcNET.Types;
|
||||
|
||||
namespace unison
|
||||
{
|
||||
public class Statistics
|
||||
{
|
||||
public int Songs { get; set; }
|
||||
public int Albums { get; set; }
|
||||
public int Artists { get; set; }
|
||||
public string TotalPlaytime { get; set; }
|
||||
public string Uptime { get; set; }
|
||||
public string TotalTimePlayed { get; set; }
|
||||
public string DatabaseUpdate { get; set; }
|
||||
}
|
||||
|
||||
public class MPDHandler
|
||||
{
|
||||
private bool _connected;
|
||||
public string _version;
|
||||
private int _currentVolume;
|
||||
private int _previousVolume;
|
||||
private bool _currentRandom;
|
||||
private bool _currentRepeat;
|
||||
private bool _currentSingle;
|
||||
@ -33,7 +47,14 @@ namespace unison
|
||||
private MpdStatus _currentStatus;
|
||||
private IMpdFile _currentSong;
|
||||
private BitmapFrame _cover;
|
||||
public Statistics _stats;
|
||||
private readonly System.Timers.Timer _elapsedTimer;
|
||||
private DispatcherTimer _retryTimer;
|
||||
|
||||
bool _isUpdatingStatus = false;
|
||||
bool _isUpdatingSong = false;
|
||||
|
||||
public IPAddress _ipAddress;
|
||||
|
||||
private event EventHandler ConnectionChanged;
|
||||
private event EventHandler StatusChanged;
|
||||
@ -43,13 +64,17 @@ namespace unison
|
||||
private MpcConnection _connection;
|
||||
private MpcConnection _commandConnection;
|
||||
private IPEndPoint _mpdEndpoint;
|
||||
private CancellationTokenSource cancelToken;
|
||||
private CancellationTokenSource _cancelToken;
|
||||
|
||||
public MPDHandler()
|
||||
{
|
||||
cancelToken = new CancellationTokenSource();
|
||||
Initialize(null, null);
|
||||
|
||||
Initialize();
|
||||
_stats = new Statistics();
|
||||
|
||||
_retryTimer = new DispatcherTimer();
|
||||
_retryTimer.Interval = TimeSpan.FromSeconds(5);
|
||||
_retryTimer.Tick += Initialize;
|
||||
|
||||
_elapsedTimer = new System.Timers.Timer(500);
|
||||
_elapsedTimer.Elapsed += new System.Timers.ElapsedEventHandler(ElapsedTimer);
|
||||
@ -68,8 +93,13 @@ namespace unison
|
||||
_elapsedTimer.Stop();
|
||||
}
|
||||
|
||||
static void OnConnectionChanged(object sender, EventArgs e)
|
||||
void OnConnectionChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!_connected)
|
||||
_retryTimer.Start();
|
||||
else
|
||||
_retryTimer.Stop();
|
||||
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
MainWindow MainWin = (MainWindow)Application.Current.MainWindow;
|
||||
@ -107,170 +137,28 @@ namespace unison
|
||||
});
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
Connect();
|
||||
}
|
||||
|
||||
public async void Connect()
|
||||
{
|
||||
CancellationToken token = cancelToken.Token;
|
||||
try
|
||||
{
|
||||
_connection = await ConnectInternal(token);
|
||||
_commandConnection = await ConnectInternal(token);
|
||||
}
|
||||
catch(MpcNET.Exceptions.MpcConnectException exception)
|
||||
{
|
||||
Trace.WriteLine("exception: " + exception);
|
||||
}
|
||||
if (_connection.IsConnected)
|
||||
{
|
||||
_connected = true;
|
||||
_version = _connection.Version;
|
||||
ConnectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
await UpdateStatusAsync();
|
||||
await UpdateSongAsync();
|
||||
|
||||
Loop(token);
|
||||
}
|
||||
|
||||
private async Task<MpcConnection> ConnectInternal(CancellationToken token)
|
||||
{
|
||||
IPAddress.TryParse(Properties.Settings.Default.mpd_host, out IPAddress ipAddress);
|
||||
|
||||
_mpdEndpoint = new IPEndPoint(ipAddress, Properties.Settings.Default.mpd_port);
|
||||
MpcConnection connection = new MpcConnection(_mpdEndpoint);
|
||||
await connection.ConnectAsync(token);
|
||||
|
||||
if (!string.IsNullOrEmpty(Properties.Settings.Default.mpd_password))
|
||||
{
|
||||
IMpdMessage<string> result = await connection.SendAsync(new PasswordCommand(Properties.Settings.Default.mpd_password));
|
||||
if (!result.IsResponseValid)
|
||||
{
|
||||
string mpdError = result.Response?.Result?.MpdError;
|
||||
Trace.WriteLine(mpdError);
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
private void Loop(CancellationToken token)
|
||||
public void SendCommand<T>(IMpcCommand<T> command)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (token.IsCancellationRequested || _connection == null)
|
||||
break;
|
||||
|
||||
Trace.WriteLine("loop");
|
||||
var idleChanges = await _connection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options"));
|
||||
|
||||
if (idleChanges.IsResponseValid)
|
||||
await HandleIdleResponseAsync(idleChanges.Response.Content);
|
||||
else
|
||||
throw new Exception(idleChanges.Response?.Content);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task HandleIdleResponseAsync(string subsystems)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options"))
|
||||
{
|
||||
await UpdateStatusAsync();
|
||||
|
||||
if (subsystems.Contains("player"))
|
||||
{
|
||||
await UpdateSongAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
bool _isUpdatingStatus = false;
|
||||
private async Task UpdateStatusAsync()
|
||||
{
|
||||
if (_connection == null) return;
|
||||
|
||||
if (_isUpdatingStatus) return;
|
||||
_isUpdatingStatus = true;
|
||||
|
||||
try
|
||||
{
|
||||
IMpdMessage<MpdStatus> response = await _connection.SendAsync(new StatusCommand());
|
||||
if (response != null && response.IsResponseValid)
|
||||
{
|
||||
_currentStatus = response.Response.Content;
|
||||
UpdateStatus();
|
||||
}
|
||||
else
|
||||
throw new Exception();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
Connect();
|
||||
}
|
||||
_isUpdatingStatus = false;
|
||||
}
|
||||
|
||||
bool _isUpdatingSong = false;
|
||||
private async Task UpdateSongAsync()
|
||||
{
|
||||
if (_connection == null) return;
|
||||
|
||||
if (_isUpdatingSong) return;
|
||||
_isUpdatingSong = true;
|
||||
|
||||
try
|
||||
{
|
||||
IMpdMessage<IMpdFile> response = await _connection.SendAsync(new CurrentSongCommand());
|
||||
if (response != null && response.IsResponseValid)
|
||||
{
|
||||
_currentSong = response.Response.Content;
|
||||
UpdateSong();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
Connect();
|
||||
}
|
||||
_isUpdatingSong = false;
|
||||
await SafelySendCommandAsync(command);
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<T> SafelySendCommandAsync<T>(IMpcCommand<T> command)
|
||||
{
|
||||
if (_commandConnection == null)
|
||||
{
|
||||
Trace.WriteLine("[SafelySendCommandAsync] no command connection");
|
||||
return default(T);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IMpdMessage<T> response = await _commandConnection.SendAsync(command);
|
||||
if (!response.IsResponseValid)
|
||||
{
|
||||
// If we have an MpdError string, only show that as the error to avoid extra noise
|
||||
var mpdError = response.Response?.Result?.MpdError;
|
||||
string mpdError = response.Response?.Result?.MpdError;
|
||||
if (mpdError != null && mpdError != "")
|
||||
throw new Exception(mpdError);
|
||||
else
|
||||
@ -287,30 +175,256 @@ namespace unison
|
||||
return default(T);
|
||||
}
|
||||
|
||||
private async void GetAlbumBitmap(string path, CancellationToken token = default)
|
||||
private void Initialize(object sender, EventArgs e)
|
||||
{
|
||||
if (!_connected)
|
||||
Connect();
|
||||
}
|
||||
|
||||
public async void Connect()
|
||||
{
|
||||
_cancelToken = new CancellationTokenSource();
|
||||
CancellationToken token = _cancelToken.Token;
|
||||
|
||||
try
|
||||
{
|
||||
_connection = await ConnectInternal(token);
|
||||
_commandConnection = await ConnectInternal(token);
|
||||
}
|
||||
catch (MpcNET.Exceptions.MpcConnectException e)
|
||||
{
|
||||
_connected = false;
|
||||
Trace.WriteLine($"Error in connect: {e.Message}");
|
||||
ConnectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
return;
|
||||
}
|
||||
if (_connection != null && _commandConnection != null)
|
||||
{
|
||||
if (_connection.IsConnected && _commandConnection.IsConnected)
|
||||
{
|
||||
_connected = true;
|
||||
_version = _connection.Version;
|
||||
ConnectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_connected = false;
|
||||
ConnectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
await UpdateStatusAsync();
|
||||
await UpdateSongAsync();
|
||||
|
||||
Loop(token);
|
||||
}
|
||||
|
||||
private async Task<MpcConnection> ConnectInternal(CancellationToken token)
|
||||
{
|
||||
IPAddress.TryParse(Properties.Settings.Default.mpd_host, out _ipAddress);
|
||||
|
||||
if (_ipAddress == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPAddress[] addrList = Dns.GetHostAddresses(Properties.Settings.Default.mpd_host);
|
||||
if (addrList.Length > 0)
|
||||
{
|
||||
foreach (IPAddress addr in addrList)
|
||||
{
|
||||
if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||
_ipAddress = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new MpcNET.Exceptions.MpcConnectException("No correct IP provided by user.");
|
||||
}
|
||||
}
|
||||
|
||||
_mpdEndpoint = new IPEndPoint(_ipAddress, Properties.Settings.Default.mpd_port);
|
||||
MpcConnection connection = new MpcConnection(_mpdEndpoint);
|
||||
await connection.ConnectAsync(token);
|
||||
|
||||
if (!string.IsNullOrEmpty(Properties.Settings.Default.mpd_password))
|
||||
{
|
||||
IMpdMessage<string> result = await connection.SendAsync(new PasswordCommand(Properties.Settings.Default.mpd_password));
|
||||
if (!result.IsResponseValid)
|
||||
{
|
||||
string mpdError = result.Response?.Result?.MpdError;
|
||||
Trace.WriteLine(mpdError);
|
||||
}
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
public void Disconnected()
|
||||
{
|
||||
_connected = false;
|
||||
ConnectionChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
if (_connection != null)
|
||||
_connection = null;
|
||||
|
||||
if (_commandConnection != null)
|
||||
_commandConnection = null;
|
||||
}
|
||||
|
||||
private void Loop(CancellationToken token)
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (token.IsCancellationRequested || _connection == null)
|
||||
break;
|
||||
|
||||
IMpdMessage<string> idleChanges = await _connection.SendAsync(new IdleCommand("stored_playlist playlist player mixer output options update"));
|
||||
|
||||
if (idleChanges.IsResponseValid)
|
||||
await HandleIdleResponseAsync(idleChanges.Response.Content);
|
||||
else
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {idleChanges.Response?.Content}");
|
||||
throw new Exception(idleChanges.Response?.Content);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
Disconnected();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}, token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task HandleIdleResponseAsync(string subsystems)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (subsystems.Contains("player") || subsystems.Contains("mixer") || subsystems.Contains("output") || subsystems.Contains("options"))
|
||||
{
|
||||
await UpdateStatusAsync();
|
||||
|
||||
if (subsystems.Contains("player"))
|
||||
await UpdateSongAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task UpdateStatusAsync()
|
||||
{
|
||||
if (_connection == null || _isUpdatingStatus)
|
||||
return;
|
||||
|
||||
_isUpdatingStatus = true;
|
||||
|
||||
try
|
||||
{
|
||||
IMpdMessage<MpdStatus> response = await _connection.SendAsync(new StatusCommand());
|
||||
if (response != null && response.IsResponseValid)
|
||||
{
|
||||
_currentStatus = response.Response.Content;
|
||||
UpdateStatus();
|
||||
}
|
||||
else
|
||||
throw new Exception();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
}
|
||||
|
||||
_isUpdatingStatus = false;
|
||||
}
|
||||
|
||||
private async Task UpdateSongAsync()
|
||||
{
|
||||
if (_connection == null || _isUpdatingSong)
|
||||
return;
|
||||
|
||||
_isUpdatingSong = true;
|
||||
|
||||
try
|
||||
{
|
||||
IMpdMessage<IMpdFile> response = await _connection.SendAsync(new CurrentSongCommand());
|
||||
if (response != null && response.IsResponseValid)
|
||||
{
|
||||
_currentSong = response.Response.Content;
|
||||
UpdateSong();
|
||||
}
|
||||
else
|
||||
throw new Exception();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Trace.WriteLine($"Error in Idle connection thread: {e.Message}");
|
||||
}
|
||||
|
||||
_isUpdatingSong = false;
|
||||
}
|
||||
|
||||
private async void GetAlbumCover(string path, CancellationToken token = default)
|
||||
{
|
||||
List<byte> data = new List<byte>();
|
||||
try
|
||||
{
|
||||
if (_connection == null) // We got cancelled
|
||||
return;
|
||||
|
||||
bool ReadPictureFailed = true;
|
||||
long totalBinarySize = 9999;
|
||||
long currentSize = 0;
|
||||
|
||||
do
|
||||
{
|
||||
var albumReq = await _connection.SendAsync(new AlbumArtCommand(path, currentSize));
|
||||
if (!albumReq.IsResponseValid) break;
|
||||
if (_connection == null)
|
||||
return;
|
||||
|
||||
var response = albumReq.Response.Content;
|
||||
if (response.Binary == 0) break; // MPD isn't giving us any more data, let's roll with what we have.
|
||||
IMpdMessage<MpdBinaryData> albumReq = await _connection.SendAsync(new ReadPictureCommand(path, currentSize));
|
||||
if (!albumReq.IsResponseValid)
|
||||
break;
|
||||
|
||||
MpdBinaryData response = albumReq.Response.Content;
|
||||
if (response == null || response.Binary == 0)
|
||||
break;
|
||||
|
||||
ReadPictureFailed = false;
|
||||
totalBinarySize = response.Size;
|
||||
currentSize += response.Binary;
|
||||
data.AddRange(response.Data);
|
||||
}
|
||||
while (currentSize < totalBinarySize && !token.IsCancellationRequested);
|
||||
|
||||
do
|
||||
{
|
||||
if (!ReadPictureFailed)
|
||||
break;
|
||||
if (_connection == null)
|
||||
return;
|
||||
|
||||
IMpdMessage<MpdBinaryData> albumReq = await _connection.SendAsync(new AlbumArtCommand(path, currentSize));
|
||||
if (!albumReq.IsResponseValid)
|
||||
break;
|
||||
|
||||
MpdBinaryData response = albumReq.Response.Content;
|
||||
if (response == null || response.Binary == 0)
|
||||
break;
|
||||
|
||||
totalBinarySize = response.Size;
|
||||
currentSize += response.Binary;
|
||||
data.AddRange(response.Data);
|
||||
//Debug.WriteLine($"Downloading albumart: {currentSize}/{totalBinarySize}");
|
||||
} while (currentSize < totalBinarySize && !token.IsCancellationRequested);
|
||||
}
|
||||
while (currentSize < totalBinarySize && !token.IsCancellationRequested);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -319,46 +433,25 @@ namespace unison
|
||||
}
|
||||
|
||||
if (data.Count == 0)
|
||||
{
|
||||
Trace.WriteLine("empty cover");
|
||||
_cover = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
using MemoryStream stream = new MemoryStream(data.ToArray());
|
||||
_cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
try
|
||||
{
|
||||
_cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
}
|
||||
catch (System.NotSupportedException)
|
||||
{
|
||||
_cover = null;
|
||||
}
|
||||
}
|
||||
UpdateCover();
|
||||
}
|
||||
|
||||
public void UpdateSong()
|
||||
{
|
||||
if (!_connected)
|
||||
return;
|
||||
if (_currentSong == null)
|
||||
return;
|
||||
|
||||
_currentTime = _currentStatus.Elapsed.TotalSeconds;
|
||||
_totalTime = _currentSong.Time;
|
||||
if (!_elapsedTimer.Enabled)
|
||||
_elapsedTimer.Start();
|
||||
|
||||
SongChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
string uri = Regex.Escape(_currentSong.Path);
|
||||
GetAlbumBitmap(uri);
|
||||
}
|
||||
|
||||
public void UpdateCover()
|
||||
{
|
||||
CoverChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void UpdateStatus()
|
||||
{
|
||||
if (!_connected)
|
||||
return;
|
||||
if (_currentStatus == null)
|
||||
if (!_connected || _currentStatus == null)
|
||||
return;
|
||||
|
||||
_currentRandom = _currentStatus.Random;
|
||||
@ -370,26 +463,48 @@ namespace unison
|
||||
StatusChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public void UpdateSong()
|
||||
{
|
||||
if (!_connected || _currentSong == null)
|
||||
return;
|
||||
|
||||
_currentTime = _currentStatus.Elapsed.TotalSeconds;
|
||||
_totalTime = _currentSong.Time;
|
||||
if (!_elapsedTimer.Enabled)
|
||||
_elapsedTimer.Start();
|
||||
|
||||
SongChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
string uri = Regex.Escape(_currentSong.Path);
|
||||
GetAlbumCover(uri);
|
||||
}
|
||||
|
||||
public void UpdateCover()
|
||||
{
|
||||
CoverChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public IMpdFile GetCurrentSong() => _currentSong;
|
||||
public MpdStatus GetStatus() => _currentStatus;
|
||||
public BitmapFrame GetCover() => _cover;
|
||||
public string GetVersion() => _version;
|
||||
public Statistics GetStats() => _stats;
|
||||
public double GetCurrentTime() => _currentTime;
|
||||
|
||||
public bool IsConnected() => _connected;
|
||||
public bool IsPlaying() => _currentStatus?.State == MpdState.Play;
|
||||
|
||||
public async void Prev() => await SafelySendCommandAsync(new PreviousCommand());
|
||||
public async void Next() => await SafelySendCommandAsync(new NextCommand());
|
||||
public async void PlayPause() =>await SafelySendCommandAsync(new PauseResumeCommand());
|
||||
public void Prev() => SendCommand(new PreviousCommand());
|
||||
public void Next() => SendCommand(new NextCommand());
|
||||
public void PlayPause() => SendCommand(new PauseResumeCommand());
|
||||
|
||||
public async void Random() => await SafelySendCommandAsync(new RandomCommand(!_currentRandom));
|
||||
public async void Repeat() => await SafelySendCommandAsync(new RepeatCommand(!_currentRepeat));
|
||||
public async void Single() => await SafelySendCommandAsync(new SingleCommand(!_currentSingle));
|
||||
public async void Consume() => await SafelySendCommandAsync(new ConsumeCommand(!_currentConsume));
|
||||
public void Random() => SendCommand(new RandomCommand(!_currentRandom));
|
||||
public void Repeat() => SendCommand(new RepeatCommand(!_currentRepeat));
|
||||
public void Single() => SendCommand(new SingleCommand(!_currentSingle));
|
||||
public void Consume() => SendCommand(new ConsumeCommand(!_currentConsume));
|
||||
|
||||
public async void SetTime(double value) => await SafelySendCommandAsync(new SeekCurCommand(value));
|
||||
public async void SetVolume(int value) => await SafelySendCommandAsync(new SetVolumeCommand((byte)value));
|
||||
public void SetTime(double value) => SendCommand(new SeekCurCommand(value));
|
||||
public void SetVolume(int value) => SendCommand(new SetVolumeCommand((byte)value));
|
||||
|
||||
public void VolumeUp()
|
||||
{
|
||||
@ -406,5 +521,57 @@ namespace unison
|
||||
_currentVolume = 0;
|
||||
SetVolume(_currentVolume);
|
||||
}
|
||||
|
||||
public void VolumeMute()
|
||||
{
|
||||
if (_currentVolume == 0)
|
||||
{
|
||||
_currentVolume = _previousVolume;
|
||||
_previousVolume = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_previousVolume = _currentVolume;
|
||||
_currentVolume = 0;
|
||||
}
|
||||
SetVolume(_currentVolume);
|
||||
}
|
||||
|
||||
public void ClearQueue() => SendCommand(new ClearCommand());
|
||||
public void PlayCommand() => SendCommand(new PlayCommand(0));
|
||||
|
||||
public void AddSong(string Uri)
|
||||
{
|
||||
SendCommand(new AddCommand(Uri));
|
||||
}
|
||||
|
||||
public void ClearAddAndPlay(string Uri)
|
||||
{
|
||||
CommandList commandList = new CommandList(new IMpcCommand<object>[] { new ClearCommand(), new AddCommand(Uri), new PlayCommand(0) });
|
||||
SendCommand(commandList);
|
||||
}
|
||||
|
||||
public async void QueryStats()
|
||||
{
|
||||
Dictionary<string, string> response = await SafelySendCommandAsync(new StatsCommand());
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
_stats.Songs = int.Parse(response["songs"]);
|
||||
_stats.Albums = int.Parse(response["albums"]);
|
||||
_stats.Artists = int.Parse(response["artists"]);
|
||||
|
||||
TimeSpan time;
|
||||
time = TimeSpan.FromSeconds(int.Parse(response["uptime"]));
|
||||
_stats.Uptime = time.ToString(@"dd\:hh\:mm\:ss");
|
||||
time = TimeSpan.FromSeconds(int.Parse(response["db_playtime"]));
|
||||
_stats.TotalPlaytime = time.ToString(@"dd\:hh\:mm\:ss");
|
||||
time = TimeSpan.FromSeconds(int.Parse(response["playtime"]));
|
||||
_stats.TotalTimePlayed = time.ToString(@"dd\:hh\:mm\:ss");
|
||||
|
||||
DateTime date = new DateTime(1970, 1, 1).AddSeconds(int.Parse(response["db_update"])).ToLocalTime();
|
||||
_stats.DatabaseUpdate = date.ToString("dd/MM/yyyy @ HH:mm");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ namespace unison
|
||||
{
|
||||
if (Properties.Settings.Default.snapcast_startup)
|
||||
{
|
||||
var mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
if (mpd.IsConnected())
|
||||
LaunchOrExit();
|
||||
}
|
||||
@ -37,8 +37,10 @@ namespace unison
|
||||
{
|
||||
if (!HasStarted && !ForceExit)
|
||||
{
|
||||
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
|
||||
_snapcast.StartInfo.FileName = Properties.Settings.Default.snapcast_path + @"\snapclient.exe";
|
||||
_snapcast.StartInfo.Arguments = $"--host {Properties.Settings.Default.mpd_host}";
|
||||
_snapcast.StartInfo.Arguments = $"--host {mpd._ipAddress}";
|
||||
_snapcast.StartInfo.CreateNoWindow = !Properties.Settings.Default.snapcast_window;
|
||||
try
|
||||
{
|
||||
@ -46,7 +48,10 @@ namespace unison
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
MessageBox.Show($"[Snapcast error]\nInvalid path: {err.Message}\n\nCurrent path: {Properties.Settings.Default.snapcast_path}\nYou can reset it in the settings if needed.",
|
||||
MessageBox.Show($"[{unison.Resources.Resources.Snapcast_Popup1}]\n" +
|
||||
$"{unison.Resources.Resources.Snapcast_Popup2} {err.Message}\n\n" +
|
||||
$"{unison.Resources.Resources.Snapcast_Popup3} {Properties.Settings.Default.snapcast_path}\n" +
|
||||
$"{unison.Resources.Resources.Snapcast_Popup4}",
|
||||
"unison", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
return;
|
||||
}
|
||||
|
2
LICENSE
@ -1,4 +1,4 @@
|
||||
MIT License Copyright (c) 2021 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
|
||||
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:
|
||||
|
178
Properties/Settings.Designer.cs
generated
@ -73,7 +73,19 @@ namespace unison.Properties {
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("snapclient_0.25.0-1_win64")]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool snapcast_window {
|
||||
get {
|
||||
return ((bool)(this["snapcast_window"]));
|
||||
}
|
||||
set {
|
||||
this["snapcast_window"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("snapclient_0.26.0-1_win64")]
|
||||
public string snapcast_path {
|
||||
get {
|
||||
return ((string)(this["snapcast_path"]));
|
||||
@ -109,13 +121,169 @@ namespace unison.Properties {
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool snapcast_window {
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("2")]
|
||||
public uint nextTrack_mod {
|
||||
get {
|
||||
return ((bool)(this["snapcast_window"]));
|
||||
return ((uint)(this["nextTrack_mod"]));
|
||||
}
|
||||
set {
|
||||
this["snapcast_window"] = value;
|
||||
this["nextTrack_mod"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("176")]
|
||||
public uint nextTrack_vk {
|
||||
get {
|
||||
return ((uint)(this["nextTrack_vk"]));
|
||||
}
|
||||
set {
|
||||
this["nextTrack_vk"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("2")]
|
||||
public uint previousTrack_mod {
|
||||
get {
|
||||
return ((uint)(this["previousTrack_mod"]));
|
||||
}
|
||||
set {
|
||||
this["previousTrack_mod"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("177")]
|
||||
public uint previousTrack_vk {
|
||||
get {
|
||||
return ((uint)(this["previousTrack_vk"]));
|
||||
}
|
||||
set {
|
||||
this["previousTrack_vk"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("2")]
|
||||
public uint playPause_mod {
|
||||
get {
|
||||
return ((uint)(this["playPause_mod"]));
|
||||
}
|
||||
set {
|
||||
this["playPause_mod"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("179")]
|
||||
public uint playPause_vk {
|
||||
get {
|
||||
return ((uint)(this["playPause_vk"]));
|
||||
}
|
||||
set {
|
||||
this["playPause_vk"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("2")]
|
||||
public uint volumeUp_mod {
|
||||
get {
|
||||
return ((uint)(this["volumeUp_mod"]));
|
||||
}
|
||||
set {
|
||||
this["volumeUp_mod"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("175")]
|
||||
public uint volumeUp_vk {
|
||||
get {
|
||||
return ((uint)(this["volumeUp_vk"]));
|
||||
}
|
||||
set {
|
||||
this["volumeUp_vk"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("2")]
|
||||
public uint volumeDown_mod {
|
||||
get {
|
||||
return ((uint)(this["volumeDown_mod"]));
|
||||
}
|
||||
set {
|
||||
this["volumeDown_mod"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("174")]
|
||||
public uint volumeDown_vk {
|
||||
get {
|
||||
return ((uint)(this["volumeDown_vk"]));
|
||||
}
|
||||
set {
|
||||
this["volumeDown_vk"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("2")]
|
||||
public uint volumeMute_mod {
|
||||
get {
|
||||
return ((uint)(this["volumeMute_mod"]));
|
||||
}
|
||||
set {
|
||||
this["volumeMute_mod"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("173")]
|
||||
public uint volumeMute_vk {
|
||||
get {
|
||||
return ((uint)(this["volumeMute_vk"]));
|
||||
}
|
||||
set {
|
||||
this["volumeMute_vk"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("3")]
|
||||
public uint showWindow_mod {
|
||||
get {
|
||||
return ((uint)(this["showWindow_mod"]));
|
||||
}
|
||||
set {
|
||||
this["showWindow_mod"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("13")]
|
||||
public uint showWindow_vk {
|
||||
get {
|
||||
return ((uint)(this["showWindow_vk"]));
|
||||
}
|
||||
set {
|
||||
this["showWindow_vk"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
<Setting Name="snapcast_path" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)">snapclient_0.25.0-1_win64</Value>
|
||||
<Value Profile="(Default)">snapclient_0.26.0-1_win64</Value>
|
||||
</Setting>
|
||||
<Setting Name="snapcast_port" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">1704</Value>
|
||||
@ -26,5 +26,47 @@
|
||||
<Setting Name="volume_offset" Type="System.Int32" Scope="User">
|
||||
<Value Profile="(Default)">5</Value>
|
||||
</Setting>
|
||||
<Setting Name="nextTrack_mod" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">2</Value>
|
||||
</Setting>
|
||||
<Setting Name="nextTrack_vk" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">176</Value>
|
||||
</Setting>
|
||||
<Setting Name="previousTrack_mod" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">2</Value>
|
||||
</Setting>
|
||||
<Setting Name="previousTrack_vk" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">177</Value>
|
||||
</Setting>
|
||||
<Setting Name="playPause_mod" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">2</Value>
|
||||
</Setting>
|
||||
<Setting Name="playPause_vk" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">179</Value>
|
||||
</Setting>
|
||||
<Setting Name="volumeUp_mod" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">2</Value>
|
||||
</Setting>
|
||||
<Setting Name="volumeUp_vk" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">175</Value>
|
||||
</Setting>
|
||||
<Setting Name="volumeDown_mod" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">2</Value>
|
||||
</Setting>
|
||||
<Setting Name="volumeDown_vk" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">174</Value>
|
||||
</Setting>
|
||||
<Setting Name="volumeMute_mod" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">2</Value>
|
||||
</Setting>
|
||||
<Setting Name="volumeMute_vk" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">173</Value>
|
||||
</Setting>
|
||||
<Setting Name="showWindow_mod" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">3</Value>
|
||||
</Setting>
|
||||
<Setting Name="showWindow_vk" Type="System.UInt32" Scope="User">
|
||||
<Value Profile="(Default)">13</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
42
README.md
@ -1,3 +1,43 @@
|
||||
# unison
|
||||
|
||||
mpd client
|
||||

|
||||
|
||||
**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
|
||||
* music control through rebindable shortcuts
|
||||
* [Snapcast](https://mjaggard.github.io/snapcast/) integration
|
||||
* Radio stations
|
||||
|
||||
## Features
|
||||
|
||||
### Window
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
### 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.
|
||||
|
||||

|
||||
|
||||
### Snapcast
|
||||
|
||||
Embedding a Snapcast client allows to listen to music on multiple devices. For example, if you music is on a distant server connected to speakers in your living room, you can still listen to it on your computer running unison with this integrated client.
|
||||
|
||||
### Radio stations
|
||||
|
||||
Through [Radio-Browser](https://www.radio-browser.info), a community database, you can play radio streams directly from unison. There are more than 28,000 stations recorded on this service, so it is a nice way to discover new music and cultures.
|
||||
|
||||

|
||||
|
||||
## Planned features
|
||||
|
||||
* A complete shuffle system based on set criteria, aka a smart playlist.
|
||||
* Playlist, queue and library management. I use other software to do it, but I will implement them at some point.
|
||||
|
||||
## Translations
|
||||
|
||||
unison is translated in English, French and Spanish. You can contribute if you want!
|
621
Resources/Resources.Designer.cs
generated
Normal file
@ -0,0 +1,621 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace unison.Resources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
public class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("unison.Resources.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
public static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Exit.
|
||||
/// </summary>
|
||||
public static string Exit {
|
||||
get {
|
||||
return ResourceManager.GetString("Exit", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Country.
|
||||
/// </summary>
|
||||
public static string Radio_Country {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_Country", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Loading stations....
|
||||
/// </summary>
|
||||
public static string Radio_Loading {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_Loading", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Name.
|
||||
/// </summary>
|
||||
public static string Radio_Name {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_Name", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No stations found!.
|
||||
/// </summary>
|
||||
public static string Radio_NotFound {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_NotFound", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reset.
|
||||
/// </summary>
|
||||
public static string Radio_Reset {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_Reset", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search.
|
||||
/// </summary>
|
||||
public static string Radio_Search {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_Search", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Search station.
|
||||
/// </summary>
|
||||
public static string Radio_SearchStation {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_SearchStation", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Tags.
|
||||
/// </summary>
|
||||
public static string Radio_Tags {
|
||||
get {
|
||||
return ResourceManager.GetString("Radio_Tags", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Radios.
|
||||
/// </summary>
|
||||
public static string Radios {
|
||||
get {
|
||||
return ResourceManager.GetString("Radios", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Settings.
|
||||
/// </summary>
|
||||
public static string Settings {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to About.
|
||||
/// </summary>
|
||||
public static string Settings_About {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_About", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to unison is a free software. It is built with the following technologies:.
|
||||
/// </summary>
|
||||
public static string Settings_AboutInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_AboutInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connect.
|
||||
/// </summary>
|
||||
public static string Settings_ConnectButton {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ConnectButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Connection.
|
||||
/// </summary>
|
||||
public static string Settings_Connection {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_Connection", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please note that since MPD passwords are not secure (they are sent in plain text to the server), there are saved as is in the setting file..
|
||||
/// </summary>
|
||||
public static string Settings_ConnectionPasswordInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ConnectionPasswordInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Status:.
|
||||
/// </summary>
|
||||
public static string Settings_ConnectionStatus {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ConnectionStatus", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to connected to MPD.
|
||||
/// </summary>
|
||||
public static string Settings_ConnectionStatusConnected {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ConnectionStatusConnected", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to connecting....
|
||||
/// </summary>
|
||||
public static string Settings_ConnectionStatusConnecting {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ConnectionStatusConnecting", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to not connected..
|
||||
/// </summary>
|
||||
public static string Settings_ConnectionStatusOffline {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ConnectionStatusOffline", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Host.
|
||||
/// </summary>
|
||||
public static string Settings_Host {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_Host", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to License.
|
||||
/// </summary>
|
||||
public static string Settings_License {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_License", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Made by.
|
||||
/// </summary>
|
||||
public static string Settings_MadeBy {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_MadeBy", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Next track.
|
||||
/// </summary>
|
||||
public static string Settings_NextTrack {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_NextTrack", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Password.
|
||||
/// </summary>
|
||||
public static string Settings_Password {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_Password", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Play / pause.
|
||||
/// </summary>
|
||||
public static string Settings_PlayPause {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_PlayPause", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Port.
|
||||
/// </summary>
|
||||
public static string Settings_Port {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_Port", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Previous track.
|
||||
/// </summary>
|
||||
public static string Settings_PreviousTrack {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_PreviousTrack", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Shortcuts.
|
||||
/// </summary>
|
||||
public static string Settings_Shortcuts {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_Shortcuts", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Please note that if the input key is not recognized, this is due to a limitation on how virtual keys work..
|
||||
/// </summary>
|
||||
public static string Settings_ShortcutsInfo {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ShortcutsInfo", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Enter a key....
|
||||
/// </summary>
|
||||
public static string Settings_ShortcutsKey {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ShortcutsKey", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show window.
|
||||
/// </summary>
|
||||
public static string Settings_ShowWindow {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_ShowWindow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You can change to your own locally installed version of the Snapcast client with an.
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastInfo1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastInfo1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to absolute .
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastInfo2 {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastInfo2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to path..
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastInfo3 {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastInfo3", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Launch at startup.
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastLauch {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastLauch", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Executable path.
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastPath {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastPath", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Port.
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastPort {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastPort", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Reset.
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastResetButton {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastResetButton", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show Snapcast window.
|
||||
/// </summary>
|
||||
public static string Settings_SnapcastWindow {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SnapcastWindow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Source code freely available.
|
||||
/// </summary>
|
||||
public static string Settings_SourceCode1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SourceCode1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to here.
|
||||
/// </summary>
|
||||
public static string Settings_SourceCode2 {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_SourceCode2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Version:.
|
||||
/// </summary>
|
||||
public static string Settings_Version {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_Version", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Volume down.
|
||||
/// </summary>
|
||||
public static string Settings_VolumeDown {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_VolumeDown", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Volume mute.
|
||||
/// </summary>
|
||||
public static string Settings_VolumeMute {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_VolumeMute", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Volume offset.
|
||||
/// </summary>
|
||||
public static string Settings_VolumeOffset {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_VolumeOffset", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Volume up.
|
||||
/// </summary>
|
||||
public static string Settings_VolumeUp {
|
||||
get {
|
||||
return ResourceManager.GetString("Settings_VolumeUp", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Show window.
|
||||
/// </summary>
|
||||
public static string ShowWindow {
|
||||
get {
|
||||
return ResourceManager.GetString("ShowWindow", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Snapcast error.
|
||||
/// </summary>
|
||||
public static string Snapcast_Popup1 {
|
||||
get {
|
||||
return ResourceManager.GetString("Snapcast_Popup1", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Invalid path:.
|
||||
/// </summary>
|
||||
public static string Snapcast_Popup2 {
|
||||
get {
|
||||
return ResourceManager.GetString("Snapcast_Popup2", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Current path:.
|
||||
/// </summary>
|
||||
public static string Snapcast_Popup3 {
|
||||
get {
|
||||
return ResourceManager.GetString("Snapcast_Popup3", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to You can reset it in the settings if needed..
|
||||
/// </summary>
|
||||
public static string Snapcast_Popup4 {
|
||||
get {
|
||||
return ResourceManager.GetString("Snapcast_Popup4", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Start Snapcast.
|
||||
/// </summary>
|
||||
public static string StartSnapcast {
|
||||
get {
|
||||
return ResourceManager.GetString("StartSnapcast", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Stats.
|
||||
/// </summary>
|
||||
public static string Stats {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Albums:.
|
||||
/// </summary>
|
||||
public static string Stats_Albums {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats_Albums", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Artists:.
|
||||
/// </summary>
|
||||
public static string Stats_Artists {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats_Artists", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Last database update:.
|
||||
/// </summary>
|
||||
public static string Stats_LastDatabaseUpdate {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats_LastDatabaseUpdate", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Songs:.
|
||||
/// </summary>
|
||||
public static string Stats_Songs {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats_Songs", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Total playtime:.
|
||||
/// </summary>
|
||||
public static string Stats_TotalPlaytime {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats_TotalPlaytime", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Total time played:.
|
||||
/// </summary>
|
||||
public static string Stats_TotalTimePlayed {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats_TotalTimePlayed", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to MPD uptime:.
|
||||
/// </summary>
|
||||
public static string Stats_Uptime {
|
||||
get {
|
||||
return ResourceManager.GetString("Stats_Uptime", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Stop Snapcast.
|
||||
/// </summary>
|
||||
public static string StopSnapcast {
|
||||
get {
|
||||
return ResourceManager.GetString("StopSnapcast", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
306
Resources/Resources.es-ES.resx
Normal file
@ -0,0 +1,306 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Exit" xml:space="preserve">
|
||||
<value>Salir</value>
|
||||
</data>
|
||||
<data name="Radios" xml:space="preserve">
|
||||
<value>Radios</value>
|
||||
</data>
|
||||
<data name="Radio_Country" xml:space="preserve">
|
||||
<value>Países</value>
|
||||
</data>
|
||||
<data name="Radio_Loading" xml:space="preserve">
|
||||
<value>Cargando estaciones...</value>
|
||||
</data>
|
||||
<data name="Radio_Name" xml:space="preserve">
|
||||
<value>Nombre</value>
|
||||
</data>
|
||||
<data name="Radio_NotFound" xml:space="preserve">
|
||||
<value>¡No estaciones encontradas!</value>
|
||||
</data>
|
||||
<data name="Radio_Reset" xml:space="preserve">
|
||||
<value>Reinicializar</value>
|
||||
</data>
|
||||
<data name="Radio_Search" xml:space="preserve">
|
||||
<value>Búsqueda</value>
|
||||
</data>
|
||||
<data name="Radio_SearchStation" xml:space="preserve">
|
||||
<value>Buscar estación</value>
|
||||
</data>
|
||||
<data name="Radio_Tags" xml:space="preserve">
|
||||
<value>Tags</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Ajustes</value>
|
||||
</data>
|
||||
<data name="Settings_About" xml:space="preserve">
|
||||
<value>Acerca de</value>
|
||||
</data>
|
||||
<data name="Settings_AboutInfo" xml:space="preserve">
|
||||
<value>unison es un software libre. Es construido con las siguientes tecnologías:</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectButton" xml:space="preserve">
|
||||
<value>Conexión</value>
|
||||
</data>
|
||||
<data name="Settings_Connection" xml:space="preserve">
|
||||
<value>Conexión</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionPasswordInfo" xml:space="preserve">
|
||||
<value>Tenga en cuenta que, dado que las contraseñas de MPD no son seguras (se envían en texto sin formato al servidor), se guardan tal cual en el archivo de configuración.</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatus" xml:space="preserve">
|
||||
<value>Estado:</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusConnected" xml:space="preserve">
|
||||
<value>conectado a MPD</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusConnecting" xml:space="preserve">
|
||||
<value>conectando...</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusOffline" xml:space="preserve">
|
||||
<value>no conectado.</value>
|
||||
</data>
|
||||
<data name="Settings_Host" xml:space="preserve">
|
||||
<value>Host</value>
|
||||
</data>
|
||||
<data name="Settings_License" xml:space="preserve">
|
||||
<value>Licencia</value>
|
||||
</data>
|
||||
<data name="Settings_MadeBy" xml:space="preserve">
|
||||
<value>Creado por</value>
|
||||
</data>
|
||||
<data name="Settings_NextTrack" xml:space="preserve">
|
||||
<value>Pista siguiente</value>
|
||||
</data>
|
||||
<data name="Settings_Password" xml:space="preserve">
|
||||
<value>Contraseña</value>
|
||||
</data>
|
||||
<data name="Settings_PlayPause" xml:space="preserve">
|
||||
<value>Jugar / pausa</value>
|
||||
</data>
|
||||
<data name="Settings_Port" xml:space="preserve">
|
||||
<value>Puerto</value>
|
||||
</data>
|
||||
<data name="Settings_PreviousTrack" xml:space="preserve">
|
||||
<value>Pista precedente</value>
|
||||
</data>
|
||||
<data name="Settings_Shortcuts" xml:space="preserve">
|
||||
<value>Atajos</value>
|
||||
</data>
|
||||
<data name="Settings_ShortcutsInfo" xml:space="preserve">
|
||||
<value>Tenga en cuenta que si no está reconocida la tecla, esto se debe a una limitación en el funcionamiento de las teclas virtuales.</value>
|
||||
</data>
|
||||
<data name="Settings_ShortcutsKey" xml:space="preserve">
|
||||
<value>Entre una tecla...</value>
|
||||
</data>
|
||||
<data name="Settings_ShowWindow" xml:space="preserve">
|
||||
<value>Mostrar ventana</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo1" xml:space="preserve">
|
||||
<value>Puede cambiar a su propia versión instalada localmente del cliente Snapcast con una ruta</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo2" xml:space="preserve">
|
||||
<value> absoluta</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo3" xml:space="preserve">
|
||||
<value>.</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastLauch" xml:space="preserve">
|
||||
<value>Lanzar al inicio</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastPath" xml:space="preserve">
|
||||
<value>Ruta del ejecutable</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastPort" xml:space="preserve">
|
||||
<value>Puerto</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastResetButton" xml:space="preserve">
|
||||
<value>Reinicializar</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastWindow" xml:space="preserve">
|
||||
<value>Mostrar ventana de Snapcast</value>
|
||||
</data>
|
||||
<data name="Settings_SourceCode1" xml:space="preserve">
|
||||
<value>Código fuente disponible gratuitamente</value>
|
||||
</data>
|
||||
<data name="Settings_SourceCode2" xml:space="preserve">
|
||||
<value>aquí</value>
|
||||
</data>
|
||||
<data name="Settings_Version" xml:space="preserve">
|
||||
<value>Versión:</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeDown" xml:space="preserve">
|
||||
<value>Bajar volumen</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeMute" xml:space="preserve">
|
||||
<value>Volumen mudo</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeOffset" xml:space="preserve">
|
||||
<value>Intervalo de volumen</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeUp" xml:space="preserve">
|
||||
<value>Subir volumen</value>
|
||||
</data>
|
||||
<data name="ShowWindow" xml:space="preserve">
|
||||
<value>Mostrar ventana</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup1" xml:space="preserve">
|
||||
<value>Error Snapcast</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup2" xml:space="preserve">
|
||||
<value>Ruta no válida:</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup3" xml:space="preserve">
|
||||
<value>Ruta actual:</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup4" xml:space="preserve">
|
||||
<value>Puede restablecerlo en la configuración si es necesario.</value>
|
||||
</data>
|
||||
<data name="StartSnapcast" xml:space="preserve">
|
||||
<value>Iniciar Snapcast</value>
|
||||
</data>
|
||||
<data name="Stats" xml:space="preserve">
|
||||
<value>Estadísticas</value>
|
||||
</data>
|
||||
<data name="Stats_Albums" xml:space="preserve">
|
||||
<value>Álbumes:</value>
|
||||
</data>
|
||||
<data name="Stats_Artists" xml:space="preserve">
|
||||
<value>Artistas:</value>
|
||||
</data>
|
||||
<data name="Stats_LastDatabaseUpdate" xml:space="preserve">
|
||||
<value>Última actualización de la base de datos:</value>
|
||||
</data>
|
||||
<data name="Stats_Songs" xml:space="preserve">
|
||||
<value>Canciones:</value>
|
||||
</data>
|
||||
<data name="Stats_TotalPlaytime" xml:space="preserve">
|
||||
<value>Tiempo de juego total:</value>
|
||||
</data>
|
||||
<data name="Stats_TotalTimePlayed" xml:space="preserve">
|
||||
<value>Tiempo total jugado:</value>
|
||||
</data>
|
||||
<data name="Stats_Uptime" xml:space="preserve">
|
||||
<value>Tiempo de actividad de MPD:</value>
|
||||
</data>
|
||||
<data name="StopSnapcast" xml:space="preserve">
|
||||
<value>Parar Snapcast</value>
|
||||
</data>
|
||||
</root>
|
306
Resources/Resources.fr-FR.resx
Normal file
@ -0,0 +1,306 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Exit" xml:space="preserve">
|
||||
<value>Quitter</value>
|
||||
</data>
|
||||
<data name="Radios" xml:space="preserve">
|
||||
<value>Radios</value>
|
||||
</data>
|
||||
<data name="Radio_Country" xml:space="preserve">
|
||||
<value>Pays</value>
|
||||
</data>
|
||||
<data name="Radio_Loading" xml:space="preserve">
|
||||
<value>Recherche de stations...</value>
|
||||
</data>
|
||||
<data name="Radio_Name" xml:space="preserve">
|
||||
<value>Nom</value>
|
||||
</data>
|
||||
<data name="Radio_NotFound" xml:space="preserve">
|
||||
<value>Aucune station trouvée !</value>
|
||||
</data>
|
||||
<data name="Radio_Reset" xml:space="preserve">
|
||||
<value>Réinitialiser</value>
|
||||
</data>
|
||||
<data name="Radio_Search" xml:space="preserve">
|
||||
<value>Chercher</value>
|
||||
</data>
|
||||
<data name="Radio_SearchStation" xml:space="preserve">
|
||||
<value>Recherche de station</value>
|
||||
</data>
|
||||
<data name="Radio_Tags" xml:space="preserve">
|
||||
<value>Tags</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Configuration</value>
|
||||
</data>
|
||||
<data name="Settings_About" xml:space="preserve">
|
||||
<value>À propos</value>
|
||||
</data>
|
||||
<data name="Settings_AboutInfo" xml:space="preserve">
|
||||
<value>unison est un logiciel libre. Il est développé avec les technologies suivantes :</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectButton" xml:space="preserve">
|
||||
<value>Connexion</value>
|
||||
</data>
|
||||
<data name="Settings_Connection" xml:space="preserve">
|
||||
<value>Connexion</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionPasswordInfo" xml:space="preserve">
|
||||
<value>Veuillez noter que comme les mots de passe de MPD ne sont pas sécurisés (ils sont envoyés en clair au serveur), ils sont sauvegardés tels quels.</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatus" xml:space="preserve">
|
||||
<value>Statut :</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusConnected" xml:space="preserve">
|
||||
<value>connecté à MPD</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusConnecting" xml:space="preserve">
|
||||
<value>connexion en cours...</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusOffline" xml:space="preserve">
|
||||
<value>non connecté.</value>
|
||||
</data>
|
||||
<data name="Settings_Host" xml:space="preserve">
|
||||
<value>Hôte</value>
|
||||
</data>
|
||||
<data name="Settings_License" xml:space="preserve">
|
||||
<value>Licence</value>
|
||||
</data>
|
||||
<data name="Settings_MadeBy" xml:space="preserve">
|
||||
<value>Créé par</value>
|
||||
</data>
|
||||
<data name="Settings_NextTrack" xml:space="preserve">
|
||||
<value>Piste suivante</value>
|
||||
</data>
|
||||
<data name="Settings_Password" xml:space="preserve">
|
||||
<value>Mot de passe</value>
|
||||
</data>
|
||||
<data name="Settings_PlayPause" xml:space="preserve">
|
||||
<value>Jouer / pause</value>
|
||||
</data>
|
||||
<data name="Settings_Port" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
<data name="Settings_PreviousTrack" xml:space="preserve">
|
||||
<value>Piste précédente</value>
|
||||
</data>
|
||||
<data name="Settings_Shortcuts" xml:space="preserve">
|
||||
<value>Raccourcis</value>
|
||||
</data>
|
||||
<data name="Settings_ShortcutsInfo" xml:space="preserve">
|
||||
<value>Veuillez noter que si votre touche n'est pas reconnue, cela est du à la manière dont les touches virtuelles sont gérées par Windows.</value>
|
||||
</data>
|
||||
<data name="Settings_ShortcutsKey" xml:space="preserve">
|
||||
<value>Appuyez sur une touche...</value>
|
||||
</data>
|
||||
<data name="Settings_ShowWindow" xml:space="preserve">
|
||||
<value>Afficher la fenêtre</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo1" xml:space="preserve">
|
||||
<value>Il est possible de mettre votre version localement installé de Snapcast avec un </value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo2" xml:space="preserve">
|
||||
<value>chemin absolu</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo3" xml:space="preserve">
|
||||
<value>.</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastLauch" xml:space="preserve">
|
||||
<value>Lancer au démarrage</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastPath" xml:space="preserve">
|
||||
<value>Chemin de l'exécutable</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastPort" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastResetButton" xml:space="preserve">
|
||||
<value>Réinitialiser</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastWindow" xml:space="preserve">
|
||||
<value>Afficher la fenêtre de Snapcast</value>
|
||||
</data>
|
||||
<data name="Settings_SourceCode1" xml:space="preserve">
|
||||
<value>Code source librement disponible</value>
|
||||
</data>
|
||||
<data name="Settings_SourceCode2" xml:space="preserve">
|
||||
<value>ici</value>
|
||||
</data>
|
||||
<data name="Settings_Version" xml:space="preserve">
|
||||
<value>Version :</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeDown" xml:space="preserve">
|
||||
<value>Baisse de volume</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeMute" xml:space="preserve">
|
||||
<value>Volume en sourdine</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeOffset" xml:space="preserve">
|
||||
<value>Écart de volume</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeUp" xml:space="preserve">
|
||||
<value>Augmentation de volume</value>
|
||||
</data>
|
||||
<data name="ShowWindow" xml:space="preserve">
|
||||
<value>Montrer la fenêtre</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup1" xml:space="preserve">
|
||||
<value>Erreur Snapcast</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup2" xml:space="preserve">
|
||||
<value>Chemin invalide :</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup3" xml:space="preserve">
|
||||
<value>Chemin actuel :</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup4" xml:space="preserve">
|
||||
<value>Vous pouvez le réinitialiser dans la configuration.</value>
|
||||
</data>
|
||||
<data name="StartSnapcast" xml:space="preserve">
|
||||
<value>Démarrer Snapcast</value>
|
||||
</data>
|
||||
<data name="Stats" xml:space="preserve">
|
||||
<value>Stats</value>
|
||||
</data>
|
||||
<data name="Stats_Albums" xml:space="preserve">
|
||||
<value>Albums :</value>
|
||||
</data>
|
||||
<data name="Stats_Artists" xml:space="preserve">
|
||||
<value>Artistes :</value>
|
||||
</data>
|
||||
<data name="Stats_LastDatabaseUpdate" xml:space="preserve">
|
||||
<value>Mise à jour de la base de données :</value>
|
||||
</data>
|
||||
<data name="Stats_Songs" xml:space="preserve">
|
||||
<value>Morceaux :</value>
|
||||
</data>
|
||||
<data name="Stats_TotalPlaytime" xml:space="preserve">
|
||||
<value>Temps total :</value>
|
||||
</data>
|
||||
<data name="Stats_TotalTimePlayed" xml:space="preserve">
|
||||
<value>Temps d'écoute écoulé :</value>
|
||||
</data>
|
||||
<data name="Stats_Uptime" xml:space="preserve">
|
||||
<value>MPD lancé depuis : </value>
|
||||
</data>
|
||||
<data name="StopSnapcast" xml:space="preserve">
|
||||
<value>Stopper Snapcast</value>
|
||||
</data>
|
||||
</root>
|
306
Resources/Resources.resx
Normal file
@ -0,0 +1,306 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Exit" xml:space="preserve">
|
||||
<value>Exit</value>
|
||||
</data>
|
||||
<data name="Radios" xml:space="preserve">
|
||||
<value>Radios</value>
|
||||
</data>
|
||||
<data name="Radio_Country" xml:space="preserve">
|
||||
<value>Country</value>
|
||||
</data>
|
||||
<data name="Radio_Loading" xml:space="preserve">
|
||||
<value>Loading stations...</value>
|
||||
</data>
|
||||
<data name="Radio_Name" xml:space="preserve">
|
||||
<value>Name</value>
|
||||
</data>
|
||||
<data name="Radio_NotFound" xml:space="preserve">
|
||||
<value>No stations found!</value>
|
||||
</data>
|
||||
<data name="Radio_Reset" xml:space="preserve">
|
||||
<value>Reset</value>
|
||||
</data>
|
||||
<data name="Radio_Search" xml:space="preserve">
|
||||
<value>Search</value>
|
||||
</data>
|
||||
<data name="Radio_SearchStation" xml:space="preserve">
|
||||
<value>Search station</value>
|
||||
</data>
|
||||
<data name="Radio_Tags" xml:space="preserve">
|
||||
<value>Tags</value>
|
||||
</data>
|
||||
<data name="Settings" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="Settings_About" xml:space="preserve">
|
||||
<value>About</value>
|
||||
</data>
|
||||
<data name="Settings_AboutInfo" xml:space="preserve">
|
||||
<value>unison is a free software. It is built with the following technologies:</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectButton" xml:space="preserve">
|
||||
<value>Connect</value>
|
||||
</data>
|
||||
<data name="Settings_Connection" xml:space="preserve">
|
||||
<value>Connection</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionPasswordInfo" xml:space="preserve">
|
||||
<value>Please note that since MPD passwords are not secure (they are sent in plain text to the server), there are saved as is in the setting file.</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatus" xml:space="preserve">
|
||||
<value>Status:</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusConnected" xml:space="preserve">
|
||||
<value>connected to MPD</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusConnecting" xml:space="preserve">
|
||||
<value>connecting...</value>
|
||||
</data>
|
||||
<data name="Settings_ConnectionStatusOffline" xml:space="preserve">
|
||||
<value>not connected.</value>
|
||||
</data>
|
||||
<data name="Settings_Host" xml:space="preserve">
|
||||
<value>Host</value>
|
||||
</data>
|
||||
<data name="Settings_License" xml:space="preserve">
|
||||
<value>License</value>
|
||||
</data>
|
||||
<data name="Settings_MadeBy" xml:space="preserve">
|
||||
<value>Made by</value>
|
||||
</data>
|
||||
<data name="Settings_NextTrack" xml:space="preserve">
|
||||
<value>Next track</value>
|
||||
</data>
|
||||
<data name="Settings_Password" xml:space="preserve">
|
||||
<value>Password</value>
|
||||
</data>
|
||||
<data name="Settings_PlayPause" xml:space="preserve">
|
||||
<value>Play / pause</value>
|
||||
</data>
|
||||
<data name="Settings_Port" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
<data name="Settings_PreviousTrack" xml:space="preserve">
|
||||
<value>Previous track</value>
|
||||
</data>
|
||||
<data name="Settings_Shortcuts" xml:space="preserve">
|
||||
<value>Shortcuts</value>
|
||||
</data>
|
||||
<data name="Settings_ShortcutsInfo" xml:space="preserve">
|
||||
<value>Please note that if the input key is not recognized, this is due to a limitation on how virtual keys work.</value>
|
||||
</data>
|
||||
<data name="Settings_ShortcutsKey" xml:space="preserve">
|
||||
<value>Enter a key...</value>
|
||||
</data>
|
||||
<data name="Settings_ShowWindow" xml:space="preserve">
|
||||
<value>Show window</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo1" xml:space="preserve">
|
||||
<value>You can change to your own locally installed version of the Snapcast client with an</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo2" xml:space="preserve">
|
||||
<value> absolute </value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastInfo3" xml:space="preserve">
|
||||
<value>path.</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastLauch" xml:space="preserve">
|
||||
<value>Launch at startup</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastPath" xml:space="preserve">
|
||||
<value>Executable path</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastPort" xml:space="preserve">
|
||||
<value>Port</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastResetButton" xml:space="preserve">
|
||||
<value>Reset</value>
|
||||
</data>
|
||||
<data name="Settings_SnapcastWindow" xml:space="preserve">
|
||||
<value>Show Snapcast window</value>
|
||||
</data>
|
||||
<data name="Settings_SourceCode1" xml:space="preserve">
|
||||
<value>Source code freely available</value>
|
||||
</data>
|
||||
<data name="Settings_SourceCode2" xml:space="preserve">
|
||||
<value>here</value>
|
||||
</data>
|
||||
<data name="Settings_Version" xml:space="preserve">
|
||||
<value>Version:</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeDown" xml:space="preserve">
|
||||
<value>Volume down</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeMute" xml:space="preserve">
|
||||
<value>Volume mute</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeOffset" xml:space="preserve">
|
||||
<value>Volume offset</value>
|
||||
</data>
|
||||
<data name="Settings_VolumeUp" xml:space="preserve">
|
||||
<value>Volume up</value>
|
||||
</data>
|
||||
<data name="ShowWindow" xml:space="preserve">
|
||||
<value>Show window</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup1" xml:space="preserve">
|
||||
<value>Snapcast error</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup2" xml:space="preserve">
|
||||
<value>Invalid path:</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup3" xml:space="preserve">
|
||||
<value>Current path:</value>
|
||||
</data>
|
||||
<data name="Snapcast_Popup4" xml:space="preserve">
|
||||
<value>You can reset it in the settings if needed.</value>
|
||||
</data>
|
||||
<data name="StartSnapcast" xml:space="preserve">
|
||||
<value>Start Snapcast</value>
|
||||
</data>
|
||||
<data name="Stats" xml:space="preserve">
|
||||
<value>Stats</value>
|
||||
</data>
|
||||
<data name="Stats_Albums" xml:space="preserve">
|
||||
<value>Albums:</value>
|
||||
</data>
|
||||
<data name="Stats_Artists" xml:space="preserve">
|
||||
<value>Artists:</value>
|
||||
</data>
|
||||
<data name="Stats_LastDatabaseUpdate" xml:space="preserve">
|
||||
<value>Last database update:</value>
|
||||
</data>
|
||||
<data name="Stats_Songs" xml:space="preserve">
|
||||
<value>Songs:</value>
|
||||
</data>
|
||||
<data name="Stats_TotalPlaytime" xml:space="preserve">
|
||||
<value>Total playtime:</value>
|
||||
</data>
|
||||
<data name="Stats_TotalTimePlayed" xml:space="preserve">
|
||||
<value>Total time played:</value>
|
||||
</data>
|
||||
<data name="Stats_Uptime" xml:space="preserve">
|
||||
<value>MPD uptime:</value>
|
||||
</data>
|
||||
<data name="StopSnapcast" xml:space="preserve">
|
||||
<value>Stop Snapcast</value>
|
||||
</data>
|
||||
</root>
|
28
Resources/Resources.xaml
Normal file
@ -0,0 +1,28 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:sys="clr-namespace:System;assembly=System.Runtime">
|
||||
|
||||
<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="playButton"></system:String>
|
||||
<system:String x:Key="pauseButton"></system:String>
|
||||
|
||||
<system:String x:Key="volume_offset">5</system:String>
|
||||
|
||||
<system:UInt32 x:Key="nextTrack_mod">2</system:UInt32>
|
||||
<system:UInt32 x:Key="nextTrack_vk">176</system:UInt32>
|
||||
<system:UInt32 x:Key="previousTrack_mod">2</system:UInt32>
|
||||
<system:UInt32 x:Key="previousTrack_vk">177</system:UInt32>
|
||||
<system:UInt32 x:Key="playPause_mod">2</system:UInt32>
|
||||
<system:UInt32 x:Key="playPause_vk">179</system:UInt32>
|
||||
<system:UInt32 x:Key="volumeUp_mod">2</system:UInt32>
|
||||
<system:UInt32 x:Key="volumeUp_vk">175</system:UInt32>
|
||||
<system:UInt32 x:Key="volumeDown_mod">2</system:UInt32>
|
||||
<system:UInt32 x:Key="volumeDown_vk">174</system:UInt32>
|
||||
<system:UInt32 x:Key="volumeMute_mod">2</system:UInt32>
|
||||
<system:UInt32 x:Key="volumeMute_vk">173</system:UInt32>
|
||||
<system:UInt32 x:Key="showWindow_mod">3</system:UInt32>
|
||||
<system:UInt32 x:Key="showWindow_vk">13</system:UInt32>
|
||||
|
||||
</ResourceDictionary>
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 294 KiB |
BIN
Resources/nocover.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
Resources/nothing.png
Normal file
After Width: | Height: | Size: 369 B |
BIN
Resources/radio.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
Screenshots/screen1.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
Screenshots/screen2.png
Normal file
After Width: | Height: | Size: 34 KiB |
BIN
Screenshots/screen3.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
Screenshots/screen4.png
Normal file
After Width: | Height: | Size: 21 KiB |
@ -4,9 +4,9 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
|
||||
xmlns:properties="clr-namespace:unison.Resources"
|
||||
mc:Ignorable="d"
|
||||
Title="unison"
|
||||
Closing="Window_Closing" Icon="/images/icon-full.ico" ResizeMode="CanMinimize" SizeToContent="WidthAndHeight">
|
||||
Title="unison" Closing="Window_Closing" Icon="/Resources/icon-full.ico" ResizeMode="CanMinimize" SizeToContent="WidthAndHeight">
|
||||
|
||||
<Window.Resources>
|
||||
<Style TargetType="Border" x:Key="UnselectedButton">
|
||||
@ -28,7 +28,7 @@
|
||||
</GroupBox.Header>
|
||||
<Grid>
|
||||
<Grid x:Name="CurrentSong" Margin="10,0,10,0" VerticalAlignment="Top" MinHeight="80">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center">
|
||||
<StackPanel Orientation="Vertical" VerticalAlignment="Center" MouseDown="MouseDownClipboard">
|
||||
<TextBlock x:Name="SongTitle" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Normal" FontSize="20" Text="Title"/>
|
||||
<TextBlock x:Name="SongArtist" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Bold" FontSize="18" Text="Artist"/>
|
||||
<TextBlock x:Name="SongAlbum" TextWrapping="Wrap" TextAlignment="Center" FontWeight="Normal" FontSize="16" Text="Album"/>
|
||||
@ -103,20 +103,38 @@
|
||||
<StackPanel.OpacityMask>
|
||||
<VisualBrush Visual="{Binding ElementName=mask}"/>
|
||||
</StackPanel.OpacityMask>
|
||||
<Image x:Name="Cover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/images/nocover.png" Visibility="Collapsed" />
|
||||
<Image x:Name="NoCover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/images/nocover.png" />
|
||||
<Image x:Name="Cover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/Resources/nocover.png" Visibility="Collapsed" />
|
||||
<Image x:Name="NoCover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/Resources/nocover.png" Visibility="Collapsed" />
|
||||
<Image x:Name="RadioCover" HorizontalAlignment="Center" VerticalAlignment="Center" Source="/Resources/radio.png" Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
<Grid x:Name="BottomLayout" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}" Width="Auto" MinHeight="40">
|
||||
<Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="10,0,0,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<emoji:TextBlock Text="🔊" Padding="0,0,0,2"/>
|
||||
<TextBlock x:Name="SnapcastText" Text="Start Snapcast" Margin="5, 0, 0, 0"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<TextBlock x:Name="Connection" HorizontalAlignment="Center" Text="Not connected" TextWrapping="Wrap" VerticalAlignment="Center" TextAlignment="Center" MinWidth="350" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}"/>
|
||||
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
|
||||
<Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="10,0,0,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<emoji:TextBlock Text="🔊" Padding="0,0,0,2"/>
|
||||
<TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
<Button x:Name="Radio" Padding="5, 2" HorizontalAlignment="Left" Click="Radios_Clicked" Margin="5,0,10,0" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<emoji:TextBlock Text="📻" Padding="0,0,0,2"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Radios}" Margin="5, 0, 0, 0"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<Grid x:Name="ConnectionOkIcon" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" VerticalAlignment="Center" HorizontalAlignment="Center" />
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" VerticalAlignment="Center" HorizontalAlignment="Center" />
|
||||
</Grid>
|
||||
<Grid x:Name="ConnectionFailIcon" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<TextBlock FontFamily="Segoe MDL2 Assets" Text="" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" VerticalAlignment="Center" HorizontalAlignment="Center" />
|
||||
</Grid>
|
||||
<TextBlock x:Name="Connection" HorizontalAlignment="Center" Text="Not connected" TextWrapping="Wrap" VerticalAlignment="Top" TextAlignment="Center" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" Margin="5,0,0,0" />
|
||||
</StackPanel>
|
||||
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
|
||||
<!--<Button x:Name="Shuffle" Padding="5, 2" HorizontalAlignment="Right" Margin="0,0,10,0" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
@ -127,7 +145,7 @@
|
||||
<Button x:Name="Settings" Padding="5, 2" Click="Settings_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<emoji:TextBlock Text="🛠️" Padding="0,0,0,2"/>
|
||||
<TextBlock Text="Settings" Margin="5, 0, 0, 0"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings}" Margin="5, 0, 0, 0"/>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
@ -12,6 +12,7 @@ namespace unison
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
private readonly Settings _settingsWin;
|
||||
private readonly Radios _radiosWin;
|
||||
private readonly DispatcherTimer _timer;
|
||||
private readonly MPDHandler _mpd;
|
||||
|
||||
@ -19,9 +20,11 @@ namespace unison
|
||||
{
|
||||
InitHwnd();
|
||||
InitializeComponent();
|
||||
DefaultState(true);
|
||||
WindowState = WindowState.Minimized;
|
||||
|
||||
_settingsWin = new Settings();
|
||||
_radiosWin = new Radios();
|
||||
_timer = new DispatcherTimer();
|
||||
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
|
||||
@ -41,10 +44,27 @@ namespace unison
|
||||
|
||||
public void OnConnectionChanged(object sender, EventArgs e)
|
||||
{
|
||||
Connection.Text = (_mpd.IsConnected() ? "✔️" : "❌") + $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
|
||||
_settingsWin.UpdateConnectionStatus();
|
||||
if (_mpd.IsConnected())
|
||||
{
|
||||
ConnectionOkIcon.Visibility = Visibility.Visible;
|
||||
ConnectionFailIcon.Visibility = Visibility.Collapsed;
|
||||
|
||||
Snapcast.IsEnabled = true;
|
||||
if (_radiosWin.IsConnected())
|
||||
Radio.IsEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_timer.Stop();
|
||||
DefaultState(true);
|
||||
ConnectionOkIcon.Visibility = Visibility.Collapsed;
|
||||
ConnectionFailIcon.Visibility = Visibility.Visible;
|
||||
|
||||
Snapcast.IsEnabled = false;
|
||||
Radio.IsEnabled = false;
|
||||
}
|
||||
_settingsWin.UpdateConnectionStatus();
|
||||
Connection.Text = $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
|
||||
}
|
||||
|
||||
public void OnSongChanged(object sender, EventArgs e)
|
||||
@ -56,11 +76,12 @@ namespace unison
|
||||
SongTitle.Text = _mpd.GetCurrentSong().Title;
|
||||
else if (_mpd.GetCurrentSong().HasName && _mpd.GetCurrentSong().Name.Length > 0)
|
||||
SongTitle.Text = _mpd.GetCurrentSong().Name;
|
||||
else
|
||||
else if (_mpd.GetCurrentSong().Path != null)
|
||||
{
|
||||
int start = _mpd.GetCurrentSong().Path.LastIndexOf("/") + 1;
|
||||
int end = _mpd.GetCurrentSong().Path.LastIndexOf(".");
|
||||
SongTitle.Text = _mpd.GetCurrentSong().Path.Substring(start, end - start);
|
||||
if (start > 0 && end > 0 && end > start)
|
||||
SongTitle.Text = _mpd.GetCurrentSong().Path.Substring(start, end - start);
|
||||
}
|
||||
|
||||
SongTitle.ToolTip = _mpd.GetCurrentSong().Path;
|
||||
@ -68,7 +89,7 @@ namespace unison
|
||||
SongAlbum.Text = _mpd.GetCurrentSong().Album;
|
||||
|
||||
if (_mpd.GetCurrentSong().Date != null)
|
||||
SongAlbum.Text += $" ({ _mpd.GetCurrentSong().Date})";
|
||||
SongAlbum.Text += $" ({_mpd.GetCurrentSong().Date.Split("-")[0]})";
|
||||
|
||||
SongGenre.Text = _mpd.GetCurrentSong().Genre;
|
||||
SongFormat.Text = _mpd.GetCurrentSong().Path.Substring(_mpd.GetCurrentSong().Path.LastIndexOf(".") + 1);
|
||||
@ -111,18 +132,21 @@ namespace unison
|
||||
UpdateButton(ref BorderConsume, _mpd.GetStatus().Consume);
|
||||
|
||||
if (_mpd.IsPlaying())
|
||||
PlayPause.Text = "\xedb4";
|
||||
PlayPause.Text = (string)Application.Current.FindResource("playButton");
|
||||
else
|
||||
{
|
||||
PlayPause.Text = "\xedb5";
|
||||
PlayPause.Text = (string)Application.Current.FindResource("pauseButton");
|
||||
if (_mpd.GetStatus().State == MpcNET.MpdState.Stop)
|
||||
{
|
||||
DefaultState();
|
||||
}
|
||||
}
|
||||
|
||||
_mpd.QueryStats();
|
||||
_settingsWin.UpdateStats();
|
||||
}
|
||||
|
||||
private void DefaultState()
|
||||
private void DefaultState(bool LostConnection = false)
|
||||
{
|
||||
SongTitle.Text = "";
|
||||
SongArtist.Text = "";
|
||||
@ -132,24 +156,35 @@ namespace unison
|
||||
SongFormat.Text = "";
|
||||
CurrentTime.Text = "";
|
||||
EndTime.Text = "";
|
||||
PlayPause.Text = (string)Application.Current.FindResource("pauseButton");
|
||||
TimeSlider.Value = 50;
|
||||
TimeSlider.IsEnabled = false;
|
||||
NoCover.Visibility = Visibility.Visible;
|
||||
NoCover.Visibility = Visibility.Collapsed;
|
||||
Cover.Visibility = Visibility.Collapsed;
|
||||
RadioCover.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (LostConnection)
|
||||
{
|
||||
ConnectionOkIcon.Visibility = Visibility.Collapsed;
|
||||
ConnectionFailIcon.Visibility = Visibility.Visible;
|
||||
}
|
||||
Connection.Text = $"{Properties.Settings.Default.mpd_host}:{Properties.Settings.Default.mpd_port}";
|
||||
}
|
||||
|
||||
public void OnCoverChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_mpd.GetCover() == null)
|
||||
{
|
||||
NoCover.Visibility = Visibility.Collapsed;
|
||||
Cover.Visibility = Visibility.Collapsed;
|
||||
RadioCover.Visibility = Visibility.Collapsed;
|
||||
|
||||
if (_mpd.GetCurrentSong().Time == -1)
|
||||
RadioCover.Visibility = Visibility.Visible;
|
||||
else if (_mpd.GetCover() == null)
|
||||
NoCover.Visibility = Visibility.Visible;
|
||||
Cover.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
else if (Cover.Source != _mpd.GetCover())
|
||||
{
|
||||
Cover.Source = _mpd.GetCover();
|
||||
Cover.Visibility = Visibility.Visible;
|
||||
NoCover.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,9 +192,9 @@ namespace unison
|
||||
{
|
||||
SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"];
|
||||
if (snapcast.HasStarted)
|
||||
SnapcastText.Text = "Stop Snapcast";
|
||||
SnapcastText.Text = unison.Resources.Resources.StopSnapcast;
|
||||
else
|
||||
SnapcastText.Text = "Start Snapcast";
|
||||
SnapcastText.Text = unison.Resources.Resources.StartSnapcast;
|
||||
}
|
||||
|
||||
public void UpdateButton(ref Border border, bool b)
|
||||
@ -194,6 +229,15 @@ namespace unison
|
||||
snapcast.LaunchOrExit();
|
||||
}
|
||||
|
||||
public void Radios_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_radiosWin.Show();
|
||||
_radiosWin.Activate();
|
||||
|
||||
if (_radiosWin.WindowState == WindowState.Minimized)
|
||||
_radiosWin.WindowState = WindowState.Normal;
|
||||
}
|
||||
|
||||
public void Settings_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
_settingsWin.Show();
|
||||
@ -234,6 +278,17 @@ namespace unison
|
||||
hk.Activate(this);
|
||||
}
|
||||
|
||||
private void MouseDownClipboard(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ClickCount == 2)
|
||||
{
|
||||
string CopyText = SongTitle.Text + " - " + SongArtist.Text + "\n";
|
||||
CopyText += SongAlbum.Text + "\n";
|
||||
CopyText += SongTitle.ToolTip;
|
||||
Clipboard.SetText(CopyText);
|
||||
}
|
||||
}
|
||||
|
||||
public void InitHwnd()
|
||||
{
|
||||
WindowInteropHelper helper = new(this);
|
||||
|
74
Views/Radios.xaml
Normal file
@ -0,0 +1,74 @@
|
||||
<Window x:Class="unison.Radios"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
|
||||
xmlns:properties="clr-namespace:unison.Resources"
|
||||
mc:Ignorable="d"
|
||||
Title="Radios" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
|
||||
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,0">
|
||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||
<GroupBox.Header>
|
||||
<TextBlock>
|
||||
<emoji:EmojiInline Text="📻"/>
|
||||
<Run Text="{x:Static properties:Resources.Radio_SearchStation}"/>
|
||||
</TextBlock>
|
||||
</GroupBox.Header>
|
||||
<StackPanel Orientation="Vertical" Margin="5,0,5,0">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock Text="{x:Static properties:Resources.Radio_Name}" Margin="0,0,0,5"/>
|
||||
<TextBox x:Name="NameSearch" KeyDown="SearchHandler" Width="200" Margin="0,4,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
|
||||
<TextBlock Text="{x:Static properties:Resources.Radio_Tags}" Margin="0,0,0,5"/>
|
||||
<TextBox x:Name="TagSearch" KeyDown="SearchHandler" Width="300" Margin="0,4,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Vertical" Margin="20,0,0,0">
|
||||
<TextBlock Text="{x:Static properties:Resources.Radio_Country}" Margin="0,0,0,5"/>
|
||||
<ComboBox x:Name="CountryList" SelectedIndex="0" KeyDown="SearchHandler" Width="240" ScrollViewer.CanContentScroll="False"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<Button Content="{x:Static properties:Resources.Radio_Search}" Click="Search_Clicked" Padding="5, 2"/>
|
||||
<Button Content="{x:Static properties:Resources.Radio_Reset}" Click="Reset_Clicked" Margin="10,0,0,0" Padding="5, 2"/>
|
||||
<TextBlock x:Name="SearchStatus" Margin="15,1,0,0" FontStyle="Italic" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Margin="5,10,5,5" MaxHeight="600" MinWidth="800" MaxWidth="800">
|
||||
<Grid.Resources>
|
||||
<DataTemplate x:Key="CountryTemplate">
|
||||
<emoji:TextBlock TextAlignment="Center" Text="{Binding Country}"/>
|
||||
</DataTemplate>
|
||||
</Grid.Resources>
|
||||
<DataGrid Name="RadioListGrid" MouseDoubleClick="Row_DoubleClick" CanUserAddRows="False" CanUserDeleteRows="False"
|
||||
CanUserReorderColumns="False" CanUserResizeRows="False" IsReadOnly="True" SelectionMode="Single"
|
||||
HeadersVisibility="Column" GridLinesVisibility="None" VirtualizingPanel.ScrollUnit="Pixel">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTemplateColumn Header="🏳️" CellTemplate="{StaticResource CountryTemplate}" MinWidth="25" />
|
||||
<DataGridTextColumn Header="{x:Static properties:Resources.Radio_Name}" Binding="{Binding Name}" MinWidth="50"/>
|
||||
<DataGridTextColumn Header="Codec" Binding="{Binding Codec}" MinWidth="47"/>
|
||||
<DataGridTextColumn Header="Bitrate" Binding="{Binding Bitrate}" MinWidth="47"/>
|
||||
<DataGridTextColumn Header="Tags" Binding="{Binding Tags}" MinWidth="50"/>
|
||||
</DataGrid.Columns>
|
||||
<DataGrid.CellStyle>
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||||
</Style>
|
||||
</DataGrid.CellStyle>
|
||||
</DataGrid>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
216
Views/Radios.xaml.cs
Normal file
@ -0,0 +1,216 @@
|
||||
using RadioBrowser;
|
||||
using RadioBrowser.Models;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace unison
|
||||
{
|
||||
public class CountryListItem
|
||||
{
|
||||
public uint Count { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Name == "")
|
||||
return "None";
|
||||
return $"{Name} ({Count})";
|
||||
}
|
||||
}
|
||||
|
||||
public class StationListItem
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Codec { get; set; }
|
||||
public string Tags { get; set; }
|
||||
public int Bitrate { get; set; }
|
||||
public Uri Url { get; set; }
|
||||
|
||||
private string _country;
|
||||
public string Country
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_country.Length == 0)
|
||||
return "🏴☠️";
|
||||
return string.Concat(_country.ToUpper().Select(x => char.ConvertFromUtf32(x + 0x1F1A5))); // return emoji
|
||||
}
|
||||
set
|
||||
{
|
||||
_country = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Radios : Window
|
||||
{
|
||||
private RadioBrowserClient _radioBrowser;
|
||||
private MPDHandler _mpd;
|
||||
private bool _connected = true;
|
||||
|
||||
public bool IsConnected() => _connected;
|
||||
|
||||
public Radios()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
try
|
||||
{
|
||||
_radioBrowser = new RadioBrowserClient();
|
||||
Initialize();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine("Exception while connecting to RadioBrowser: " + e.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
public async void Initialize()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<NameAndCount> Countries = await _radioBrowser.Lists.GetCountriesAsync();
|
||||
CountryList.Items.Add(new CountryListItem { Name = "", Count = 0 });
|
||||
|
||||
foreach (NameAndCount Country in Countries)
|
||||
{
|
||||
CountryList.Items.Add(new CountryListItem
|
||||
{
|
||||
Name = Country.Name,
|
||||
Count = Country.Stationcount
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.WriteLine("Exception while getting countries in RadioBrowser: " + e.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private string CleanString(string str)
|
||||
{
|
||||
return str.Replace("\r\n", "").Replace("\n", "").Replace("\r", "");
|
||||
}
|
||||
|
||||
public async Task SearchAdvanced(string name, string country, string tags)
|
||||
{
|
||||
try
|
||||
{
|
||||
SearchStatus.Text = unison.Resources.Resources.Radio_Loading;
|
||||
|
||||
List<StationInfo> advancedSearch = await _radioBrowser.Search.AdvancedAsync(new AdvancedSearchOptions
|
||||
{
|
||||
Name = name,
|
||||
Country = country,
|
||||
TagList = tags
|
||||
});
|
||||
|
||||
RadioListGrid.Items.Clear();
|
||||
if (advancedSearch.Count > 0)
|
||||
{
|
||||
SearchStatus.Text = "";
|
||||
foreach (StationInfo station in advancedSearch)
|
||||
{
|
||||
RadioListGrid.Items.Add(new StationListItem
|
||||
{
|
||||
Name = CleanString(station.Name),
|
||||
Country = station.CountryCode,
|
||||
Codec = station.Codec,
|
||||
Bitrate = station.Bitrate,
|
||||
Url = station.Url,
|
||||
Tags = string.Join(", ", station.Tags)
|
||||
});
|
||||
}
|
||||
FitToContent();
|
||||
}
|
||||
else
|
||||
SearchStatus.Text = unison.Resources.Resources.Radio_NotFound;
|
||||
}
|
||||
catch (Exception except)
|
||||
{
|
||||
Debug.WriteLine("Error on RadioBrowser search advanced: " + except.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void FitToContent()
|
||||
{
|
||||
foreach (DataGridColumn column in RadioListGrid.Columns)
|
||||
column.Width = new DataGridLength(1.0, DataGridLengthUnitType.SizeToCells);
|
||||
}
|
||||
|
||||
private void Row_DoubleClick(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
DataGrid grid = sender as DataGrid;
|
||||
StationListItem station;
|
||||
try
|
||||
{
|
||||
station = grid.Items[grid.SelectedIndex] as StationListItem;
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{
|
||||
Debug.WriteLine("Error: Invalid index.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (station.Url == null)
|
||||
{
|
||||
Debug.WriteLine("Error: Invalid station.");
|
||||
return;
|
||||
}
|
||||
|
||||
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
_mpd.ClearAddAndPlay(station.Url.AbsoluteUri);
|
||||
}
|
||||
|
||||
private async void Search_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
CountryListItem a = (CountryListItem)CountryList.SelectedItem;
|
||||
await SearchAdvanced(NameSearch.Text, a?.Name, TagSearch.Text);
|
||||
}
|
||||
catch (Exception except)
|
||||
{
|
||||
Debug.WriteLine("Error on RadioBrowser search: " + except.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void Reset_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
NameSearch.Text = "";
|
||||
TagSearch.Text = "";
|
||||
CountryList.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
private void SearchHandler(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Return)
|
||||
Search_Clicked(null, null);
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, CancelEventArgs e)
|
||||
{
|
||||
e.Cancel = true;
|
||||
WindowState = WindowState.Minimized;
|
||||
Hide();
|
||||
}
|
||||
|
||||
public void InitHwnd()
|
||||
{
|
||||
WindowInteropHelper helper = new(this);
|
||||
helper.EnsureHandle();
|
||||
}
|
||||
}
|
||||
}
|
@ -4,8 +4,19 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
|
||||
xmlns:properties="clr-namespace:unison.Resources" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
|
||||
mc:Ignorable="d"
|
||||
Closing="Window_Closing" Title="Settings" ResizeMode="CanMinimize" Icon="/images/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>
|
||||
<x:Array x:Key="ShortcutItems" Type="sys:String">
|
||||
<sys:String>None</sys:String>
|
||||
<sys:String>Alt</sys:String>
|
||||
<sys:String>Control</sys:String>
|
||||
<sys:String>Shift</sys:String>
|
||||
</x:Array>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TabControl Margin="10">
|
||||
@ -13,29 +24,35 @@
|
||||
<DockPanel Margin="8">
|
||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||
<GroupBox.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<emoji:TextBlock Text="📶 Connection"/>
|
||||
</StackPanel>
|
||||
<TextBlock>
|
||||
<emoji:EmojiInline Text="📶"/>
|
||||
<Run Text="{x:Static properties:Resources.Settings_Connection}"/>
|
||||
</TextBlock>
|
||||
</GroupBox.Header>
|
||||
<Grid VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock Text="Host" TextWrapping="Wrap" Margin="5,0,0,0"/>
|
||||
<TextBox x:Name="MpdHost" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_Host}" TextWrapping="Wrap" Margin="5,0,0,0"/>
|
||||
<TextBox x:Name="MpdHost" KeyDown="ConnectHandler" TextChanged="MpdConnectTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,5,0,0">
|
||||
<TextBlock Text="Port" TextWrapping="Wrap" Margin="5,0,0,0"/>
|
||||
<TextBox x:Name="MpdPort" MaxLength="5" PreviewTextInput="NumberValidationTextBox" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_Port}" TextWrapping="Wrap" Margin="5,0,0,0"/>
|
||||
<TextBox x:Name="MpdPort" KeyDown="ConnectHandler" PreviewTextInput="NumberValidationTextBox" MaxLength="5" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,5,0,0">
|
||||
<TextBlock Text="Password" TextWrapping="Wrap" Margin="5,0,0,0"/>
|
||||
<TextBox x:Name="MpdPassword" TextWrapping="Wrap" Width="250" Margin="10,2,0,0"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_Password}" TextWrapping="Wrap" Margin="5,0,0,0"/>
|
||||
<PasswordBox x:Name="MpdPassword" KeyDown="ConnectHandler" Width="250" Margin="10,2,0,0"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_ConnectionPasswordInfo}" TextWrapping="Wrap" Margin="10,5,0,0" MaxWidth="250" HorizontalAlignment="Left"/>
|
||||
</StackPanel>
|
||||
|
||||
<TextBlock x:Name="ConnectionStatus" Text="Not connected." TextWrapping="Wrap" Margin="5,10,0,0"/>
|
||||
<Button x:Name="ConnectButton" Content="Connect" Margin="0,10,0,0" Width="120" Click="MPDConnect_Clicked"/>
|
||||
<TextBlock TextWrapping="Wrap" Margin="5,10,0,0">
|
||||
<Run Text="{x:Static properties:Resources.Settings_ConnectionStatus}" FontWeight="Bold" />
|
||||
<Run x:Name="ConnectionStatus" Text="{x:Static properties:Resources.Settings_ConnectionStatusOffline}"/>
|
||||
</TextBlock>
|
||||
|
||||
<Button x:Name="ConnectButton" Content="{x:Static properties:Resources.Settings_ConnectButton}" Margin="0,10,0,0" Width="120" Click="MPDConnect_Clicked"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
@ -46,27 +63,25 @@
|
||||
<DockPanel Margin="8">
|
||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||
<GroupBox.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<emoji:TextBlock Text="🔊 Snapcast"/>
|
||||
</StackPanel>
|
||||
<emoji:TextBlock Text="🔊 Snapcast"/>
|
||||
</GroupBox.Header>
|
||||
<Grid VerticalAlignment="Top">
|
||||
<StackPanel>
|
||||
<StackPanel>
|
||||
<CheckBox x:Name="SnapcastStartup" Margin="5, 5, 0, 0">
|
||||
<TextBlock Text="Launch at startup" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastLauch}" TextWrapping="Wrap"/>
|
||||
</CheckBox>
|
||||
<CheckBox x:Name="SnapcastWindow" Margin="5,2.5,0,0">
|
||||
<TextBlock Text="Show Snapcast window" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_SnapcastWindow}" TextWrapping="Wrap"/>
|
||||
</CheckBox>
|
||||
<TextBlock Text="Port" 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" Width="250" Margin="10,2,5,0" HorizontalAlignment="Left"/>
|
||||
<TextBlock Text="Executable path" 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" Width="250" Margin="10,2,5,0" HorizontalAlignment="Left"/>
|
||||
<TextBlock TextWrapping="Wrap" Margin="5,5,0,0" TextAlignment="Left" Width="250">
|
||||
You can change to your own locally installed version of the Snapcast client with an <Run FontStyle="Italic" FontWeight="DemiBold">absolute</Run> path.
|
||||
<Run Text="{x:Static properties:Resources.Settings_SnapcastInfo1}" /><Run Text="{x:Static properties:Resources.Settings_SnapcastInfo2}" FontStyle="Italic" FontWeight="DemiBold" /><Run Text="{x:Static properties:Resources.Settings_SnapcastInfo3}" />
|
||||
</TextBlock>
|
||||
<Button Content="Reset" Margin="0,10,0,0" Width="120" Click="SnapcastReset_Clicked"/>
|
||||
<Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,10,0,0" Width="120" Click="SnapcastReset_Clicked"/>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@ -74,21 +89,23 @@
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Shortcuts">
|
||||
<TabItem Header="{x:Static properties:Resources.Settings_Shortcuts}">
|
||||
<DockPanel Margin="8">
|
||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||
<GroupBox.Header>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<emoji:TextBlock Text="⌨️ Shortcuts"/>
|
||||
</StackPanel>
|
||||
<TextBlock>
|
||||
<emoji:EmojiInline Text="⌨️ "/>
|
||||
<Run Text="{x:Static properties:Resources.Settings_Shortcuts}"></Run>
|
||||
</TextBlock>
|
||||
</GroupBox.Header>
|
||||
<Grid>
|
||||
<StackPanel>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Text="Volume offset" TextWrapping="Wrap"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeOffset}" TextWrapping="Wrap" Margin="0,2,0,0"/>
|
||||
<TextBox x:Name="VolumeOffset" TextWrapping="Wrap" Width="25" PreviewTextInput="NumberValidationTextBox" Margin="8,2,0,0"/>
|
||||
</StackPanel>
|
||||
<Grid MinWidth="220" Margin="0,5,0,0">
|
||||
|
||||
<Grid Margin="0,5,0,0" x:Name="RebindKeyWrapper">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
@ -100,28 +117,111 @@
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="Next track" TextWrapping="Wrap" Grid.Column="0" Grid.Row="0" Margin="1"/>
|
||||
<TextBlock Text="Previous track" TextWrapping="Wrap" Grid.Column="0" Grid.Row="1" Margin="1"/>
|
||||
<TextBlock Text="Play / Pause" TextWrapping="Wrap" Grid.Column="0" Grid.Row="2" Margin="1"/>
|
||||
<TextBlock Text="Volume up" TextWrapping="Wrap" Grid.Column="0" Grid.Row="3" Margin="1"/>
|
||||
<TextBlock Text="Volume down" TextWrapping="Wrap" Grid.Column="0" Grid.Row="4" Margin="1"/>
|
||||
<TextBlock Text="Show window" TextWrapping="Wrap" Grid.Column="0" Grid.Row="5" Margin="1"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_NextTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="0" Margin="1,1,1,1" Grid.RowSpan="2"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_PreviousTrack}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="2" Margin="1,1,1,1"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_PlayPause}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="3" Margin="1,1,1,1"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeUp}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="4" Margin="1,1,1,1"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeDown}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="5" Margin="1,1,1,1"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_VolumeMute}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="6" Margin="1,1,1,1"/>
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_ShowWindow}" TextWrapping="Wrap" Grid.Column="0" Grid.Row="7" Margin="1,1,1,1"/>
|
||||
|
||||
<TextBlock Text="ctrl + media_next" TextWrapping="Wrap" Grid.Column="1" Grid.Row="0" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||
<TextBlock Text="ctrl + media_prev" TextWrapping="Wrap" Grid.Column="1" Grid.Row="1" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||
<TextBlock Text="ctrl + media_play" TextWrapping="Wrap" Grid.Column="1" Grid.Row="2" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||
<TextBlock Text="ctrl + volume_up" TextWrapping="Wrap" Grid.Column="1" Grid.Row="3" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||
<TextBlock Text="ctrl + volume_down" TextWrapping="Wrap" Grid.Column="1" Grid.Row="4" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||
<TextBlock Text="ctrl + alt + enter" TextWrapping="Wrap" Grid.Column="1" Grid.Row="5" Margin="1" HorizontalAlignment="Right" FontWeight="Bold"/>
|
||||
<StackPanel x:Name="Shortcut_NextTrack" Orientation="Horizontal" Grid.Column="1" Grid.Row="0" Margin="10,0,0,2" Grid.RowSpan="2">
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
|
||||
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Shortcut_PreviousTrack" Orientation="Horizontal" Grid.Column="1" Grid.Row="2" Margin="10,0,0,2">
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
|
||||
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Shortcut_PlayPause" Orientation="Horizontal" Grid.Column="1" Grid.Row="3" Margin="10,0,0,2">
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
|
||||
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Shortcut_VolumeUp" Orientation="Horizontal" Grid.Column="1" Grid.Row="4" Margin="10,0,0,2">
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
|
||||
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Shortcut_VolumeDown" Orientation="Horizontal" Grid.Column="1" Grid.Row="5" Margin="10,0,0,2">
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
|
||||
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Shortcut_VolumeMute" Orientation="Horizontal" Grid.Column="1" Grid.Row="6" Margin="10,0,0,2">
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
|
||||
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel x:Name="Shortcut_ShowWindow" Orientation="Horizontal" Grid.Column="1" Grid.Row="7" Margin="10,0,0,0">
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD1" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<ComboBox ItemsSource="{StaticResource ShortcutItems}" Margin="0,0,5,0" MinWidth="70" SelectionChanged="MOD_SelectionChanged" Tag="MOD2" FontWeight="Light" SelectedIndex="0" BorderBrush="{x:Null}" FocusVisualStyle="{x:Null}"></ComboBox>
|
||||
<Button Click="RemapKey_Clicked" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FocusVisualStyle="{x:Null}">
|
||||
<TextBlock Text="None" TextAlignment="Center" TextWrapping="Wrap" MinWidth="150" Margin="5,1,5,1" HorizontalAlignment="Stretch" FontWeight="Light" VerticalAlignment="Stretch"/>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
|
||||
<TextBlock Text="{x:Static properties:Resources.Settings_ShortcutsInfo}" TextWrapping="Wrap" MaxWidth="440" TextAlignment="Justify" />
|
||||
</StackPanel>
|
||||
|
||||
<Button Content="{x:Static properties:Resources.Settings_SnapcastResetButton}" Margin="0,10,0,0" Width="120" Click="ShortcutsReset_Clicked"/>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="About" Height="20" VerticalAlignment="Bottom">
|
||||
<TabItem Header="{x:Static properties:Resources.Stats}">
|
||||
<DockPanel Margin="8">
|
||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||
<GroupBox.Header>
|
||||
<TextBlock>
|
||||
<emoji:EmojiInline Text="📊"/>
|
||||
<Run Text="{x:Static properties:Resources.Stats}"/>
|
||||
</TextBlock>
|
||||
</GroupBox.Header>
|
||||
<Grid VerticalAlignment="Top">
|
||||
<TextBlock>
|
||||
<Run Text="{x:Static properties:Resources.Stats_Songs}"/><Run Text=" "/><Run x:Name="StatSong"/><LineBreak/>
|
||||
<Run Text="{x:Static properties:Resources.Stats_Albums}"/><Run Text=" "/><Run x:Name="StatAlbum"/><LineBreak/>
|
||||
<Run Text="{x:Static properties:Resources.Stats_Artists}"/><Run Text=" "/><Run x:Name="StatArtist"/><LineBreak/>
|
||||
<Run Text="{x:Static properties:Resources.Stats_TotalPlaytime}"/><Run Text=" "/><Run x:Name="StatTotalPlaytime"/><LineBreak/><LineBreak/>
|
||||
<Run Text="{x:Static properties:Resources.Stats_Uptime}"/><Run Text=" "/><Run x:Name="StatUptime"/><LineBreak/>
|
||||
<Run Text="{x:Static properties:Resources.Stats_TotalTimePlayed}"/><Run Text=" "/><Run x:Name="StatTotalTimePlayed"/><LineBreak/>
|
||||
<Run Text="{x:Static properties:Resources.Stats_LastDatabaseUpdate}"/><Run Text=" "/><Run x:Name="StatDatabaseUpdate"/>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</DockPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="{x:Static properties:Resources.Settings_About}" Height="20" VerticalAlignment="Bottom">
|
||||
<DockPanel Margin="8">
|
||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
|
||||
<GroupBox.Header>
|
||||
@ -130,23 +230,24 @@
|
||||
<Grid VerticalAlignment="Top">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<TextBlock TextWrapping="Wrap" Margin="0,0,0,10" VerticalAlignment="Top">
|
||||
<Run Text="Version:"/>
|
||||
<Run Text="{x:Static properties:Resources.Settings_Version}"/>
|
||||
<Run Text="{Binding GetVersion, Mode = OneWay}"/>
|
||||
</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" VerticalAlignment="Top">
|
||||
unison is free software. It is built with the following technologies:<LineBreak/>
|
||||
※ <Hyperlink NavigateUri="https://github.com/Difegue/Stylophone" RequestNavigate="Hyperlink_RequestNavigate">Stylophone</Hyperlink>'s updated MpcNET<LineBreak/>
|
||||
<Run Text="{x:Static properties:Resources.Settings_AboutInfo}" /><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/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink>
|
||||
※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink><LineBreak/>
|
||||
※ <Hyperlink NavigateUri="https://github.com/tof4/RadioBrowser" RequestNavigate="Hyperlink_RequestNavigate">RadioBrowser</Hyperlink>
|
||||
</TextBlock>
|
||||
<TextBlock Margin="0,10,0,0">
|
||||
Source code freely available
|
||||
<Hyperlink NavigateUri="https://git.n700.ovh/keb/unison" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
here
|
||||
<Run Text="{x:Static properties:Resources.Settings_SourceCode1}" />
|
||||
<Hyperlink NavigateUri="https://github.com/ZetaKebab/unison" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
<Run Text="{x:Static properties:Resources.Settings_SourceCode2}" />
|
||||
</Hyperlink>.
|
||||
</TextBlock>
|
||||
<TextBlock Margin="0,10,0,0">
|
||||
Made by
|
||||
<Run Text="{x:Static properties:Resources.Settings_MadeBy}" />
|
||||
<Hyperlink NavigateUri="https://marchal.dev" RequestNavigate="Hyperlink_RequestNavigate">
|
||||
Théo Marchal
|
||||
</Hyperlink>.
|
||||
@ -157,7 +258,10 @@
|
||||
|
||||
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0" Margin="0,10,0,0">
|
||||
<GroupBox.Header>
|
||||
<emoji:TextBlock Text="📝 License"/>
|
||||
<TextBlock>
|
||||
<emoji:EmojiInline Text="📝 "/>
|
||||
<Run Text="{x:Static properties:Resources.Settings_License}" />
|
||||
</TextBlock>
|
||||
</GroupBox.Header>
|
||||
<Grid VerticalAlignment="Top">
|
||||
<TextBlock Text="{Binding GetLicense, Mode = OneWay}" TextWrapping="Wrap" Width="500" TextAlignment="Justify" />
|
||||
|
@ -2,9 +2,11 @@
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Navigation;
|
||||
@ -13,9 +15,6 @@ namespace unison
|
||||
{
|
||||
public partial class Settings : Window
|
||||
{
|
||||
private readonly string defaultSnapcastPath = "snapclient_0.25.0-1_win64";
|
||||
private readonly string defaultSnapcastPort = "1704";
|
||||
|
||||
public static string GetVersion => Assembly.GetEntryAssembly().GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||
|
||||
public static string GetLicense
|
||||
@ -36,6 +35,9 @@ namespace unison
|
||||
}
|
||||
}
|
||||
|
||||
HotkeyHandler _hotkeys = (HotkeyHandler)Application.Current.Properties["hotkeys"];
|
||||
|
||||
|
||||
public Settings()
|
||||
{
|
||||
InitHwnd();
|
||||
@ -44,14 +46,36 @@ namespace unison
|
||||
|
||||
WindowState = WindowState.Minimized;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
MpdHost.Text = Properties.Settings.Default.mpd_host;
|
||||
MpdPort.Text = Properties.Settings.Default.mpd_port.ToString();
|
||||
MpdPassword.Text = Properties.Settings.Default.mpd_password;
|
||||
MpdPassword.Password = Properties.Settings.Default.mpd_password;
|
||||
SnapcastStartup.IsChecked = Properties.Settings.Default.snapcast_startup;
|
||||
SnapcastWindow.IsChecked = Properties.Settings.Default.snapcast_window;
|
||||
SnapcastPath.Text = Properties.Settings.Default.snapcast_path;
|
||||
SnapcastPort.Text = Properties.Settings.Default.snapcast_port.ToString();
|
||||
VolumeOffset.Text = Properties.Settings.Default.volume_offset.ToString();
|
||||
|
||||
InitializeShortcuts();
|
||||
}
|
||||
|
||||
public void UpdateConnectionStatus()
|
||||
{
|
||||
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
if (mpd.IsConnected())
|
||||
{
|
||||
ConnectionStatus.Text = $"{unison.Resources.Resources.Settings_ConnectionStatusConnected} {mpd.GetVersion()}.";
|
||||
ConnectButton.IsEnabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusOffline;
|
||||
ConnectButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void NumberValidationTextBox(object sender, TextCompositionEventArgs e)
|
||||
@ -60,6 +84,18 @@ namespace unison
|
||||
e.Handled = regex.IsMatch(e.Text);
|
||||
}
|
||||
|
||||
private void MpdConnectTextBox(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
TextBox textBox = (TextBox)sender;
|
||||
|
||||
if (textBox.Text == Properties.Settings.Default.mpd_host)
|
||||
ConnectButton.IsEnabled = false;
|
||||
else
|
||||
ConnectButton.IsEnabled = true;
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
|
||||
{
|
||||
ProcessStartInfo psi = new(e.Uri.AbsoluteUri);
|
||||
@ -68,40 +104,45 @@ namespace unison
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
public void UpdateConnectionStatus()
|
||||
{
|
||||
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
if (mpd.IsConnected())
|
||||
ConnectionStatus.Text = "Connected to MPD " + mpd.GetVersion() + ".";
|
||||
else
|
||||
ConnectionStatus.Text = "Not connected.";
|
||||
}
|
||||
|
||||
private void MPDConnect_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!ConnectButton.IsEnabled)
|
||||
return;
|
||||
|
||||
SaveSettings();
|
||||
ConnectionStatus.Text = "Connecting...";
|
||||
|
||||
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
mpd.Connect();
|
||||
if (mpd.IsConnected())
|
||||
mpd = new MPDHandler();
|
||||
|
||||
ConnectButton.IsEnabled = false;
|
||||
ConnectionStatus.Text = unison.Resources.Resources.Settings_ConnectionStatusConnecting;
|
||||
|
||||
System.Threading.Tasks.Task.Run(() => { mpd.Connect(); });
|
||||
}
|
||||
|
||||
private void SnapcastReset_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
SnapcastPath.Text = defaultSnapcastPath;
|
||||
SnapcastPort.Text = defaultSnapcastPort;
|
||||
SnapcastPath.Text = (string)Application.Current.FindResource("snapcastPath");
|
||||
SnapcastPort.Text = (string)Application.Current.FindResource("snapcastPort");
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
public void UpdateStats()
|
||||
{
|
||||
Properties.Settings.Default.mpd_host = MpdHost.Text;
|
||||
Properties.Settings.Default.mpd_port = int.Parse(MpdPort.Text, CultureInfo.InvariantCulture);
|
||||
Properties.Settings.Default.mpd_password = MpdPassword.Text;
|
||||
Properties.Settings.Default.snapcast_startup = (bool)SnapcastStartup.IsChecked;
|
||||
Properties.Settings.Default.snapcast_window = (bool)SnapcastWindow.IsChecked;
|
||||
Properties.Settings.Default.snapcast_path = SnapcastPath.Text;
|
||||
Properties.Settings.Default.snapcast_port = int.Parse(SnapcastPort.Text, CultureInfo.InvariantCulture);
|
||||
Properties.Settings.Default.volume_offset = int.Parse(VolumeOffset.Text, CultureInfo.InvariantCulture);
|
||||
Properties.Settings.Default.Save();
|
||||
MPDHandler mpd = (MPDHandler)Application.Current.Properties["mpd"];
|
||||
StatSong.Text = mpd.GetStats().Songs.ToString();
|
||||
StatAlbum.Text = mpd.GetStats().Albums.ToString();
|
||||
StatArtist.Text = mpd.GetStats().Artists.ToString();
|
||||
StatTotalPlaytime.Text = mpd.GetStats().TotalPlaytime.ToString();
|
||||
StatUptime.Text = mpd.GetStats().Uptime.ToString();
|
||||
StatTotalTimePlayed.Text = mpd.GetStats().TotalTimePlayed.ToString();
|
||||
StatDatabaseUpdate.Text = mpd.GetStats().DatabaseUpdate.ToString();
|
||||
}
|
||||
|
||||
private void ConnectHandler(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Return)
|
||||
MPDConnect_Clicked(null, null);
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, CancelEventArgs e)
|
||||
@ -117,5 +158,234 @@ namespace unison
|
||||
WindowInteropHelper helper = new(this);
|
||||
helper.EnsureHandle();
|
||||
}
|
||||
|
||||
public void SaveSettings()
|
||||
{
|
||||
Properties.Settings.Default.mpd_host = MpdHost.Text;
|
||||
Properties.Settings.Default.mpd_port = int.Parse(MpdPort.Text, CultureInfo.InvariantCulture);
|
||||
Properties.Settings.Default.mpd_password = MpdPassword.Password;
|
||||
Properties.Settings.Default.snapcast_startup = (bool)SnapcastStartup.IsChecked;
|
||||
Properties.Settings.Default.snapcast_window = (bool)SnapcastWindow.IsChecked;
|
||||
Properties.Settings.Default.snapcast_path = SnapcastPath.Text;
|
||||
Properties.Settings.Default.snapcast_port = int.Parse(SnapcastPort.Text, CultureInfo.InvariantCulture);
|
||||
Properties.Settings.Default.volume_offset = int.Parse(VolumeOffset.Text, CultureInfo.InvariantCulture);
|
||||
|
||||
Properties.Settings.Default.nextTrack_mod = GetMod(Shortcut_NextTrack);
|
||||
Properties.Settings.Default.nextTrack_vk = GetVk(Shortcut_NextTrack);
|
||||
Properties.Settings.Default.previousTrack_mod = GetMod(Shortcut_PreviousTrack);
|
||||
Properties.Settings.Default.previousTrack_vk = GetVk(Shortcut_PreviousTrack);
|
||||
Properties.Settings.Default.playPause_mod = GetMod(Shortcut_PlayPause);
|
||||
Properties.Settings.Default.playPause_vk = GetVk(Shortcut_PlayPause);
|
||||
Properties.Settings.Default.volumeUp_mod = GetMod(Shortcut_VolumeUp);
|
||||
Properties.Settings.Default.volumeUp_vk = GetVk(Shortcut_VolumeUp);
|
||||
Properties.Settings.Default.volumeDown_mod = GetMod(Shortcut_VolumeDown);
|
||||
Properties.Settings.Default.volumeDown_vk = GetVk(Shortcut_VolumeDown);
|
||||
Properties.Settings.Default.volumeMute_mod = GetMod(Shortcut_VolumeMute);
|
||||
Properties.Settings.Default.volumeMute_vk = GetVk(Shortcut_VolumeMute);
|
||||
Properties.Settings.Default.showWindow_mod = GetMod(Shortcut_ShowWindow);
|
||||
Properties.Settings.Default.showWindow_vk = GetVk(Shortcut_ShowWindow);
|
||||
|
||||
Properties.Settings.Default.Save();
|
||||
}
|
||||
|
||||
|
||||
// Hotkeys
|
||||
|
||||
void InitializeShortcuts()
|
||||
{
|
||||
System.Collections.Generic.IEnumerable<StackPanel> stackPanelCollection = RebindKeyWrapper.Children.OfType<StackPanel>();
|
||||
StackPanel[] stackPanelList = stackPanelCollection.ToArray();
|
||||
|
||||
// Default state
|
||||
for (int i = 0; i < stackPanelList.Length; i++)
|
||||
{
|
||||
ComboBox[] comboBoxList = stackPanelList[i].Children.OfType<ComboBox>().ToArray();
|
||||
foreach (ComboBox comboBox in comboBoxList) // default status (for reset)
|
||||
{
|
||||
comboBox.FontWeight = FontWeights.Light;
|
||||
comboBox.SelectedItem = "None";
|
||||
}
|
||||
TextBlock textBlock = (TextBlock)stackPanelList[i].Children.OfType<Button>().FirstOrDefault().Content;
|
||||
textBlock.Text = "None";
|
||||
}
|
||||
|
||||
// Populate values
|
||||
for (int i = 0; i < stackPanelList.Length; i++)
|
||||
{
|
||||
// setup MOD
|
||||
HotkeyHandler.MOD mod = _hotkeys._Shortcuts[i].mod;
|
||||
|
||||
HotkeyHandler.MOD[] MODList = System.Enum.GetValues(typeof(HotkeyHandler.MOD))
|
||||
.OfType<HotkeyHandler.MOD>()
|
||||
.Select(x => x & _hotkeys._Shortcuts[i].mod)
|
||||
.Where(x => x != HotkeyHandler.MOD.None)
|
||||
.ToArray();
|
||||
|
||||
ComboBox[] comboBox = stackPanelList[i].Children.OfType<ComboBox>().ToArray();
|
||||
for (int j = 0; j < MODList.Length; j++)
|
||||
{
|
||||
comboBox[j].SelectedItem = MODList[j].ToString();
|
||||
if (comboBox[j].SelectedItem.ToString() != "None")
|
||||
comboBox[j].FontWeight = FontWeights.Bold;
|
||||
}
|
||||
|
||||
// setup VK
|
||||
TextBlock textBlock = (TextBlock)stackPanelList[i].Children.OfType<Button>().FirstOrDefault().Content;
|
||||
textBlock.Text = _hotkeys._Shortcuts[i].vk.ToString();
|
||||
if (textBlock.Text != "None")
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
}
|
||||
}
|
||||
|
||||
private void HotkeyChanged()
|
||||
{
|
||||
_hotkeys.RemoveHotkeys();
|
||||
_hotkeys.AddHotkeys();
|
||||
}
|
||||
|
||||
private ref HotkeyHandler.HotkeyPair GetHotkeyVariable(string Name)
|
||||
{
|
||||
if (Name == "Shortcut_NextTrack")
|
||||
return ref _hotkeys._NextTrack;
|
||||
if (Name == "Shortcut_PreviousTrack")
|
||||
return ref _hotkeys._PreviousTrack;
|
||||
if (Name == "Shortcut_PlayPause")
|
||||
return ref _hotkeys._PlayPause;
|
||||
if (Name == "Shortcut_VolumeUp")
|
||||
return ref _hotkeys._VolumeUp;
|
||||
if (Name == "Shortcut_VolumeDown")
|
||||
return ref _hotkeys._VolumeDown;
|
||||
if (Name == "Shortcut_VolumeMute")
|
||||
return ref _hotkeys._VolumeMute;
|
||||
if (Name == "Shortcut_ShowWindow")
|
||||
return ref _hotkeys._ShowWindow;
|
||||
return ref _hotkeys._NextTrack;
|
||||
}
|
||||
|
||||
private void UpdateHotkey_MOD(string Name, HotkeyHandler.MOD mod) => GetHotkeyVariable(Name).SetMOD(mod);
|
||||
private void UpdateHotkey_VK(string Name, HotkeyHandler.VK vk) => GetHotkeyVariable(Name).SetVK(vk);
|
||||
|
||||
private void MOD_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
ComboBox comboBox = (ComboBox)sender;
|
||||
StackPanel stackPanel = (StackPanel)comboBox.Parent;
|
||||
System.Collections.Generic.IEnumerable<ComboBox> stackPanelCollection = stackPanel.Children.OfType<ComboBox>();
|
||||
|
||||
HotkeyHandler.MOD MOD1, MOD2;
|
||||
|
||||
// we need to do this because the element is modified -after- this function
|
||||
if (comboBox.Tag.ToString() == "MOD1")
|
||||
{
|
||||
MOD1 = GetMOD(e.AddedItems[0].ToString());
|
||||
MOD2 = GetMOD(stackPanelCollection.Last().Text);
|
||||
}
|
||||
else
|
||||
{
|
||||
MOD1 = GetMOD(stackPanelCollection.First().Text);
|
||||
MOD2 = GetMOD(e.AddedItems[0].ToString());
|
||||
}
|
||||
|
||||
if (e.AddedItems[0].ToString() == "None")
|
||||
comboBox.FontWeight = FontWeights.Light;
|
||||
else
|
||||
comboBox.FontWeight = FontWeights.Bold;
|
||||
|
||||
HotkeyHandler.MOD ModKey = MOD1 | MOD2;
|
||||
|
||||
UpdateHotkey_MOD(stackPanel.Name, ModKey);
|
||||
HotkeyChanged();
|
||||
}
|
||||
|
||||
private void RemapKey_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Button button = (Button)sender;
|
||||
TextBlock textBlock = (TextBlock)button.Content;
|
||||
textBlock.Text = unison.Resources.Resources.Settings_ShortcutsKey;
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
button.PreviewKeyDown += DetectPressedKey;
|
||||
}
|
||||
|
||||
private void DetectPressedKey(object sender, KeyEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
|
||||
Key pressedKey = e.Key;
|
||||
HotkeyHandler.VK VirtualKey = GetVirtualKey(pressedKey);
|
||||
|
||||
Button button = (Button)sender;
|
||||
TextBlock textBlock = (TextBlock)button.Content;
|
||||
StackPanel stackPanel = (StackPanel)button.Parent;
|
||||
|
||||
if (VirtualKey == HotkeyHandler.VK.None)
|
||||
{
|
||||
pressedKey = Key.None;
|
||||
textBlock.FontWeight = FontWeights.Light;
|
||||
}
|
||||
else
|
||||
textBlock.FontWeight = FontWeights.Bold;
|
||||
|
||||
textBlock.Text = pressedKey.ToString();
|
||||
button.PreviewKeyDown -= DetectPressedKey;
|
||||
|
||||
UpdateHotkey_VK(stackPanel.Name, VirtualKey);
|
||||
HotkeyChanged();
|
||||
}
|
||||
|
||||
private HotkeyHandler.VK GetVirtualKey(Key key)
|
||||
{
|
||||
foreach (object value in System.Enum.GetValues(typeof(HotkeyHandler.VK)))
|
||||
{
|
||||
if (key.ToString().ToLower() == value.ToString().ToLower())
|
||||
return (HotkeyHandler.VK)value;
|
||||
}
|
||||
return HotkeyHandler.VK.None;
|
||||
}
|
||||
|
||||
private HotkeyHandler.MOD GetMOD(string str)
|
||||
{
|
||||
foreach (object value in System.Enum.GetValues(typeof(HotkeyHandler.MOD)))
|
||||
{
|
||||
if (str.ToLower() == value.ToString().ToLower())
|
||||
return (HotkeyHandler.MOD)value;
|
||||
}
|
||||
return HotkeyHandler.MOD.None;
|
||||
}
|
||||
|
||||
private void ShortcutsReset_Clicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Properties.Settings.Default.nextTrack_mod = (uint)Application.Current.FindResource("nextTrack_mod");
|
||||
Properties.Settings.Default.nextTrack_vk = (uint)Application.Current.FindResource("nextTrack_vk");
|
||||
Properties.Settings.Default.previousTrack_mod = (uint)Application.Current.FindResource("previousTrack_mod");
|
||||
Properties.Settings.Default.previousTrack_vk = (uint)Application.Current.FindResource("previousTrack_vk");
|
||||
Properties.Settings.Default.playPause_mod = (uint)Application.Current.FindResource("playPause_mod");
|
||||
Properties.Settings.Default.playPause_vk = (uint)Application.Current.FindResource("playPause_vk");
|
||||
Properties.Settings.Default.volumeUp_mod = (uint)Application.Current.FindResource("volumeUp_mod");
|
||||
Properties.Settings.Default.volumeUp_vk = (uint)Application.Current.FindResource("volumeUp_vk");
|
||||
Properties.Settings.Default.volumeDown_mod = (uint)Application.Current.FindResource("volumeDown_mod");
|
||||
Properties.Settings.Default.volumeDown_vk = (uint)Application.Current.FindResource("volumeDown_vk");
|
||||
Properties.Settings.Default.volumeMute_mod = (uint)Application.Current.FindResource("volumeMute_mod");
|
||||
Properties.Settings.Default.volumeMute_vk = (uint)Application.Current.FindResource("volumeMute_vk");
|
||||
Properties.Settings.Default.showWindow_mod = (uint)Application.Current.FindResource("showWindow_mod");
|
||||
Properties.Settings.Default.showWindow_vk = (uint)Application.Current.FindResource("showWindow_vk");
|
||||
|
||||
_hotkeys.Initialize();
|
||||
HotkeyChanged();
|
||||
InitializeShortcuts();
|
||||
}
|
||||
|
||||
public uint GetMod(StackPanel stackPanel)
|
||||
{
|
||||
return (uint)(GetMOD(stackPanel.Children.OfType<ComboBox>().First().SelectedItem.ToString()) | GetMOD(stackPanel.Children.OfType<ComboBox>().Last().SelectedItem.ToString()));
|
||||
}
|
||||
|
||||
public uint GetVk(StackPanel stackPanel)
|
||||
{
|
||||
Button button = stackPanel.Children.OfType<Button>().First();
|
||||
TextBlock textBlock = (TextBlock)button.Content;
|
||||
return (uint)(HotkeyHandler.VK)System.Enum.Parse(typeof(HotkeyHandler.VK), textBlock.Text, true);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,47 +2,53 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:tb="http://www.hardcodet.net/taskbar"
|
||||
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
|
||||
xmlns:properties="clr-namespace:unison.Resources"
|
||||
xmlns:local="clr-namespace:unison">
|
||||
|
||||
<ContextMenu x:Shared="false" x:Key="SystrayMenu">
|
||||
<MenuItem IsEnabled="False">
|
||||
<MenuItem.Icon>
|
||||
<Image Source="/images/icon-full.ico" Width="16" Height="16"/>
|
||||
<Image Source="/Resources/icon-full.ico" Width="16" Height="16"/>
|
||||
</MenuItem.Icon>
|
||||
<MenuItem.Header>
|
||||
<TextBlock Text="{Binding GetAppText}" />
|
||||
</MenuItem.Header>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Show window" Command="{Binding ShowWindowCommand}">
|
||||
<MenuItem Header="{x:Static properties:Resources.ShowWindow}" Command="{Binding ShowWindowCommand}">
|
||||
<MenuItem.Icon>
|
||||
<Image Width="16" Height="16" emoji:Image.Source="▶️" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Name="test_snapcast_item" Header="{Binding SnapcastText}" Command="{Binding Snapcast}">
|
||||
<MenuItem Header="{Binding SnapcastText}" Command="{Binding Snapcast}">
|
||||
<MenuItem.Icon>
|
||||
<Image Width="16" Height="16" emoji:Image.Source="🔊" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="{x:Static properties:Resources.Radios}" Command="{Binding Radios}">
|
||||
<MenuItem.Icon>
|
||||
<Image Width="16" Height="16" emoji:Image.Source="📻" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<!--<MenuItem Header="Shuffle" Command="{Binding Shuffle}">
|
||||
<MenuItem.Icon>
|
||||
<Image Width="16" Height="16" emoji:Image.Source="🔀" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>-->
|
||||
<MenuItem Header="Settings" Command="{Binding Settings}">
|
||||
<MenuItem Header="{x:Static properties:Resources.Settings}" Command="{Binding Settings}">
|
||||
<MenuItem.Icon>
|
||||
<Image Width="16" Height="16" emoji:Image.Source="🛠️" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Exit" Command="{Binding ExitApplicationCommand}">
|
||||
<MenuItem Header="{x:Static properties:Resources.Exit}" Command="{Binding ExitApplicationCommand}">
|
||||
<MenuItem.Icon>
|
||||
<Image Width="16" Height="16" emoji:Image.Source="❌" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
|
||||
<tb:TaskbarIcon x:Key="SystrayTaskbar" IconSource="/images/icon-mini.ico" ToolTipText="{Binding GetAppText}" DoubleClickCommand="{Binding ShowWindowCommand}" ContextMenu="{StaticResource SystrayMenu}">
|
||||
<tb:TaskbarIcon x:Key="SystrayTaskbar" IconSource="/Resources/icon-mini.ico" ToolTipText="{Binding GetAppText}" DoubleClickCommand="{Binding ShowWindowCommand}" ContextMenu="{StaticResource SystrayMenu}">
|
||||
<tb:TaskbarIcon.DataContext>
|
||||
<local:SystrayViewModel />
|
||||
</tb:TaskbarIcon.DataContext>
|
||||
|
@ -37,7 +37,7 @@ namespace unison
|
||||
get
|
||||
{
|
||||
SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"];
|
||||
return snapcast.HasStarted ? "Stop Snapcast" : "Start Snapcast";
|
||||
return snapcast.HasStarted ? unison.Resources.Resources.StopSnapcast : unison.Resources.Resources.StartSnapcast;
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +59,18 @@ namespace unison
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand Radios
|
||||
{
|
||||
get
|
||||
{
|
||||
return new DelegateCommand
|
||||
{
|
||||
CommandAction = () => ((MainWindow)Application.Current.MainWindow).Radios_Clicked(null, null),
|
||||
CanExecuteFunc = () => true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public ICommand Settings
|
||||
{
|
||||
get
|
||||
|
Before Width: | Height: | Size: 14 KiB |
BIN
snapclient_0.26.0-1_win64/snapclient.exe
Normal file
101
unison.csproj
@ -4,31 +4,26 @@
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net5.0-windows</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ApplicationIcon>images\icon-full.ico</ApplicationIcon>
|
||||
<ApplicationIcon>Resources\icon-full.ico</ApplicationIcon>
|
||||
<Win32Resource></Win32Resource>
|
||||
<StartupObject>unison.App</StartupObject>
|
||||
<Version>0.0.1</Version>
|
||||
<Version>1.3</Version>
|
||||
<Company />
|
||||
<Authors>Théo Marchal</Authors>
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
<PackageProjectUrl>https://git.n700.ovh/keb/unison</PackageProjectUrl>
|
||||
<RepositoryUrl>https://git.n700.ovh/keb/unison</RepositoryUrl>
|
||||
<PackageProjectUrl>https://github.com/ZetaKebab/unison</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/ZetaKebab/unison</RepositoryUrl>
|
||||
<Copyright>Théo Marchal</Copyright>
|
||||
<PackageIconUrl />
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="images\icon-full.ico" />
|
||||
<None Remove="images\icon-mini.ico" />
|
||||
<None Remove="images\nocover.png" />
|
||||
<None Remove="Resources\icon-full.ico" />
|
||||
<None Remove="Resources\icon-mini.ico" />
|
||||
<None Remove="Resources\nocover.png" />
|
||||
<None Remove="LICENSE" />
|
||||
<None Remove="snapclient_0.25.0-1_win64\FLAC.dll" />
|
||||
<None Remove="snapclient_0.25.0-1_win64\ogg.dll" />
|
||||
<None Remove="snapclient_0.25.0-1_win64\opus.dll" />
|
||||
<None Remove="snapclient_0.25.0-1_win64\README.txt" />
|
||||
<None Remove="snapclient_0.25.0-1_win64\snapclient.exe" />
|
||||
<None Remove="snapclient_0.25.0-1_win64\soxr.dll" />
|
||||
<None Remove="snapclient_0.25.0-1_win64\vorbis.dll" />
|
||||
<None Remove="Resources\nothing.png" />
|
||||
<None Remove="Resources\radio.png" />
|
||||
<None Include="LICENSE">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath></PackagePath>
|
||||
@ -36,48 +31,31 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="images\icon-full.ico">
|
||||
<Resource Include="Resources\icon-full.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="images\icon-mini.ico">
|
||||
<Resource Include="Resources\icon-mini.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="images\nocover.png">
|
||||
<Resource Include="Resources\nocover.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\nothing.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Resource Include="Resources\radio.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<Content Include="LICENSE">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="snapclient_0.25.0-1_win64\README.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="snapclient_0.25.0-1_win64\FLAC.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="snapclient_0.25.0-1_win64\ogg.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="snapclient_0.25.0-1_win64\opus.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="snapclient_0.25.0-1_win64\snapclient.exe">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="snapclient_0.25.0-1_win64\soxr.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Include="snapclient_0.25.0-1_win64\vorbis.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Emoji.Wpf" Version="0.3.3" />
|
||||
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MpcNET\MpcNET.csproj" />
|
||||
<PackageReference Include="RadioBrowser" Version="0.6.1" />
|
||||
<PackageReference Include="MpcNET" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -86,6 +64,24 @@
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Resources\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Resources\Resources.es-ES.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Resources\Resources.fr-FR.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Update="Resources\Resources.resx">
|
||||
<Generator>PublicResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -93,6 +89,27 @@
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<None Update="snapclient_0.26.0-1_win64\FLAC.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="snapclient_0.26.0-1_win64\ogg.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="snapclient_0.26.0-1_win64\opus.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="snapclient_0.26.0-1_win64\README.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="snapclient_0.26.0-1_win64\snapclient.exe">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="snapclient_0.26.0-1_win64\soxr.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="snapclient_0.26.0-1_win64\vorbis.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
11
unison.sln
@ -4,11 +4,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
VisualStudioVersion = 16.0.31515.178
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "unison", "unison.csproj", "{489048C4-3FCA-4573-B34C-943D03F94D04}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{230556C6-5AC3-4FD8-8947-C9ABF1416D19} = {230556C6-5AC3-4FD8-8947-C9ABF1416D19}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MpcNET", "..\MpcNET\MpcNET.csproj", "{230556C6-5AC3-4FD8-8947-C9ABF1416D19}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -23,12 +18,6 @@ Global
|
||||
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release-Stable|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{489048C4-3FCA-4573-B34C-943D03F94D04}.Release-Stable|Any CPU.Build.0 = Release|Any CPU
|
||||
{230556C6-5AC3-4FD8-8947-C9ABF1416D19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{230556C6-5AC3-4FD8-8947-C9ABF1416D19}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{230556C6-5AC3-4FD8-8947-C9ABF1416D19}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{230556C6-5AC3-4FD8-8947-C9ABF1416D19}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{230556C6-5AC3-4FD8-8947-C9ABF1416D19}.Release-Stable|Any CPU.ActiveCfg = Release-Stable|Any CPU
|
||||
{230556C6-5AC3-4FD8-8947-C9ABF1416D19}.Release-Stable|Any CPU.Build.0 = Release-Stable|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|