227 lines
5.4 KiB
C#
227 lines
5.4 KiB
C#
using Avalonia.Media.Imaging;
|
|
using Avalonia.Threading;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
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.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Input;
|
|
|
|
namespace Harmonia.UI.ViewModels;
|
|
|
|
public partial class PlaybackBarViewModel : ViewModelBase, IDisposable
|
|
{
|
|
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();
|
|
OnPropertyChanged(nameof(FormattedSongInfo));
|
|
|
|
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?.Dispose();
|
|
_songImageSource = value;
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
|
|
public string FormattedSongInfo => Song != null
|
|
? $"{Song.FileType} - {Song.BitRate} kbps - {Song.SampleRate} Hz"
|
|
: string.Empty;
|
|
|
|
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 ICommand PlaySongCommand => new RelayCommand(Play);
|
|
public ICommand PauseSongCommand => new RelayCommand(Pause);
|
|
public ICommand StopSongCommand => new RelayCommand(Stop);
|
|
public ICommand PreviousSongCommand => new RelayCommand(Previous);
|
|
public ICommand NextSongCommand => new RelayCommand(Next);
|
|
|
|
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)
|
|
{
|
|
if (songPictureInfo.Data.Length == 0)
|
|
return;
|
|
|
|
using MemoryStream stream = new(songPictureInfo.Data);
|
|
SongImageSource = new(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();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
SongImageSource?.Dispose();
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
} |