Skip to content

Commit

Permalink
Merge pull request #101 from Difegue/dev
Browse files Browse the repository at this point in the history
2.7.0
  • Loading branch information
Difegue authored Sep 9, 2024
2 parents 7eb251f + 5609f5e commit 5e84739
Show file tree
Hide file tree
Showing 37 changed files with 1,092 additions and 231 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ jobs:
- name: Setup MSBuild.exe
uses: microsoft/[email protected]

- name: Add Toolkit Labs nuget feed
run: |
nuget sources add -name "CommunityToolkit-Labs" -source "https://pkgs.dev.azure.com/dotnet/CommunityToolkit/_packaging/CommunityToolkit-Labs/nuget/v3/index.json"
# Restore the application
- name: Restore the application
working-directory: ./Sources
Expand Down
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
Stylophone
===========

[**Music Player Daemon**](https://www.musicpd.org/) Client for UWP and iOS/iPadOS.
[**Music Player Daemon**](https://www.musicpd.org/) Client for Windows, Xbox, macOS and iOS/iPadOS.
Based on [MpcNET](https://github.com/Difegue/MpcNET), my own fork of the original .NET Client Library for MPD. (now on NuGet!)

[<img src="https://get.microsoft.com/images/en-us%20dark.svg" width="200"/>](https://www.microsoft.com/store/apps/9NCB693428T8?cid=storebadge&ocid=badge) [<img src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg" width="216"/>](https://apps.apple.com/us/app/stylophone/id1644672889?itsct=apps_box_link&itscg=30200)
[<img src="https://get.microsoft.com/images/en-us%20dark.svg" width="244"/>](https://www.microsoft.com/store/apps/9NCB693428T8?cid=storebadge&ocid=badge) [<img src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg" width="216"/>](https://apps.apple.com/us/app/stylophone/id1644672889?itsct=apps_box_link&itscg=30200)

Get the macOS version for free from the [Releases!](https://github.com/Difegue/Stylophone/releases)

[Buy a sticker if you want!](https://ko-fi.com/s/9fcf421b6e)

Expand All @@ -16,6 +18,7 @@ Based on [MpcNET](https://github.com/Difegue/MpcNET), my own fork of the origina
* Playlist management (Create, Add/Remove tracks, Delete)
* Picture-in-picture mode
* Live tile on Windows 10
* Local playback support if your MPD server has `httpd` as an output
* Integration with native playback controls
* Browse library by albums, or directly by folders
* All data is pulled from your MPD Server only
Expand All @@ -31,6 +34,23 @@ There is a workaround you can use with checknetisolation which should work:
checknetisolation loopbackexempt -a -n="13459Difegue.Stylophone_zd7bwy3j4yjfy"
```

## Protocol usage (Windows only)

Stylophone can be launched through the `stylophone://` protocol on Windows devices; This feature also makes it so you can control some features of the app through protocol invocations.

The following URLs are supported:

- `stylophone://?verb=stylophone_play` or `stylophone://?verb=stylophone_pause` : Toggle playback status
- `stylophone://?verb=stylophone_stop` : Stop playback
- `stylophone://?verb=stylophone_next` : Go to next track
- `stylophone://?verb=stylophone_prev` : Go to previous track
- `stylophone://?verb=stylophone_shuffle` : Toggle shuffle
- `stylophone://?verb=stylophone_volume_up` : Raise volume
- `stylophone://?verb=stylophone_volume_down` : Lower volume
- `stylophone://?verb=stylophone_volume_set&volume=50` : Set volume to desired value
- `stylophone://?verb=stylophone_seek&seek=50` : Seek to desired position in current track, in seconds
- `stylophone://?verb=stylophone_load_playlistt&playlist=YourPlaylistName` : Load the desired playlist in queue

## Translation

You can easily contribute translations to Stylophone! To help translate, follow these instructions.
Expand Down
22 changes: 17 additions & 5 deletions Sources/Stylophone.Common/Services/AlbumArtService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,14 +246,26 @@ internal async Task<SKBitmap> LoadImageFromFile(string fileName)

private SKBitmap ImageFromBytes(byte[] bytes)
{
SKBitmap image = SKBitmap.Decode(bytes);
try
{
SKBitmap image = SKBitmap.Decode(bytes);

// Resize overly large images to reduce OOM risk. Is 2048 too small ?
if (image.Width > 2048)
if (image == null)
return null;

// Resize overly large images to reduce OOM risk. Is 2048 too small ?
if (image.Width > 2048)
{
image.Resize(new SKImageInfo(2048, 2048 * image.Height / image.Width), SKFilterQuality.High);
}
return image;
}
catch (Exception e)
{
image.Resize(new SKImageInfo(2048, 2048 * image.Height / image.Width), SKFilterQuality.High);
Debug.WriteLine("Exception caught while loading albumart from MPD response: " + e);
_notificationService.ShowErrorNotification(e);
return null;
}
return image;
}

}
Expand Down
12 changes: 6 additions & 6 deletions Sources/Stylophone.Common/Stylophone.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@

<ItemGroup>
<PackageReference Include="CodeProject.ObjectPool" Version="6.0.0" />
<PackageReference Include="CommunityToolkit.Common" Version="8.2.2" />
<PackageReference Include="CommunityToolkit.Common" Version="8.3.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="LibVLCSharp" Version="3.7.0" />
<PackageReference Include="MpcNET" Version="1.6.5" />
<PackageReference Include="SkiaSharp" Version="2.88.7" />
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.88.7" />
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="5.0.2" />
<PackageReference Include="LibVLCSharp" Version="3.9.0" />
<PackageReference Include="MpcNET" Version="1.6.6" />
<PackageReference Include="SkiaSharp" Version="2.88.8" />
<PackageReference Include="SkiaSharp.Views.Desktop.Common" Version="2.88.8" />
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="5.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 2 additions & 1 deletion Sources/Stylophone.Common/ViewModels/AlbumDetailViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ private void CreateTrackViewModels()
Source.Add(_trackVmFactory.GetTrackViewModel(file));
}

var totalTime = Source.Select(vm => vm.File.Time).Aggregate((t1, t2) => t1 + t2);
var times = Source.Select(vm => vm.File.Time);
var totalTime = times.Count() > 0 ? times.Aggregate((t1, t2) => t1 + t2) : 0;
TimeSpan t = TimeSpan.FromSeconds(totalTime);

PlaylistInfo = $"{Source.Count} Tracks, Total Time: {t.ToReadableString()}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ public async Task LoadDataAsync()
var albumList = await _mpdService.SafelySendCommandAsync(new ListCommand(MpdTags.Album));
var albumSortList = await _mpdService.SafelySendCommandAsync(new ListCommand(MpdTags.AlbumSort));

// Create a list of tuples
var response = albumList.Zip(albumSortList, (album, albumSort) => new Album{ Name = album, SortName = albumSort });

if (albumSortList != null)
if (albumList != null && albumSortList != null)
{
// Create a list of tuples
var response = albumList.Zip(albumSortList, (album, albumSort) => new Album { Name = album, SortName = albumSort });
GroupAlbumsByName(response);
}

if (Source.Count > 0)
FilteredSource.AddRange(Source);
Expand Down
42 changes: 42 additions & 0 deletions Sources/Stylophone.Common/ViewModels/Items/OutputViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using MpcNET;
using MpcNET.Commands.Output;
using MpcNET.Types;
using Stylophone.Common.Interfaces;
using Stylophone.Common.Services;

namespace Stylophone.Common.ViewModels.Items
{
public partial class OutputViewModel: ObservableObject
{
[ObservableProperty]
private string _name;

[ObservableProperty]
private string _plugin;

[ObservableProperty]
private bool _isEnabled;

private int _id;

public OutputViewModel() { }

public OutputViewModel(MpdOutput o)
{
_id = o.Id;
_name = o.Name;
_plugin = o.Plugin;
_isEnabled = o.IsEnabled;
}

partial void OnIsEnabledChanged(bool value)
{
IMpcCommand<string> command = value ? new EnableOutputCommand(_id) : new DisableOutputCommand(_id);

Ioc.Default.GetRequiredService<MPDConnectionService>().SafelySendCommandAsync(command);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public partial class LocalPlaybackViewModel : ViewModelBase
private LibVLC _vlcCore;
private MediaPlayer _mediaPlayer;
private string _serverHost;
private int _serverPort;

public LocalPlaybackViewModel(SettingsViewModel settingsVm, MPDConnectionService mpdService, IInteropService interopService, INotificationService notificationService, IDispatcherService dispatcherService) : base(dispatcherService)
{
Expand All @@ -37,6 +38,9 @@ public LocalPlaybackViewModel(SettingsViewModel settingsVm, MPDConnectionService

if (e.PropertyName == nameof(_settingsVm.ServerHost))
_serverHost = _settingsVm.ServerHost;

if (e.PropertyName == nameof(_settingsVm.LocalPlaybackPort))
_serverPort = _settingsVm.LocalPlaybackPort;
};

// Run an idle loop in a spare thread to make sure the libVLC volume is always accurate
Expand All @@ -57,9 +61,10 @@ public LocalPlaybackViewModel(SettingsViewModel settingsVm, MPDConnectionService
});
}

public void Initialize(string host, bool isEnabled)
public void Initialize(string host, int port, bool isEnabled)
{
_serverHost = host;
_serverPort = port;
IsEnabled = isEnabled;
}

Expand Down Expand Up @@ -147,7 +152,7 @@ partial void OnIsPlayingChanged(bool value)
{
if (value && _serverHost != null && _mpdService.IsConnected)
{
var urlString = "http://" + _serverHost + ":8000";
var urlString = "http://" + _serverHost + ":" + _serverPort;
var streamUrl = new Uri(urlString);
var media = new Media(_vlcCore, streamUrl);

Expand Down
28 changes: 24 additions & 4 deletions Sources/Stylophone.Common/ViewModels/SettingsViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -7,8 +9,10 @@
using CommunityToolkit.Mvvm.Input;
using MpcNET.Commands.Output;
using MpcNET.Commands.Status;
using MpcNET.Types;
using Stylophone.Common.Interfaces;
using Stylophone.Common.Services;
using Stylophone.Common.ViewModels.Items;
using Stylophone.Localization.Strings;

namespace Stylophone.Common.ViewModels
Expand Down Expand Up @@ -75,6 +79,12 @@ public SettingsViewModel(MPDConnectionService mpdService, IApplicationStorageSer
[ObservableProperty]
private bool _isLocalPlaybackEnabled;

[ObservableProperty]
private int _localPlaybackPort;

[ObservableProperty]
private ObservableCollection<OutputViewModel> _outputs = new();

partial void OnElementThemeChanged(Theme value)
{
Task.Run (async () => await _interop.SetThemeAsync(value));
Expand Down Expand Up @@ -123,6 +133,11 @@ partial void OnIsLocalPlaybackEnabledChanged(bool value)
_applicationStorageService.SetValue(nameof(IsLocalPlaybackEnabled), value);
}

partial void OnLocalPlaybackPortChanged(int value)
{
_applicationStorageService.SetValue(nameof(LocalPlaybackPort), value);
}


[RelayCommand]
private async Task ClearCacheAsync()
Expand Down Expand Up @@ -176,6 +191,7 @@ public async Task EnsureInstanceInitializedAsync()
_enableAnalytics = _applicationStorageService.GetValue(nameof(EnableAnalytics), true);
_isAlbumArtFetchingEnabled = _applicationStorageService.GetValue(nameof(IsAlbumArtFetchingEnabled), true);
_isLocalPlaybackEnabled = _applicationStorageService.GetValue<bool>(nameof(IsLocalPlaybackEnabled));
_localPlaybackPort = _applicationStorageService.GetValue(nameof(LocalPlaybackPort), 8000);

Enum.TryParse(_applicationStorageService.GetValue<string>(nameof(ElementTheme)), out _elementTheme);

Expand All @@ -199,7 +215,6 @@ private async Task CheckUpdatingDbAsync()

private string GetVersionDescription()
{
var appName = Resources.AppDisplayName;
Version version = _interop.GetAppVersion();

return $"{version.Major}.{version.Minor}.{(version.Revision > -1 ? version.Revision : 0)}";
Expand Down Expand Up @@ -232,13 +247,18 @@ private async Task UpdateServerVersionAsync()
lastUpdatedDb = DateTimeOffset.FromUnixTimeSeconds(db_update).UtcDateTime;
}

// Build info string
// Get server outputs
var outputs = await _mpdService.SafelySendCommandAsync(new OutputsCommand());
Outputs.Clear();

foreach (var o in outputs)
Outputs.Add(new OutputViewModel(o));

var songs = response.ContainsKey("songs") ? response["songs"] : "??";
var albums = response.ContainsKey("albums") ? response["albums"] : "??";

if (outputs != null && outputs.Count() > 0)
// Build info string
if (outputs?.Count() > 0)
{
var outputString = outputs.Select(o => o.Plugin).Aggregate((s, s2) => $"{s}, {s2}");

Expand All @@ -247,7 +267,7 @@ private async Task UpdateServerVersionAsync()
$"Database last updated {lastUpdatedDb}\n" +
$"Outputs available: {outputString}";

IsStreamingAvailable = outputs.Select(o => o.Plugin).Contains("httpd");
IsStreamingAvailable = outputs.Any(o => o.Plugin.Contains("httpd"));

if (!IsStreamingAvailable)
IsLocalPlaybackEnabled = false;
Expand Down
36 changes: 36 additions & 0 deletions Sources/Stylophone.Localization/Strings/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions Sources/Stylophone.Localization/Strings/Resources.en-US.resx
Original file line number Diff line number Diff line change
Expand Up @@ -488,4 +488,16 @@ Enabling this option will show a second volume slider to control local volume.</
<data name="SongPlaybackLabel" xml:space="preserve">
<value>Song playback</value>
</data>
<data name="SettingsLocalPlaybackPortHeader" xml:space="preserve">
<value>Stream port</value>
</data>
<data name="SettingsLocalPlaybackPortText" xml:space="preserve">
<value>Set this to the port of your server's httpd stream.</value>
</data>
<data name="SettingsOutputsHeader" xml:space="preserve">
<value>Server Outputs</value>
</data>
<data name="SettingsOutputsText" xml:space="preserve">
<value>Enable or disable the server's music outputs. </value>
</data>
</root>
Loading

0 comments on commit 5e84739

Please sign in to comment.