10 Commits

23 changed files with 268 additions and 88 deletions

View File

@ -74,10 +74,28 @@
<value>13</value> <value>13</value>
</setting> </setting>
<setting name="MainWindowTop" serializeAs="String"> <setting name="MainWindowTop" serializeAs="String">
<value>100</value> <value>10</value>
</setting> </setting>
<setting name="MainWindowLeft" serializeAs="String"> <setting name="MainWindowLeft" serializeAs="String">
<value>100</value> <value>10</value>
</setting>
<setting name="RadiosWindowTop" serializeAs="String">
<value>10</value>
</setting>
<setting name="RadiosWindowLeft" serializeAs="String">
<value>1000</value>
</setting>
<setting name="ShuffleWindowTop" serializeAs="String">
<value>10</value>
</setting>
<setting name="ShuffleWindowLeft" serializeAs="String">
<value>330</value>
</setting>
<setting name="SettingsWindowTop" serializeAs="String">
<value>330</value>
</setting>
<setting name="SettingsWindowLeft" serializeAs="String">
<value>600</value>
</setting> </setting>
</unison.Properties.Settings> </unison.Properties.Settings>
</userSettings> </userSettings>

View File

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

View File

@ -677,15 +677,24 @@ namespace unison
if (Response == null) if (Response == null)
return; return;
_stats.Songs = int.Parse(Response["songs"]); if (Response.ContainsKey("songs"))
_stats.Albums = int.Parse(Response["albums"]); _stats.Songs = int.Parse(Response["songs"]);
_stats.Artists = int.Parse(Response["artists"]); if (Response.ContainsKey("albums"))
_stats.Albums = int.Parse(Response["albums"]);
_stats.Uptime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["uptime"]))); if (Response.ContainsKey("artists"))
_stats.TotalPlaytime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["db_playtime"]))); _stats.Artists = int.Parse(Response["artists"]);
_stats.TotalTimePlayed = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["playtime"])));
if (Response.ContainsKey("uptime"))
_stats.Uptime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["uptime"])));
if (Response.ContainsKey("db_playtime"))
_stats.TotalPlaytime = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["db_playtime"])));
if (Response.ContainsKey("playtime"))
_stats.TotalTimePlayed = FormatTime(TimeSpan.FromSeconds(int.Parse(Response["playtime"])));
DateTime date = new DateTime(1970, 1, 1);
if (Response.ContainsKey("db_update"))
date = date.AddSeconds(int.Parse(Response["db_update"])).ToLocalTime();
DateTime date = new DateTime(1970, 1, 1).AddSeconds(int.Parse(Response["db_update"])).ToLocalTime();
string dayOfWeek = Resources.Resources.Culture.DateTimeFormat.GetDayName(date.DayOfWeek); string dayOfWeek = Resources.Resources.Culture.DateTimeFormat.GetDayName(date.DayOfWeek);
_stats.DatabaseUpdate = dayOfWeek + " " + date.ToString("dd/MM/yyyy @ HH:mm"); _stats.DatabaseUpdate = dayOfWeek + " " + date.ToString("dd/MM/yyyy @ HH:mm");
} }

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ namespace unison.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.8.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.13.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@ -289,7 +289,7 @@ namespace unison.Properties {
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("100")] [global::System.Configuration.DefaultSettingValueAttribute("10")]
public double MainWindowTop { public double MainWindowTop {
get { get {
return ((double)(this["MainWindowTop"])); return ((double)(this["MainWindowTop"]));
@ -301,7 +301,7 @@ namespace unison.Properties {
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("100")] [global::System.Configuration.DefaultSettingValueAttribute("10")]
public double MainWindowLeft { public double MainWindowLeft {
get { get {
return ((double)(this["MainWindowLeft"])); return ((double)(this["MainWindowLeft"]));
@ -310,5 +310,77 @@ namespace unison.Properties {
this["MainWindowLeft"] = value; this["MainWindowLeft"] = value;
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("10")]
public double RadiosWindowTop {
get {
return ((double)(this["RadiosWindowTop"]));
}
set {
this["RadiosWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("1000")]
public double RadiosWindowLeft {
get {
return ((double)(this["RadiosWindowLeft"]));
}
set {
this["RadiosWindowLeft"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("10")]
public double ShuffleWindowTop {
get {
return ((double)(this["ShuffleWindowTop"]));
}
set {
this["ShuffleWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("330")]
public double ShuffleWindowLeft {
get {
return ((double)(this["ShuffleWindowLeft"]));
}
set {
this["ShuffleWindowLeft"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("330")]
public double SettingsWindowTop {
get {
return ((double)(this["SettingsWindowTop"]));
}
set {
this["SettingsWindowTop"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("600")]
public double SettingsWindowLeft {
get {
return ((double)(this["SettingsWindowLeft"]));
}
set {
this["SettingsWindowLeft"] = value;
}
}
} }
} }

View File

@ -69,10 +69,28 @@
<Value Profile="(Default)">13</Value> <Value Profile="(Default)">13</Value>
</Setting> </Setting>
<Setting Name="MainWindowTop" Type="System.Double" Scope="User"> <Setting Name="MainWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">100</Value> <Value Profile="(Default)">10</Value>
</Setting> </Setting>
<Setting Name="MainWindowLeft" Type="System.Double" Scope="User"> <Setting Name="MainWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">100</Value> <Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="RadiosWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="RadiosWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">1000</Value>
</Setting>
<Setting Name="ShuffleWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="ShuffleWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">330</Value>
</Setting>
<Setting Name="SettingsWindowTop" Type="System.Double" Scope="User">
<Value Profile="(Default)">330</Value>
</Setting>
<Setting Name="SettingsWindowLeft" Type="System.Double" Scope="User">
<Value Profile="(Default)">600</Value>
</Setting> </Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -7,7 +7,8 @@
* lightweight window that can be toggled with shortcuts * lightweight window that can be toggled with shortcuts
* music control through rebindable shortcuts * music control through rebindable shortcuts
* [Snapcast](https://github.com/badaix/snapcast) integration * [Snapcast](https://github.com/badaix/snapcast) integration
* Radio stations * radio stations
* basic shuffle system
## Features ## Features
@ -33,11 +34,20 @@ Through [Radio-Browser](https://www.radio-browser.info), a community database, y
![Radio stations](Screenshots/screen4.png) ![Radio stations](Screenshots/screen4.png)
## Planned features ### Shuffle system
* A complete shuffle system based on set criteria, aka a smart playlist. A shuffle system is available, allowing you to only use unison as a (very basic) music manager. You can use filters to have a tailored shuffle experience.
* Playlist, queue and library management. I use other software to do it, but I will implement them at some point.
![Shuffle system](Screenshots/screen5.png)
## Translations ## Translations
unison is translated in English, French and Spanish. You can contribute if you want! unison is translated in English, French and Spanish. You can contribute if you want!
## The future
This project started as a simple daemon to control my music with shortcuts, and a Snapcast player. With time, I added a few useful features, while learning C# and WPF. I wanted to make a complete implementation of MPD features (library, playlist, queue management, etc). However, this will probably not happen. Indeed, I have been waiting a few years for Microsoft to settle on a modern implementation of its user interface framework. But it is still not there, and I do not want to spend time for a technology that might die too quickly. There's no coherence in Windows' interfaces, and it is not really motivating. I also want to be able to play Snapcast from iOS, so I think I will most likely invest (natively) my time in this platform for the foreseeable future.
If I were to continue, the first thing to do would be to make a single window interface, by making a real MVVP implementation, and not one depending on windows like I did here, by iterating on a single program that has become a bit too big to not separate logic from visual.
I still plan on maintaining this app in a working state, as its working great as is. But I do not intend, for the time being at least, to make dramatic changes as I would have liked to.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

BIN
Screenshots/screen5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -17,7 +17,7 @@
</Style> </Style>
</Window.Resources> </Window.Resources>
<Grid Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}" MinHeight="270" MinWidth="700"> <Grid Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}" MinHeight="270" MinWidth="700" Margin="0,10,0,0">
<Grid x:Name="TopLayout" Margin="10,0,10,0" VerticalAlignment="Stretch" Width="Auto" Height="Auto"> <Grid x:Name="TopLayout" Margin="10,0,10,0" VerticalAlignment="Stretch" Width="Auto" Height="Auto">
<Grid x:Name="Display" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="225,0,0,0" Height="Auto" Width="Auto"> <Grid x:Name="Display" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="225,0,0,0" Height="Auto" Width="Auto">
<GroupBox Height="220" VerticalAlignment="Center"> <GroupBox Height="220" VerticalAlignment="Center">
@ -114,14 +114,14 @@
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,0"> <StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,0">
<Button x:Name="Shuffle" Padding="5, 2" Click="Shuffle_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" Margin="0,0,10,0"> <Button x:Name="Shuffle" Padding="5, 2" Click="Shuffle_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" Margin="0,0,10,0">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<emoji:TextBlock Text="🔁" Padding="0,0,0,2"/> <TextBlock Text="🔁" Margin="0,0,0,2"/>
<TextBlock Text="{x:Static properties:Resources.Shuffle}" Margin="5, 0, 0, 0"/> <TextBlock Text="{x:Static properties:Resources.Shuffle}" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button x:Name="Radio" Padding="5, 2" HorizontalAlignment="Left" Click="Radios_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False"> <Button x:Name="Radio" Padding="5, 2" HorizontalAlignment="Left" Click="Radios_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<emoji:TextBlock Text="📻" Padding="0,0,0,2"/> <TextBlock Text="📻" Padding="0,0,0,2"/>
<TextBlock Text="{x:Static properties:Resources.Radios}" Margin="5, 0, 0, 0"/> <TextBlock Text="{x:Static properties:Resources.Radios}" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>
@ -139,14 +139,14 @@
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0"> <StackPanel HorizontalAlignment="Right" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
<Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="0,0,10,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False"> <Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="0,0,10,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<emoji:TextBlock Text="🔊" Padding="0,0,0,2"/> <TextBlock Text="🔊" Padding="0,0,0,2"/>
<TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/> <TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button x:Name="Settings" Padding="5, 2" Click="Settings_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}"> <Button x:Name="Settings" Padding="5, 2" Click="Settings_Clicked" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<emoji:TextBlock Text="🛠️" Padding="0,0,0,2"/> <TextBlock Text="🛠️" Padding="0,0,0,2"/>
<TextBlock Text="{x:Static properties:Resources.Settings}" Margin="5, 0, 0, 0"/> <TextBlock Text="{x:Static properties:Resources.Settings}" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>

View File

@ -17,17 +17,22 @@ namespace unison
private readonly Settings _settingsWin; private readonly Settings _settingsWin;
private readonly Radios _radiosWin; private readonly Radios _radiosWin;
private readonly Shuffle _shuffleWin; private readonly Shuffle _shuffleWin;
private readonly DispatcherTimer _timer;
private readonly MPDHandler _mpd;
public Settings GetSettings() => _settingsWin; public Settings GetSettings() => _settingsWin;
public Radios GetRadios() => _radiosWin;
public Shuffle GetShuffle() => _shuffleWin;
private readonly DispatcherTimer _timer;
private readonly MPDHandler _mpd;
public MainWindow() public MainWindow()
{ {
InitHwnd(); InitHwnd();
InitializeComponent(); InitializeComponent();
DefaultState(true); DefaultState(true);
WindowState = WindowState.Minimized; WindowState = WindowState.Minimized;
Top = Properties.Settings.Default.MainWindowTop; Top = Properties.Settings.Default.MainWindowTop;
Left = Properties.Settings.Default.MainWindowLeft; Left = Properties.Settings.Default.MainWindowLeft;
@ -257,56 +262,49 @@ namespace unison
snapcast.LaunchOrExit(); snapcast.LaunchOrExit();
} }
private bool _radiosVisible = false;
private bool _shuffleVisible = false;
private bool _settingsVisible = false;
public void Radios_Clicked(object sender, RoutedEventArgs e) public void Radios_Clicked(object sender, RoutedEventArgs e)
{ {
if (_radiosVisible)
{
_radiosVisible = false;
_radiosWin.Hide();
return;
}
_radiosVisible = true;
_radiosWin.Show();
_radiosWin.Activate();
if (_radiosWin.WindowState == WindowState.Minimized) if (_radiosWin.WindowState == WindowState.Minimized)
{
_radiosWin.Show();
_radiosWin.Activate();
_radiosWin.WindowState = WindowState.Normal; _radiosWin.WindowState = WindowState.Normal;
}
else if (_radiosWin.WindowState == WindowState.Normal)
{
_radiosWin.Hide();
_radiosWin.WindowState = WindowState.Minimized;
}
} }
public void Shuffle_Clicked(object sender, RoutedEventArgs e) public void Shuffle_Clicked(object sender, RoutedEventArgs e)
{ {
if (_shuffleVisible)
{
_shuffleVisible = false;
_shuffleWin.Hide();
return;
}
_shuffleVisible = true;
_shuffleWin.Show();
_shuffleWin.Activate();
if (_shuffleWin.WindowState == WindowState.Minimized) if (_shuffleWin.WindowState == WindowState.Minimized)
{
_shuffleWin.Show();
_shuffleWin.Activate();
_shuffleWin.WindowState = WindowState.Normal; _shuffleWin.WindowState = WindowState.Normal;
}
else if (_shuffleWin.WindowState == WindowState.Normal)
{
_shuffleWin.Hide();
_shuffleWin.WindowState = WindowState.Minimized;
}
} }
public void Settings_Clicked(object sender, RoutedEventArgs e) public void Settings_Clicked(object sender, RoutedEventArgs e)
{ {
if (_settingsVisible)
{
_settingsVisible = false;
_settingsWin.Hide();
return;
}
_settingsVisible = true;
_settingsWin.Show();
_settingsWin.Activate();
if (_settingsWin.WindowState == WindowState.Minimized) if (_settingsWin.WindowState == WindowState.Minimized)
{
_settingsWin.Show();
_settingsWin.Activate();
_settingsWin.WindowState = WindowState.Normal; _settingsWin.WindowState = WindowState.Normal;
}
else if (_settingsWin.WindowState == WindowState.Normal)
{
_settingsWin.Hide();
_settingsWin.WindowState = WindowState.Minimized;
}
} }
private void TimeSlider_DragStarted(object sender, DragStartedEventArgs e) private void TimeSlider_DragStarted(object sender, DragStartedEventArgs e)
@ -352,7 +350,7 @@ namespace unison
string CopyText = SongTitle.Text + " - " + SongArtist.Text + "\n"; string CopyText = SongTitle.Text + " - " + SongArtist.Text + "\n";
CopyText += SongAlbum.Text + "\n"; CopyText += SongAlbum.Text + "\n";
CopyText += SongTitle.ToolTip; CopyText += SongTitle.ToolTip;
Clipboard.SetText(CopyText); Clipboard.SetDataObject(CopyText);
} }
} }

View File

@ -6,15 +6,15 @@
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
xmlns:properties="clr-namespace:unison.Resources" xmlns:properties="clr-namespace:unison.Resources"
mc:Ignorable="d" mc:Ignorable="d"
Title="Radios" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize"> Title="Radios" Closing="Window_Closing" LocationChanged="Window_LocationChanged" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<Grid> <Grid Margin="0,5,0,0">
<StackPanel> <StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,0"> <StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,0">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="📻"/> <Run Text="📻"/>
<Run Text="{x:Static properties:Resources.Radio_SearchStation}"/> <Run Text="{x:Static properties:Resources.Radio_SearchStation}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>

View File

@ -23,6 +23,12 @@ namespace unison
{ {
InitializeComponent(); InitializeComponent();
Initialize(); Initialize();
InitHwnd();
WindowState = WindowState.Minimized;
Top = Properties.Settings.Default.RadiosWindowTop;
Left = Properties.Settings.Default.RadiosWindowLeft;
} }
public async void Initialize() public async void Initialize()
@ -169,5 +175,12 @@ namespace unison
WindowInteropHelper helper = new(this); WindowInteropHelper helper = new(this);
helper.EnsureHandle(); helper.EnsureHandle();
} }
private void Window_LocationChanged(object sender, EventArgs e)
{
Properties.Settings.Default.RadiosWindowTop = Top;
Properties.Settings.Default.RadiosWindowLeft = Left;
Properties.Settings.Default.Save();
}
} }
} }

View File

@ -6,7 +6,7 @@
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
xmlns:properties="clr-namespace:unison.Resources" xmlns:sys="clr-namespace:System;assembly=System.Runtime" xmlns:properties="clr-namespace:unison.Resources" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" mc:Ignorable="d"
Closing="Window_Closing" Title="{x:Static properties:Resources.Settings}" ResizeMode="CanMinimize" Icon="/Resources/icon-full.ico" WindowStyle="ToolWindow" SizeToContent="WidthAndHeight"> Title="{x:Static properties:Resources.Settings}" Closing="Window_Closing" LocationChanged="Window_LocationChanged" ResizeMode="CanMinimize" Icon="/Resources/icon-full.ico" WindowStyle="ToolWindow" SizeToContent="WidthAndHeight">
<Window.Resources> <Window.Resources>
<x:Array x:Key="ShortcutItems" Type="sys:String"> <x:Array x:Key="ShortcutItems" Type="sys:String">
@ -25,7 +25,7 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="📶"/> <Run Text="📶"/>
<Run Text="{x:Static properties:Resources.Settings_Connection}"/> <Run Text="{x:Static properties:Resources.Settings_Connection}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
@ -64,7 +64,7 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="⌨️ "/> <Run Text="⌨️"/>
<Run Text="{x:Static properties:Resources.Settings_Shortcuts}"></Run> <Run Text="{x:Static properties:Resources.Settings_Shortcuts}"></Run>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
@ -170,7 +170,7 @@
<DockPanel Margin="8"> <DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<emoji:TextBlock Text="🔊 Snapcast"/> <Run Text="🔊 Snapcast"/>
</GroupBox.Header> </GroupBox.Header>
<Grid VerticalAlignment="Top"> <Grid VerticalAlignment="Top">
<StackPanel> <StackPanel>
@ -201,8 +201,8 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="🔁 "/> <Run Text="🔁"/>
<Run Text="{x:Static properties:Resources.Shuffle}"></Run> <Run Text="{x:Static properties:Resources.Shuffle}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
<Grid MaxWidth="500"> <Grid MaxWidth="500">
@ -230,7 +230,7 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="📊"/> <Run Text="📊"/>
<Run Text="{x:Static properties:Resources.Database}"/> <Run Text="{x:Static properties:Resources.Database}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
@ -262,7 +262,7 @@
<DockPanel Margin="8"> <DockPanel Margin="8">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<emoji:TextBlock Text="🎶 unison"/> <Run Text="🎶 unison"/>
</GroupBox.Header> </GroupBox.Header>
<Grid VerticalAlignment="Top"> <Grid VerticalAlignment="Top">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
@ -281,7 +281,7 @@
※ <Hyperlink NavigateUri="https://github.com/Difegue/MpcNET" RequestNavigate="Hyperlink_RequestNavigate">MpcNET</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/Difegue/MpcNET" RequestNavigate="Hyperlink_RequestNavigate">MpcNET</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/hardcodet/wpf-notifyicon" RequestNavigate="Hyperlink_RequestNavigate">wpf-notifyicon</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/samhocevar/emoji.wpf" RequestNavigate="Hyperlink_RequestNavigate">Emoji.WPF</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/tof4/RadioBrowser" RequestNavigate="Hyperlink_RequestNavigate">RadioBrowser</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://git.sr.ht/~youkai/RadioBrowser.NET" RequestNavigate="Hyperlink_RequestNavigate">RadioBrowser</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/ravibpatel/AutoUpdater.NET" RequestNavigate="Hyperlink_RequestNavigate">AutoUpdater.NET</Hyperlink><LineBreak/> ※ <Hyperlink NavigateUri="https://github.com/ravibpatel/AutoUpdater.NET" RequestNavigate="Hyperlink_RequestNavigate">AutoUpdater.NET</Hyperlink><LineBreak/>
※ <Hyperlink NavigateUri="https://github.com/badaix/snapcast" RequestNavigate="Hyperlink_RequestNavigate">Snapcast</Hyperlink> ※ <Hyperlink NavigateUri="https://github.com/badaix/snapcast" RequestNavigate="Hyperlink_RequestNavigate">Snapcast</Hyperlink>
</TextBlock> </TextBlock>
@ -308,7 +308,7 @@
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0" Margin="0,10,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0" Margin="0,10,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="📝 "/> <Run Text="📝"/>
<Run Text="{x:Static properties:Resources.Settings_License}" /> <Run Text="{x:Static properties:Resources.Settings_License}" />
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>

View File

@ -45,11 +45,13 @@ namespace unison
{ {
InitHwnd(); InitHwnd();
InitializeComponent(); InitializeComponent();
Initialize();
DataContext = this; DataContext = this;
WindowState = WindowState.Minimized; WindowState = WindowState.Minimized;
Initialize(); Top = Properties.Settings.Default.SettingsWindowTop;
Left = Properties.Settings.Default.SettingsWindowLeft;
} }
void Initialize() void Initialize()
@ -207,6 +209,13 @@ namespace unison
helper.EnsureHandle(); helper.EnsureHandle();
} }
private void Window_LocationChanged(object sender, EventArgs e)
{
Properties.Settings.Default.SettingsWindowTop = Top;
Properties.Settings.Default.SettingsWindowLeft = Left;
Properties.Settings.Default.Save();
}
public void SaveSettings() public void SaveSettings()
{ {
Properties.Settings.Default.mpd_host = MpdHost.Text; Properties.Settings.Default.mpd_host = MpdHost.Text;

View File

@ -6,7 +6,7 @@
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf" xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
xmlns:properties="clr-namespace:unison.Resources" xmlns:sys="clr-namespace:System;assembly=System.Runtime" xmlns:properties="clr-namespace:unison.Resources" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
mc:Ignorable="d" mc:Ignorable="d"
Title="Shuffle" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize"> Title="Shuffle" Closing="Window_Closing" LocationChanged="Window_LocationChanged" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<Window.Resources> <Window.Resources>
<x:Array x:Key="FilterType" Type="sys:String"> <x:Array x:Key="FilterType" Type="sys:String">
@ -42,13 +42,13 @@
</DataTemplate> </DataTemplate>
</Window.Resources> </Window.Resources>
<Grid> <Grid Margin="0,5,0,0">
<StackPanel> <StackPanel>
<StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,5"> <StackPanel HorizontalAlignment="Left" Orientation="Vertical" Margin="5,0,5,5">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0"> <GroupBox DockPanel.Dock="Top" Padding="0,4,0,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="🔡"/> <Run Text="🔡"/>
<Run Text="{x:Static properties:Resources.Shuffle_Filter}"/> <Run Text="{x:Static properties:Resources.Shuffle_Filter}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
@ -80,7 +80,7 @@
<GroupBox DockPanel.Dock="Right" Padding="0,4,0,0" Width="300"> <GroupBox DockPanel.Dock="Right" Padding="0,4,0,0" Width="300">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text=""/> <Run Text="🧾"/>
<Run Text="{x:Static properties:Resources.Queue_Management}"/> <Run Text="{x:Static properties:Resources.Queue_Management}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>
@ -107,7 +107,7 @@
<GroupBox DockPanel.Dock="Left" Padding="0,4,0,0" Width="260" Margin="0,0,5,0"> <GroupBox DockPanel.Dock="Left" Padding="0,4,0,0" Width="260" Margin="0,0,5,0">
<GroupBox.Header> <GroupBox.Header>
<TextBlock> <TextBlock>
<emoji:EmojiInline Text="♾️"/> <Run Text="♾️"/>
<Run Text="{x:Static properties:Resources.Shuffle_Continuous}"/> <Run Text="{x:Static properties:Resources.Shuffle_Continuous}"/>
</TextBlock> </TextBlock>
</GroupBox.Header> </GroupBox.Header>

View File

@ -32,11 +32,18 @@ namespace unison
public Shuffle() public Shuffle()
{ {
InitializeComponent(); InitializeComponent();
InitHwnd();
GenreList = new(); GenreList = new();
FolderList = new(); FolderList = new();
Filters = new(); Filters = new();
SongFilterNumber.Text = "0"; SongFilterNumber.Text = "0";
WindowState = WindowState.Minimized;
Top = Properties.Settings.Default.ShuffleWindowTop;
Left = Properties.Settings.Default.ShuffleWindowLeft;
_mpd = (MPDHandler)Application.Current.Properties["mpd"]; _mpd = (MPDHandler)Application.Current.Properties["mpd"];
_shuffle = (ShuffleHandler)Application.Current.Properties["shuffle"]; _shuffle = (ShuffleHandler)Application.Current.Properties["shuffle"];
} }
@ -428,5 +435,12 @@ namespace unison
WindowInteropHelper helper = new(this); WindowInteropHelper helper = new(this);
helper.EnsureHandle(); helper.EnsureHandle();
} }
private void Window_LocationChanged(object sender, EventArgs e)
{
Properties.Settings.Default.ShuffleWindowTop = Top;
Properties.Settings.Default.ShuffleWindowLeft = Left;
Properties.Settings.Default.Save();
}
} }
} }

View File

@ -3,6 +3,7 @@ using System.Windows.Input;
using System.Reflection; using System.Reflection;
using System.ComponentModel; using System.ComponentModel;
using System; using System;
using System.Diagnostics;
namespace unison namespace unison
{ {
@ -32,7 +33,7 @@ namespace unison
CanExecuteFunc = () => true CanExecuteFunc = () => true
}; };
public static string SnapcastText public string SnapcastText
{ {
get get
{ {
@ -49,7 +50,7 @@ namespace unison
{ {
CommandAction = () => CommandAction = () =>
{ {
Application.Current.Dispatcher.BeginInvoke(new Action(() => OnPropertyChanged("SnapcastText"))); Application.Current.Dispatcher.BeginInvoke(new Action(() => OnPropertyChanged(SnapcastText)));
SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"]; SnapcastHandler snapcast = (SnapcastHandler)Application.Current.Properties["snapcast"];
snapcast.LaunchOrExit(); snapcast.LaunchOrExit();

View File

@ -7,7 +7,7 @@
<ApplicationIcon>Resources\icon-full.ico</ApplicationIcon> <ApplicationIcon>Resources\icon-full.ico</ApplicationIcon>
<Win32Resource></Win32Resource> <Win32Resource></Win32Resource>
<StartupObject>unison.App</StartupObject> <StartupObject>unison.App</StartupObject>
<Version>1.4-dev</Version> <Version>1.5</Version>
<Company /> <Company />
<Authors>Théo Marchal</Authors> <Authors>Théo Marchal</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
@ -15,6 +15,7 @@
<RepositoryUrl>https://github.com/ZetaKebab/unison</RepositoryUrl> <RepositoryUrl>https://github.com/ZetaKebab/unison</RepositoryUrl>
<Copyright>Théo Marchal</Copyright> <Copyright>Théo Marchal</Copyright>
<PackageIconUrl /> <PackageIconUrl />
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>