podcast support (#203)

* implement search for shows/podcasts

* create Playable supertype for queue to contain tracks and episodes

* wip: implement playback of episodes

* load spotify id from uri instead of raw id to fix podcast playback

* show duration for podcast episodes

* implement generic status bar for playables (tracks and episodes)

omit saved indicator for now as the library does not yet support podcasts

* instead of only the last 50 fetch all episodes of a show

* refactor: extract Playable code to separate file

* implement playback/queuing of shows + sharing url

* implement podcast library

* migrate mpris code to Playable supertype
This commit is contained in:
Henrik Friedrichsen
2020-07-14 10:38:22 +02:00
committed by GitHub
parent 8bf06147e2
commit 1b1d392ab8
19 changed files with 723 additions and 115 deletions

View File

@@ -56,13 +56,15 @@ use std::{env, io};
use crate::artist::Artist;
use crate::config;
use crate::events::{Event, EventManager};
use crate::playable::Playable;
use crate::queue;
use crate::track::Track;
use rspotify::model::show::{Show, SimplifiedEpisode};
pub const VOLUME_PERCENT: u16 = ((u16::max_value() as f64) * 1.0 / 100.0) as u16;
enum WorkerCommand {
Load(Box<Track>),
Load(Playable),
Play,
Pause,
Stop,
@@ -159,15 +161,16 @@ impl futures::Future for Worker {
progress = true;
debug!("message received!");
match cmd {
WorkerCommand::Load(track) => {
if let Some(track_id) = &track.id {
let id = SpotifyId::from_base62(track_id).expect("could not parse id");
WorkerCommand::Load(playable) => match SpotifyId::from_uri(&playable.uri()) {
Ok(id) => {
self.play_task = Box::pin(self.player.load(id, true, 0).compat());
info!("player loading track: {:?}", track);
} else {
info!("player loading track: {:?}", playable);
}
Err(e) => {
error!("error parsing uri: {:?}", e);
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
}
}
},
WorkerCommand::Play => {
self.player.play();
}
@@ -588,12 +591,12 @@ impl Spotify {
.is_some()
}
pub fn overwrite_playlist(&self, id: &str, tracks: &[Track]) {
pub fn overwrite_playlist(&self, id: &str, tracks: &[Playable]) {
// extract only track IDs
let mut tracks: Vec<String> = tracks
.iter()
.filter(|track| track.id.is_some())
.map(|track| track.id.clone().unwrap())
.filter(|track| track.id().is_some())
.map(|track| track.id().clone().unwrap())
.collect();
// we can only send 100 tracks per request
@@ -712,6 +715,24 @@ impl Spotify {
})
}
pub fn show_episodes(&self, show_id: &str, offset: u32) -> Option<Page<SimplifiedEpisode>> {
self.api_with_retry(|api| api.get_shows_episodes(show_id.to_string(), 50, offset, None))
}
pub fn get_saved_shows(&self, offset: u32) -> Option<Page<Show>> {
self.api_with_retry(|api| api.get_saved_show(50, offset))
}
pub fn save_shows(&self, ids: Vec<String>) -> bool {
self.api_with_retry(|api| api.save_shows(ids.clone()))
.is_some()
}
pub fn unsave_shows(&self, ids: Vec<String>) -> bool {
self.api_with_retry(|api| api.remove_users_saved_shows(ids.clone(), None))
.is_some()
}
pub fn current_user_followed_artists(
&self,
last: Option<String>,
@@ -770,9 +791,9 @@ impl Spotify {
self.api_with_retry(|api| api.current_user())
}
pub fn load(&self, track: &Track) {
pub fn load(&self, track: &Playable) {
info!("loading track: {:?}", track);
self.send_worker(WorkerCommand::Load(Box::new(track.clone())));
self.send_worker(WorkerCommand::Load(track.clone()));
}
pub fn update_status(&self, new_status: PlayerEvent) {