using Avalonia.Controls; using Avalonia.Media.Imaging; using Avalonia.Threading; using Harmonia.Core.Caching; using Harmonia.Core.Imaging; using Harmonia.Core.Models; using Harmonia.Core.Player; using Harmonia.Core.Playlists; using Harmonia.Core.Scanner; using System; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace Harmonia.UI.ViewModels; public partial class PlaybackBarViewModel : ViewModelBase { private readonly IAudioPlayer _audioPlayer; private readonly IAudioImageCache _audioImageCache; private readonly DispatcherTimer _timer; private CancellationTokenSource? _audioImageCancellationTokenSource; private Song? _song; public Song? Song { get { return _song; } private set { _song = value; OnPropertyChanged(); CurrentPosition = 0; // What if player is being loaded and returning to save state? Position = 0; MaxPosition = value?.Length.TotalSeconds ?? 0; } } private Bitmap? _songImageSource; public Bitmap? SongImageSource { get { return _songImageSource; } private set { _songImageSource = value; OnPropertyChanged(); } } private double _currentPosition; public double CurrentPosition { get { return _currentPosition; } set { _currentPosition = value; OnPropertyChanged(); if (_isPositionChangeInProgress) _audioPlayer.Position = value; } } private double _position; public double Position { get { return _position; } private set { _position = value; OnPropertyChanged(); } } private double _maxPosition; public double MaxPosition { get { return _maxPosition; } set { _maxPosition = value; OnPropertyChanged(); } } private bool _isPositionChangeInProgress; public bool IsPositionChangeInProgress { get { return _isPositionChangeInProgress; } set { _isPositionChangeInProgress = value; OnPropertyChanged(); } } public bool IsRandom { get { return _audioPlayer.IsRandom; } private set { _audioPlayer.IsRandom = value; OnPropertyChanged(); } } public string Greeting => "Welcome to Harmonia!"; public PlaybackBarViewModel(IAudioPlayer audioPlayer, IAudioImageCache audioImageCache, IPlaylistRepository playlistRepository, IAudioFileScanner audioFileScanner) { _audioPlayer = audioPlayer; _audioPlayer.PlayingSongChanged += OnAudioPlayerPlayingSongChanged; _audioImageCache = audioImageCache; _timer = new(TimeSpan.FromMilliseconds(100), DispatcherPriority.Default, TickTock); } private void OnAudioPlayerPlayingSongChanged(object? sender, EventArgs e) { Song = _audioPlayer.PlayingSong?.Song; Task.Run(UpdateImage); } private async Task UpdateImage() { // TODO: Show default picture if (Song == null) return; if (_audioImageCancellationTokenSource != null) await _audioImageCancellationTokenSource.CancelAsync(); _audioImageCancellationTokenSource = new(); CancellationToken cancellationToken = _audioImageCancellationTokenSource.Token; SongPictureInfo? songPictureInfo = await _audioImageCache.GetAsync(Song, cancellationToken); if (songPictureInfo == null) return; await Dispatcher.UIThread.InvokeAsync(() => SetSongImageSource(songPictureInfo)); } private void SetSongImageSource(SongPictureInfo songPictureInfo) { SongImageSource = new(songPictureInfo.Stream); } private void TickTock(object? sender, object e) { Position = _audioPlayer.Position; if (IsPositionChangeInProgress) return; CurrentPosition = _audioPlayer.Position; } public void Play() { _audioPlayer.Play(); } public void Pause() { _audioPlayer.Pause(); } public void Stop() { _audioPlayer.Stop(); CurrentPosition = 0; Position = 0; } public void Previous() { _audioPlayer.PreviousAsync(); } public void Next() { _audioPlayer.NextAsync(); } }