Added tracking/capturing/restoration of audio playback state and position.

This commit is contained in:
2026-08-08 16:37:55 -04:00
parent c262ee4caa
commit 0942e3e5ad
11 changed files with 92 additions and 15 deletions

View File

@@ -58,7 +58,7 @@ public partial class App : Application
await sessionService.LoadAsync(); await sessionService.LoadAsync();
ISessionRestorer sessionRestorer = ServiceProvider.GetRequiredService<ISessionRestorer>(); ISessionRestorer sessionRestorer = ServiceProvider.GetRequiredService<ISessionRestorer>();
sessionRestorer.Restore(); await sessionRestorer.RestoreAsync();
ISessionTracker sessionTracker = ServiceProvider.GetRequiredService<ISessionTracker>(); ISessionTracker sessionTracker = ServiceProvider.GetRequiredService<ISessionTracker>();
sessionTracker.Start(); sessionTracker.Start();

View File

@@ -9,12 +9,14 @@ public sealed partial class MainWindow : Window
private const string ApplicationTitle = "Harmonia"; private const string ApplicationTitle = "Harmonia";
private readonly ISessionService _sessionService; private readonly ISessionService _sessionService;
private readonly ISessionTracker _sessionTracker;
private bool _isSaved; private bool _isSaved;
public MainWindow(ISessionService sessionService) public MainWindow(ISessionService sessionService, ISessionTracker sessionTracker)
{ {
_sessionService = sessionService; _sessionService = sessionService;
_sessionTracker = sessionTracker;
InitializeComponent(); InitializeComponent();
InitializeTitleBar(); InitializeTitleBar();
@@ -57,6 +59,7 @@ public sealed partial class MainWindow : Window
args.Handled = true; args.Handled = true;
_sessionTracker.Capture();
await _sessionService.SaveAsync(); await _sessionService.SaveAsync();
_isSaved = true; _isSaved = true;

View File

@@ -1,11 +1,13 @@
namespace Harmonia.WinUI.Session; using Harmonia.Core.Engine;
namespace Harmonia.WinUI.Session;
public partial class AudioPlayerState public partial class AudioPlayerState
{ {
public string? PlaylistUID { get; set; } public string? PlaylistUID { get; set; }
public string? PlaylistSongUID { get; set; } public string? PlaylistSongUID { get; set; }
public double Position { get; set; } = 0.0; public double Position { get; set; } = 0.0;
public string? PlaybackState { get; set; } public AudioPlaybackState PlaybackState { get; set; } = AudioPlaybackState.Stopped;
public double Volume { get; set; } = 1.0; public double Volume { get; set; } = 1.0;
public bool IsMuted { get; set; } public bool IsMuted { get; set; }
public string? RepeatState { get; set; } public string? RepeatState { get; set; }

View File

@@ -1,6 +1,8 @@
namespace Harmonia.WinUI.Session; using System.Threading.Tasks;
namespace Harmonia.WinUI.Session;
public interface ISessionRestorer public interface ISessionRestorer
{ {
void Restore(); Task RestoreAsync();
} }

View File

@@ -3,4 +3,5 @@ namespace Harmonia.WinUI.Session;
public interface ISessionTracker public interface ISessionTracker
{ {
void Start(); void Start();
void Capture();
} }

View File

@@ -1,7 +1,9 @@
using Harmonia.Core.Player; using Harmonia.Core.Engine;
using Harmonia.Core.Player;
using Harmonia.Core.Playlists; using Harmonia.Core.Playlists;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace Harmonia.WinUI.Session; namespace Harmonia.WinUI.Session;
@@ -10,10 +12,10 @@ public class SessionRestorer(
IPlaylistManager playlistManager, IPlaylistManager playlistManager,
IAudioPlayer audioPlayer) : ISessionRestorer IAudioPlayer audioPlayer) : ISessionRestorer
{ {
public void Restore() public async Task RestoreAsync()
{ {
RestorePlaylist(); RestorePlaylist();
RestoreAudioPlayer(); await RestoreAudioPlayerAsync();
} }
private void RestorePlaylist() private void RestorePlaylist()
@@ -31,7 +33,7 @@ public class SessionRestorer(
playlistManager.CurrentPlaylist = playlist; playlistManager.CurrentPlaylist = playlist;
} }
private void RestoreAudioPlayer() private async Task RestoreAudioPlayerAsync()
{ {
AudioPlayerState audioPlayerState = sessionService.Session.Player; AudioPlayerState audioPlayerState = sessionService.Session.Player;
@@ -43,13 +45,27 @@ public class SessionRestorer(
if (playlist is null) if (playlist is null)
return; return;
PlaylistSong? song = playlist.Songs.FirstOrDefault(s => s.UID == audioPlayerState.PlaylistSongUID); PlaylistSong? playlistSong = playlist.Songs.FirstOrDefault(s => s.UID == audioPlayerState.PlaylistSongUID);
if (song is null) if (playlistSong is null)
return; return;
//PlaybackMode playbackMode = audioPlayerState.PlaybackState await audioPlayer.LoadAsync(playlistSong, PlaybackMode.LoadOnly);
audioPlayer.LoadAsync(song, PlaybackMode.LoadOnly); if (audioPlayerState.Position > 0 && audioPlayerState.Position <= playlistSong.Song.Length.TotalSeconds)
audioPlayer.Position = audioPlayerState.Position;
switch (audioPlayerState.PlaybackState)
{
case AudioPlaybackState.Playing:
audioPlayer.Play();
break;
case AudioPlaybackState.Paused:
audioPlayer.Pause();
break;
case AudioPlaybackState.Stopped:
audioPlayer.Stop();
break;
}
} }
} }

View File

@@ -13,6 +13,7 @@ public class SessionTracker(
{ {
playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged; playlistManager.CurrentPlaylistChanged += OnCurrentPlaylistChanged;
audioPlayer.PlayingSongChanged += OnPlayingSongChanged; audioPlayer.PlayingSongChanged += OnPlayingSongChanged;
audioPlayer.PropertyChanged += OnAudioPlayerPropertyChanged;
} }
private void OnCurrentPlaylistChanged(object? sender, EventArgs e) private void OnCurrentPlaylistChanged(object? sender, EventArgs e)
@@ -26,4 +27,19 @@ public class SessionTracker(
AudioPlayerState audioPlayerState = sessionService.Session.Player; AudioPlayerState audioPlayerState = sessionService.Session.Player;
audioPlayerState.PlaylistSongUID = e.NewSong?.UID; audioPlayerState.PlaylistSongUID = e.NewSong?.UID;
} }
private void OnAudioPlayerPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(IAudioPlayer.State):
sessionService.Session.Player.PlaybackState = audioPlayer.State;
break;
}
}
public void Capture()
{
sessionService.Session.Player.Position = audioPlayer.Position;
}
} }

