use super::ArcServer;
use super::api::GetArtistSongsProgressUpdate;
use super::player::{DecodedInMemSong, Player};
use super::song_downloader::{DownloadProgressUpdate, InMemSong};
use super::song_thumbnail_downloader::SongThumbnail;
use crate::app::server::api::GetPlaylistSongsProgressUpdate;
use crate::app::server::song_thumbnail_downloader::SongThumbnailID;
use crate::app::structures::ListSongID;
use crate::async_rodio_sink::rodio::decoder::DecoderError;
use crate::async_rodio_sink::{
AllStopped, AutoplayUpdate, PausePlayResponse, Paused, PlayUpdate, ProgressUpdate, QueueUpdate,
Resumed, SeekDirection, Stopped, VolumeUpdate,
};
use anyhow::{Error, Result};
use async_callback_manager::{BackendStreamingTask, BackendTask, MapFn};
use futures::{Future, Stream};
use std::sync::Arc;
use std::time::Duration;
use ytmapi_rs::common::{ArtistChannelID, PlaylistID, SearchSuggestion, VideoID};
use ytmapi_rs::parse::{SearchResultArtist, SearchResultPlaylist, SearchResultSong};
#[derive(PartialEq, Debug)]
pub enum TaskMetadata {
PlayingSong,
PlayPause,
}
#[derive(Debug)]
pub struct HandleApiError {
pub error: Error,
pub message: String,
}
#[derive(Debug, PartialEq)]
pub struct GetSearchSuggestions(pub String);
#[derive(Debug, PartialEq)]
pub struct SearchArtists(pub String);
#[derive(Debug, PartialEq)]
pub struct SearchSongs(pub String);
#[derive(Debug, PartialEq)]
pub struct SearchPlaylists(pub String);
#[derive(Debug, PartialEq)]
pub struct GetArtistSongs(pub ArtistChannelID<'static>);
#[derive(Debug, PartialEq)]
pub struct GetPlaylistSongs {
pub playlist_id: PlaylistID<'static>,
pub max_songs: usize,
}
#[derive(Debug, PartialEq)]
pub struct DownloadSong(pub VideoID<'static>, pub ListSongID);
#[derive(PartialEq, Debug)]
pub struct IncreaseVolume(pub i8);
#[derive(Debug, PartialEq)]
pub struct SetVolume(pub u8);
#[derive(Debug, PartialEq)]
pub struct Seek {
pub duration: Duration,
pub direction: SeekDirection,
}
#[derive(Debug, PartialEq)]
pub struct SeekTo {
pub position: Duration,
pub id: ListSongID,
}
#[derive(Debug, PartialEq)]
pub struct Stop(pub ListSongID);
#[derive(Debug, PartialEq)]
pub struct StopAll;
#[derive(Debug, PartialEq)]
pub struct PausePlay(pub ListSongID);
#[derive(Debug, PartialEq)]
pub struct Resume(pub ListSongID);
#[derive(Debug, PartialEq)]
pub struct Pause(pub ListSongID);
#[derive(PartialEq, Debug)]
pub struct DecodeSong(pub Arc<InMemSong>);
#[derive(Debug)]
pub struct PlaySong {
pub song: DecodedInMemSong,
pub id: ListSongID,
}
#[derive(Debug)]
pub struct AutoplaySong {
pub song: DecodedInMemSong,
pub id: ListSongID,
}
#[derive(Debug)]
pub struct QueueSong {
pub song: DecodedInMemSong,
pub id: ListSongID,
}
#[derive(Debug, PartialEq)]
pub struct GetSongThumbnail {
pub thumbnail_url: String,
pub thumbnail_id: SongThumbnailID<'static>,
}
impl BackendTask<ArcServer> for HandleApiError {
type Output = ();
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let Self { error, message } = self;
let backend = backend.clone();
async move {
backend.api_error_handler.handle_error(error, message).await;
}
}
}
impl BackendTask<ArcServer> for GetSearchSuggestions {
type Output = Result<(Vec<SearchSuggestion>, String)>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.api.get_search_suggestions(self.0).await }
}
}
impl BackendTask<ArcServer> for SearchArtists {
type Output = Result<Vec<SearchResultArtist>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.api.search_artists(self.0).await }
}
}
impl BackendTask<ArcServer> for SearchSongs {
type Output = Result<Vec<SearchResultSong>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.api.search_songs(self.0).await }
}
}
impl BackendTask<ArcServer> for SearchPlaylists {
type Output = Result<Vec<SearchResultPlaylist>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.api.search_playlists(self.0).await }
}
}
impl BackendStreamingTask<ArcServer> for GetArtistSongs {
type Output = GetArtistSongsProgressUpdate;
type MetadataType = TaskMetadata;
fn into_stream(
self,
backend: &ArcServer,
) -> impl futures::Stream<Item = Self::Output> + Send + Unpin + 'static {
let backend = backend.clone();
backend.api.get_artist_songs(self.0)
}
}
impl BackendStreamingTask<ArcServer> for GetPlaylistSongs {
type Output = GetPlaylistSongsProgressUpdate;
type MetadataType = TaskMetadata;
fn into_stream(
self,
backend: &ArcServer,
) -> impl futures::Stream<Item = Self::Output> + Send + Unpin + 'static {
let backend = backend.clone();
backend
.api
.get_playlist_songs(self.playlist_id, self.max_songs)
}
}
impl BackendStreamingTask<ArcServer> for DownloadSong {
type Output = DownloadProgressUpdate;
type MetadataType = TaskMetadata;
fn into_stream(
self,
backend: &ArcServer,
) -> impl futures::Stream<Item = Self::Output> + Send + Unpin + 'static {
let backend = backend.clone();
backend.song_downloader.download_song(self.0, self.1)
}
}
impl BackendTask<ArcServer> for Seek {
type Output = Option<ProgressUpdate<ListSongID>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.seek(self.duration, self.direction).await }
}
}
impl BackendTask<ArcServer> for SeekTo {
type Output = Option<ProgressUpdate<ListSongID>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.seek_to(self.position, self.id).await }
}
}
impl BackendTask<ArcServer> for DecodeSong {
type Output = std::result::Result<DecodedInMemSong, DecoderError>;
type MetadataType = TaskMetadata;
fn into_future(
self,
_backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
Player::try_decode(self.0)
}
}
impl BackendTask<ArcServer> for IncreaseVolume {
type Output = Option<VolumeUpdate>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.increase_volume(self.0).await }
}
}
impl BackendTask<ArcServer> for SetVolume {
type Output = Option<VolumeUpdate>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.set_volume(self.0).await }
}
}
impl BackendTask<ArcServer> for Stop {
type Output = Option<Stopped<ListSongID>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.stop(self.0).await }
}
}
impl BackendTask<ArcServer> for StopAll {
type Output = Option<AllStopped>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.stop_all().await }
}
}
impl BackendTask<ArcServer> for PausePlay {
type Output = Option<PausePlayResponse<ListSongID>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.pause_play(self.0).await }
}
fn metadata() -> Vec<Self::MetadataType> {
vec![TaskMetadata::PlayPause]
}
}
impl BackendTask<ArcServer> for Resume {
type Output = Option<Resumed<ListSongID>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.resume(self.0).await }
}
fn metadata() -> Vec<Self::MetadataType> {
vec![TaskMetadata::PlayPause]
}
}
impl BackendTask<ArcServer> for Pause {
type Output = Option<Paused<ListSongID>>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move { backend.player.pause(self.0).await }
}
fn metadata() -> Vec<Self::MetadataType> {
vec![TaskMetadata::PlayPause]
}
}
impl BackendStreamingTask<ArcServer> for PlaySong {
type Output = PlayUpdate<ListSongID>;
type MetadataType = TaskMetadata;
fn into_stream(
self,
backend: &ArcServer,
) -> impl Stream<Item = Self::Output> + Send + Unpin + 'static {
let backend = backend.clone();
backend.player.play_song(self.song, self.id)
}
fn metadata() -> Vec<Self::MetadataType> {
vec![TaskMetadata::PlayingSong]
}
}
impl BackendStreamingTask<ArcServer> for AutoplaySong {
type Output = AutoplayUpdate<ListSongID>;
type MetadataType = TaskMetadata;
fn into_stream(
self,
backend: &ArcServer,
) -> impl Stream<Item = Self::Output> + Send + Unpin + 'static {
let backend = backend.clone();
backend.player.autoplay_song(self.song, self.id)
}
fn metadata() -> Vec<Self::MetadataType> {
vec![TaskMetadata::PlayingSong]
}
}
impl BackendStreamingTask<ArcServer> for QueueSong {
type Output = QueueUpdate<ListSongID>;
type MetadataType = TaskMetadata;
fn into_stream(
self,
backend: &ArcServer,
) -> impl Stream<Item = Self::Output> + Send + Unpin + 'static {
let backend = backend.clone();
backend.player.queue_song(self.song, self.id)
}
fn metadata() -> Vec<Self::MetadataType> {
vec![TaskMetadata::PlayingSong]
}
}
impl BackendTask<ArcServer> for GetSongThumbnail {
type Output = anyhow::Result<SongThumbnail>;
type MetadataType = TaskMetadata;
fn into_future(
self,
backend: &ArcServer,
) -> impl Future<Output = Self::Output> + Send + 'static {
let backend = backend.clone();
async move {
backend
.song_thumbnail_downloader
.download_song_thumbnail(self.thumbnail_id, self.thumbnail_url)
.await
}
}
}
#[derive(PartialEq, Debug)]
pub struct PlayDecodedSong(pub ListSongID);
impl MapFn<DecodedInMemSong> for PlayDecodedSong {
type Output = PlaySong;
fn apply(self, input: DecodedInMemSong) -> Self::Output {
tracing::info!("Song decoded succesfully. {:?}", self.0);
PlaySong {
song: input,
id: self.0,
}
}
}
#[derive(PartialEq, Debug)]
pub struct AutoplayDecodedSong(pub ListSongID);
impl MapFn<DecodedInMemSong> for AutoplayDecodedSong {
type Output = AutoplaySong;
fn apply(self, input: DecodedInMemSong) -> Self::Output {
tracing::info!("Song decoded succesfully. {:?}", self.0);
AutoplaySong {
song: input,
id: self.0,
}
}
}
#[derive(PartialEq, Debug)]
pub struct QueueDecodedSong(pub ListSongID);
impl MapFn<DecodedInMemSong> for QueueDecodedSong {
type Output = QueueSong;
fn apply(self, input: DecodedInMemSong) -> Self::Output {
tracing::info!("Song decoded succesfully. {:?}", self.0);
QueueSong {
song: input,
id: self.0,
}
}
}
#[cfg(any(test, clippy))]
#[allow(unexpected_cfgs)]
mod test_config {
use crate::app::server::{AutoplaySong, HandleApiError, PlaySong, QueueSong};
impl PartialEq for HandleApiError {
fn eq(&self, _: &Self) -> bool {
panic!("Unable to compare HandleApiError");
}
}
impl PartialEq for PlaySong {
fn eq(&self, _: &Self) -> bool {
panic!("Unable to compare PlaySong");
}
}
impl PartialEq for AutoplaySong {
fn eq(&self, _: &Self) -> bool {
panic!("Unable to compare AutoplaySong");
}
}
impl PartialEq for QueueSong {
fn eq(&self, _: &Self) -> bool {
panic!("Unable to compare QueueSong");
}
}
}