Passing IDs to rspotify still feels a little wonky with their `Id` changes making use of `Cow`. Maybe it can be simplified on our end, but it's a start. Fixes #844 See also: https://github.com/ramsayleung/rspotify/pull/332
231 lines
6.6 KiB
Rust
231 lines
6.6 KiB
Rust
use chrono::{DateTime, Utc};
|
|
use rspotify::model::PlayableItem;
|
|
|
|
use crate::library::Library;
|
|
use crate::model::album::Album;
|
|
use crate::model::artist::Artist;
|
|
use crate::model::episode::Episode;
|
|
use crate::model::track::Track;
|
|
use crate::queue::Queue;
|
|
use crate::traits::{ListItem, ViewExt};
|
|
use std::fmt;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
|
#[serde(tag = "type")]
|
|
pub enum Playable {
|
|
Track(Track),
|
|
Episode(Episode),
|
|
}
|
|
|
|
impl Playable {
|
|
pub fn format(playable: &Playable, formatting: &str, library: Arc<Library>) -> String {
|
|
formatting
|
|
.replace(
|
|
"%artists",
|
|
if let Some(artists) = playable.artists() {
|
|
artists
|
|
.iter()
|
|
.map(|artist| artist.clone().name)
|
|
.collect::<Vec<String>>()
|
|
.join(", ")
|
|
} else {
|
|
String::new()
|
|
}
|
|
.as_str(),
|
|
)
|
|
.replace(
|
|
"%title",
|
|
match playable.clone() {
|
|
Playable::Episode(episode) => episode.name,
|
|
Playable::Track(track) => track.title,
|
|
}
|
|
.as_str(),
|
|
)
|
|
.replace(
|
|
"%album",
|
|
match playable.clone() {
|
|
Playable::Track(track) => track.album.unwrap_or_default(),
|
|
_ => String::new(),
|
|
}
|
|
.as_str(),
|
|
)
|
|
.replace(
|
|
"%saved",
|
|
if library.is_saved_track(&match playable.clone() {
|
|
Playable::Episode(episode) => Playable::Episode(episode),
|
|
Playable::Track(track) => Playable::Track(track),
|
|
}) {
|
|
if library.cfg.values().use_nerdfont.unwrap_or_default() {
|
|
"\u{f62b}"
|
|
} else {
|
|
"✓"
|
|
}
|
|
} else {
|
|
""
|
|
},
|
|
)
|
|
.replace("%duration", playable.duration_str().as_str())
|
|
}
|
|
|
|
pub fn id(&self) -> Option<String> {
|
|
match self {
|
|
Playable::Track(track) => track.id.clone(),
|
|
Playable::Episode(episode) => Some(episode.id.clone()),
|
|
}
|
|
}
|
|
|
|
pub fn uri(&self) -> String {
|
|
match self {
|
|
Playable::Track(track) => track.uri.clone(),
|
|
Playable::Episode(episode) => episode.uri.clone(),
|
|
}
|
|
}
|
|
|
|
pub fn cover_url(&self) -> Option<String> {
|
|
match self {
|
|
Playable::Track(track) => track.cover_url.clone(),
|
|
Playable::Episode(episode) => episode.cover_url.clone(),
|
|
}
|
|
}
|
|
|
|
pub fn duration(&self) -> u32 {
|
|
match self {
|
|
Playable::Track(track) => track.duration,
|
|
Playable::Episode(episode) => episode.duration,
|
|
}
|
|
}
|
|
|
|
pub fn list_index(&self) -> usize {
|
|
match self {
|
|
Playable::Track(track) => track.list_index,
|
|
Playable::Episode(episode) => episode.list_index,
|
|
}
|
|
}
|
|
|
|
pub fn set_list_index(&mut self, index: usize) {
|
|
match self {
|
|
Playable::Track(track) => track.list_index = index,
|
|
Playable::Episode(episode) => episode.list_index = index,
|
|
}
|
|
}
|
|
|
|
pub fn set_added_at(&mut self, added_at: Option<DateTime<Utc>>) {
|
|
match self {
|
|
Playable::Track(track) => track.added_at = added_at,
|
|
Playable::Episode(episode) => episode.added_at = added_at,
|
|
}
|
|
}
|
|
|
|
pub fn duration_str(&self) -> String {
|
|
let duration = self.duration();
|
|
let minutes = duration / 60_000;
|
|
let seconds = (duration / 1000) % 60;
|
|
format!("{:02}:{:02}", minutes, seconds)
|
|
}
|
|
|
|
pub fn as_listitem(&self) -> Box<dyn ListItem> {
|
|
match self {
|
|
Playable::Track(track) => track.as_listitem(),
|
|
Playable::Episode(episode) => episode.as_listitem(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&PlayableItem> for Playable {
|
|
fn from(item: &PlayableItem) -> Self {
|
|
match item {
|
|
PlayableItem::Episode(episode) => Playable::Episode(episode.into()),
|
|
PlayableItem::Track(track) => Playable::Track(track.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&Playable> for rspotify::prelude::PlayableId<'_> {
|
|
fn from(p: &Playable) -> Self {
|
|
match p {
|
|
Playable::Track(t) => rspotify::prelude::PlayableId::Track(
|
|
rspotify::model::TrackId::from_id(t.id.clone().unwrap()).unwrap(),
|
|
),
|
|
Playable::Episode(e) => rspotify::prelude::PlayableId::Episode(
|
|
rspotify::model::EpisodeId::from_id(e.id.clone()).unwrap(),
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for Playable {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self {
|
|
Playable::Track(track) => track.fmt(f),
|
|
Playable::Episode(episode) => episode.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl ListItem for Playable {
|
|
fn is_playing(&self, queue: Arc<Queue>) -> bool {
|
|
self.as_listitem().is_playing(queue)
|
|
}
|
|
|
|
fn display_left(&self, library: Arc<Library>) -> String {
|
|
self.as_listitem().display_left(library)
|
|
}
|
|
|
|
fn display_center(&self, library: Arc<Library>) -> String {
|
|
self.as_listitem().display_center(library)
|
|
}
|
|
|
|
fn display_right(&self, library: Arc<Library>) -> String {
|
|
self.as_listitem().display_right(library)
|
|
}
|
|
|
|
fn play(&mut self, queue: Arc<Queue>) {
|
|
self.as_listitem().play(queue)
|
|
}
|
|
|
|
fn play_next(&mut self, queue: Arc<Queue>) {
|
|
self.as_listitem().play_next(queue)
|
|
}
|
|
|
|
fn queue(&mut self, queue: Arc<Queue>) {
|
|
self.as_listitem().queue(queue)
|
|
}
|
|
|
|
fn toggle_saved(&mut self, library: Arc<Library>) {
|
|
self.as_listitem().toggle_saved(library)
|
|
}
|
|
|
|
fn save(&mut self, library: Arc<Library>) {
|
|
self.as_listitem().save(library)
|
|
}
|
|
|
|
fn unsave(&mut self, library: Arc<Library>) {
|
|
self.as_listitem().unsave(library)
|
|
}
|
|
|
|
fn open(&self, queue: Arc<Queue>, library: Arc<Library>) -> Option<Box<dyn ViewExt>> {
|
|
self.as_listitem().open(queue, library)
|
|
}
|
|
|
|
fn share_url(&self) -> Option<String> {
|
|
self.as_listitem().share_url()
|
|
}
|
|
|
|
fn album(&self, queue: Arc<Queue>) -> Option<Album> {
|
|
self.as_listitem().album(queue)
|
|
}
|
|
|
|
fn artists(&self) -> Option<Vec<Artist>> {
|
|
self.as_listitem().artists()
|
|
}
|
|
|
|
fn track(&self) -> Option<Track> {
|
|
self.as_listitem().track()
|
|
}
|
|
|
|
fn as_listitem(&self) -> Box<dyn ListItem> {
|
|
self.as_listitem()
|
|
}
|
|
}
|