View File

@@ -225,6 +225,20 @@ public partial class PlayerViewModel : ViewModelBase
_timer.Tick += TickTock; _timer.Tick += TickTock;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread(); _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
TryRestore();
}
private void TryRestore()
{
if (_audioPlayer.PlayingSong is null)
return;
Song = _audioPlayer.PlayingSong.Song;
Position = _audioPlayer.Position;
CurrentPosition = _audioPlayer.Position;
Task.Run(UpdateImage);
} }
#region Event Handlers #region Event Handlers

View File

@@ -50,6 +50,17 @@ public partial class PlayingSongViewModel : ViewModelBase
_audioBitmapImageCache = audioBitmapImageCache; _audioBitmapImageCache = audioBitmapImageCache;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread(); _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
TryRestore();
}
private void TryRestore()
{
if (_audioPlayer.PlayingSong is null)
return;
Song = _audioPlayer.PlayingSong.Song;
Task.Run(UpdateImage);
} }
private void OnAudioPlayerPlayingSongChanged(object? sender, PlayingSongChangedEventArgs e) private void OnAudioPlayerPlayingSongChanged(object? sender, PlayingSongChangedEventArgs e)

View File

@@ -220,6 +220,8 @@ public partial class PlaylistDetailViewModel : ViewModelBase
FilteredPlaylistSongs.CollectionChanged += OnFilteredPlaylistSongsCollectionChanged; FilteredPlaylistSongs.CollectionChanged += OnFilteredPlaylistSongsCollectionChanged;
Playlist = _playlistManager.CurrentPlaylist; // Testing Playlist = _playlistManager.CurrentPlaylist; // Testing
PlayingSong = _audioPlayer.PlayingSong; // Testing
UpdateFilteredSongs(); UpdateFilteredSongs();
} }

View File

@@ -56,6 +56,16 @@ public sealed partial class PlaylistDetailView : UserControl
SortSelectedByMenuFlyoutSubItem.IsEnabled = _viewModel.SortSelectedCommand.CanExecute(null); SortSelectedByMenuFlyoutSubItem.IsEnabled = _viewModel.SortSelectedCommand.CanExecute(null);
SortSelectedByMenuFlyoutSubItem.IsEnabled = _viewModel.SortSelectedCommand.CanExecute(null); SortSelectedByMenuFlyoutSubItem.IsEnabled = _viewModel.SortSelectedCommand.CanExecute(null);
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Loaded -= OnLoaded;
// Defer until the ListView has completed its first layout/container realization pass.
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, BringPlayingSongIntoView);
} }
private void OnViewModelPropertyChanging(object? sender, PropertyChangingEventArgs e) private void OnViewModelPropertyChanging(object? sender, PropertyChangingEventArgs e)
@@ -274,7 +284,7 @@ public sealed partial class PlaylistDetailView : UserControl
if (args.NewValue is not PlaylistSong playlistSong) if (args.NewValue is not PlaylistSong playlistSong)
return; return;
bool isPlaying = playlistSong == _viewModel.PlayingSong; bool isPlaying = playlistSong.UID == _viewModel.PlayingSong?.UID;
UpdateListViewItemStyle(sender, isPlaying); UpdateListViewItemStyle(sender, isPlaying);
} }