Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.7.0 #101

Merged
merged 25 commits into from
Sep 9, 2024
Merged

2.7.0 #101

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
459e9d5
Fix crash reported by appcenter
Difegue Apr 12, 2024
bc0481b
Migrate to NET8 for the app to not horribly crash when targeting iOS17.4
Difegue Apr 17, 2024
f7051f4
Disable silencePlayer on catalyst
Difegue Apr 17, 2024
fc0ccaf
Update README.md
Difegue Apr 21, 2024
5862411
Add xcprivacy file for future iOS updates
Difegue Aug 15, 2024
039e99d
Add widget template
Difegue Aug 12, 2024
2984e76
Add protocol activation handler
Difegue Aug 13, 2024
4aa9db6
Tweak protocol error message to work without shell
Difegue Sep 4, 2024
f44bd0c
Cleanup protocol activation
Difegue Sep 4, 2024
d9756d2
Update nugets
Difegue Sep 4, 2024
b7d157f
(#94) Make local playback port configurable instead of hardcoding 8000
Difegue Sep 4, 2024
9611a95
(#99) Refine albumart loading error handling
Difegue Sep 4, 2024
1ee2b2a
(#98) Fix windows crash when searching for albums too fast
Difegue Sep 4, 2024
6de8829
(#91) Add support for toggling server outputs in settings
Difegue Sep 5, 2024
206a15d
Bump version
Difegue Sep 5, 2024
de6fbc9
Fix iOS build and cleanup
Difegue Sep 8, 2024
337e83c
Implement server outputs/local playback port on iOS
Difegue Sep 8, 2024
27aa2c4
Absolute minimum effort for catalyst
Difegue Sep 8, 2024
2e01656
(#98) Fix potential crash when opening album details
Difegue Sep 8, 2024
b0d8361
Note needed runtimeIdentifiers for catalyst release
Difegue Sep 8, 2024
82f6b4b
Use labs MarqueeLabel to spice up the now playing track in the queue
Difegue Sep 8, 2024
4461dc9
Bump version, update README
Difegue Sep 8, 2024
d85a649
More readme updating
Difegue Sep 8, 2024
b33df77
Add Toolkit labs nuget source to CI
Difegue Sep 8, 2024
5609f5e
(#96) Resume local playback after AVAudioSession interruption
Difegue Sep 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading