3 Commits

Author SHA1 Message Date
23098e0ebb Progress on radio implementation 2021-10-03 13:57:45 +02:00
32d3610b07 Progress on radio implementation 2021-10-03 13:54:54 +02:00
fbb65a039a First POC for a radio browser 2021-10-01 02:24:18 +02:00
6 changed files with 321 additions and 10 deletions

View File

@ -12,6 +12,7 @@ using System.Windows.Threading;
using MpcNET; using MpcNET;
using MpcNET.Commands.Database; using MpcNET.Commands.Database;
using MpcNET.Commands.Playback; using MpcNET.Commands.Playback;
using MpcNET.Commands.Queue;
using MpcNET.Commands.Status; using MpcNET.Commands.Status;
using MpcNET.Message; using MpcNET.Message;
using MpcNET.Types; using MpcNET.Types;
@ -363,8 +364,15 @@ namespace unison
else else
{ {
using MemoryStream stream = new MemoryStream(data.ToArray()); using MemoryStream stream = new MemoryStream(data.ToArray());
try
{
_cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad); _cover = BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
} }
catch (System.NotSupportedException e)
{
_cover = null;
}
}
UpdateCover(); UpdateCover();
} }
@ -439,5 +447,14 @@ namespace unison
_currentVolume = 0; _currentVolume = 0;
SetVolume(_currentVolume); SetVolume(_currentVolume);
} }
public void ClearQueue() => SendCommand(new ClearCommand());
public void PlayCommand() => SendCommand(new PlayCommand(0));
public void AddSong(string Uri)
{
Debug.WriteLine("AddCommand path: " + Uri);
SendCommand(new AddCommand(Uri));
}
} }
} }

View File

@ -110,12 +110,20 @@
</Border> </Border>
</Grid> </Grid>
<Grid x:Name="BottomLayout" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}" Width="Auto" MinHeight="40"> <Grid x:Name="BottomLayout" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="{DynamicResource {x:Static SystemColors.ControlLightBrushKey}}" Width="Auto" MinHeight="40">
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
<Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="10,0,0,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False"> <Button x:Name="Snapcast" HorizontalAlignment="Left" VerticalAlignment="Center" Click="Snapcast_Clicked" Margin="10,0,0,0" Padding="5, 2" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" FocusVisualStyle="{x:Null}" IsEnabled="False">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<emoji:TextBlock Text="🔊" Padding="0,0,0,2"/> <emoji:TextBlock Text="🔊" Padding="0,0,0,2"/>
<TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/> <TextBlock x:Name="SnapcastText" Text="{x:Static properties:Resources.StartSnapcast}" Margin="5, 0, 0, 0"/>
</StackPanel> </StackPanel>
</Button> </Button>
<Button x:Name="Radio" Padding="5, 2" HorizontalAlignment="Left" Click="Radios_Clicked" Margin="5,0,10,0" Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
<StackPanel Orientation="Horizontal">
<emoji:TextBlock Text="📻" Padding="0,0,0,2"/>
<TextBlock Text="Radios" Margin="5, 0, 0, 0"/>
</StackPanel>
</Button>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid x:Name="ConnectionOkIcon" HorizontalAlignment="Center" VerticalAlignment="Center"> <Grid x:Name="ConnectionOkIcon" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xf385;" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" VerticalAlignment="Center" HorizontalAlignment="Center" /> <TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xf385;" Foreground="{DynamicResource {x:Static SystemColors.ControlDarkDarkBrushKey}}" VerticalAlignment="Center" HorizontalAlignment="Center" />

View File

@ -12,6 +12,7 @@ namespace unison
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private readonly Settings _settingsWin; private readonly Settings _settingsWin;
private readonly Radios _radiosWin;
private readonly DispatcherTimer _timer; private readonly DispatcherTimer _timer;
private readonly MPDHandler _mpd; private readonly MPDHandler _mpd;
@ -23,6 +24,7 @@ namespace unison
WindowState = WindowState.Minimized; WindowState = WindowState.Minimized;
_settingsWin = new Settings(); _settingsWin = new Settings();
_radiosWin = new Radios();
_timer = new DispatcherTimer(); _timer = new DispatcherTimer();
_mpd = (MPDHandler)Application.Current.Properties["mpd"]; _mpd = (MPDHandler)Application.Current.Properties["mpd"];
@ -68,10 +70,11 @@ namespace unison
SongTitle.Text = _mpd.GetCurrentSong().Title; SongTitle.Text = _mpd.GetCurrentSong().Title;
else if (_mpd.GetCurrentSong().HasName && _mpd.GetCurrentSong().Name.Length > 0) else if (_mpd.GetCurrentSong().HasName && _mpd.GetCurrentSong().Name.Length > 0)
SongTitle.Text = _mpd.GetCurrentSong().Name; SongTitle.Text = _mpd.GetCurrentSong().Name;
else else if (_mpd.GetCurrentSong().Path != null)
{ {
int start = _mpd.GetCurrentSong().Path.LastIndexOf("/") + 1; int start = _mpd.GetCurrentSong().Path.LastIndexOf("/") + 1;
int end = _mpd.GetCurrentSong().Path.LastIndexOf("."); int end = _mpd.GetCurrentSong().Path.LastIndexOf(".");
if (start > 0 && end > 0 && end > start)
SongTitle.Text = _mpd.GetCurrentSong().Path.Substring(start, end - start); SongTitle.Text = _mpd.GetCurrentSong().Path.Substring(start, end - start);
} }
@ -214,6 +217,15 @@ namespace unison
snapcast.LaunchOrExit(); snapcast.LaunchOrExit();
} }
public void Radios_Clicked(object sender, RoutedEventArgs e)
{
_radiosWin.Show();
_radiosWin.Activate();
if (_radiosWin.WindowState == WindowState.Minimized)
_radiosWin.WindowState = WindowState.Normal;
}
public void Settings_Clicked(object sender, RoutedEventArgs e) public void Settings_Clicked(object sender, RoutedEventArgs e)
{ {
_settingsWin.Show(); _settingsWin.Show();

74
Views/Radios.xaml Normal file
View 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:local="clr-namespace:unison"
mc:Ignorable="d"
Title="Radios" Closing="Window_Closing" SizeToContent="WidthAndHeight" ResizeMode="NoResize">
<StackPanel HorizontalAlignment="Left" Orientation="Vertical">
<GroupBox DockPanel.Dock="Top" Padding="0,4,0,0" Margin="5,0,5,0">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<TextBlock>
<emoji:EmojiInline Text="📻"/>
<Run Text="Search station"/>
</TextBlock>
</StackPanel>
</GroupBox.Header>
<Grid VerticalAlignment="Top">
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<StackPanel HorizontalAlignment="Stretch">
<TextBlock Text="Name" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Margin="5,0,0,0" />
<TextBox x:Name="NameSearch" KeyDown="SearchHandler" Text="" TextWrapping="Wrap" HorizontalAlignment="Left" Width="200" Margin="5,6,0,0"/>
</StackPanel>
<StackPanel HorizontalAlignment="Stretch" Margin="20,0,0,0">
<TextBlock Text="Tags" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" />
<TextBox x:Name="TagSearch" KeyDown="SearchHandler" Text="" TextWrapping="Wrap" HorizontalAlignment="Left" Width="300" Margin="0,6,0,0"/>
</StackPanel>
<StackPanel HorizontalAlignment="Stretch" Margin="20,0,0,0">
<TextBlock Text="Country" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" />
<ComboBox x:Name="CountryList" SelectedIndex="0" KeyDown="SearchHandler" Width="240" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="0,2,0,0" ScrollViewer.CanContentScroll="False"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Margin="5,7.5,0,0">
<Button Content="Search" Click="Search_Clicked"/>
<Button Content="Reset" Click="Reset_Clicked" Margin="10,0,0,0"/>
<TextBlock x:Name="SearchStatus" Text="" Margin="15,1,0,0" FontStyle="Italic" />
</StackPanel>
</StackPanel>
</Grid>
</GroupBox>
<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" CanUserAddRows="False" CanUserDeleteRows="False"
IsReadOnly="True" MouseDoubleClick="Row_DoubleClick" SelectionMode="Single" CanUserReorderColumns="False"
HeadersVisibility="Column" GridLinesVisibility="None" AutoGenerateColumns="False" CanUserResizeRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="🏳️" CellTemplate="{StaticResource CountryTemplate}" MinWidth="25" />
<DataGridTextColumn Header="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>
</Window>

199
Views/Radios.xaml.cs Normal file
View File

@ -0,0 +1,199 @@
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 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;
}
}
private string _bitrate;
public string Bitrate
{
get
{
if (_bitrate == "0")
return "—";
return _bitrate.ToString();
}
set
{
_bitrate = value;
}
}
}
public partial class Radios : Window
{
private RadioBrowserClient _radioBrowser;
private MPDHandler _mpd;
public Radios()
{
InitializeComponent();
_radioBrowser = new RadioBrowserClient();
Initialize();
}
public async void Initialize()
{
List<NameAndCount> Countries = await _radioBrowser.Lists.GetCountriesAsync();
CountryList.Items.Add(new CountryListItem { Name = "", Count = 0 });
foreach (NameAndCount Country in Countries)
{
CountryList.Items.Add(new CountryListItem
{
Name = Country.Name,
Count = Country.Stationcount
});
}
}
private string CleanString(string str)
{
return str.Replace("\r\n", "").Replace("\n", "").Replace("\r", "");
}
public async Task SearchAdvanced(string name, string country, string tags)
{
SearchStatus.Text = "Loading stations...";
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.ToString(),
Url = station.Url,
Tags = string.Join(", ", station.Tags)
});
}
FitToContent();
}
else
SearchStatus.Text = "No stations found!";
}
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 (System.ArgumentOutOfRangeException)
{
Debug.WriteLine("Error: Invalid index.");
return;
}
if (station.Url == null)
{
Debug.WriteLine("Error: Invalid station.");
return;
}
_mpd = (MPDHandler)Application.Current.Properties["mpd"];
_mpd.ClearQueue();
_mpd.AddSong(station.Url.AbsoluteUri);
_mpd.PlayCommand();
}
private async void Search_Clicked(object sender, RoutedEventArgs e)
{
CountryListItem a = (CountryListItem)CountryList.SelectedItem;
await SearchAdvanced(NameSearch.Text, a?.Name, TagSearch.Text);
}
private void Reset_Clicked(object sender, RoutedEventArgs e)
{
NameSearch.Text = "";
TagSearch.Text = "";
CountryList.SelectedIndex = 0;
}
private void SearchHandler(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
Search_Clicked(null, null);
}
}
private void Window_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
WindowState = WindowState.Minimized;
Hide();
}
public void InitHwnd()
{
WindowInteropHelper helper = new(this);
helper.EnsureHandle();
}
}
}

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.0</Version> <Version>1.1</Version>
<Company /> <Company />
<Authors>Théo Marchal</Authors> <Authors>Théo Marchal</Authors>
<PackageLicenseFile>LICENSE</PackageLicenseFile> <PackageLicenseFile>LICENSE</PackageLicenseFile>
@ -74,6 +74,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Emoji.Wpf" Version="0.3.3" /> <PackageReference Include="Emoji.Wpf" Version="0.3.3" />
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" /> <PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
<PackageReference Include="RadioBrowser" Version="0.6.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>