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

@@ -13,7 +13,9 @@ use crate::album::Album;
use crate::artist::Artist;
use crate::config;
use crate::events::EventManager;
use crate::playable::Playable;
use crate::playlist::Playlist;
use crate::show::Show;
use crate::spotify::Spotify;
use crate::track::Track;
@@ -28,6 +30,7 @@ pub struct Library {
pub albums: Arc<RwLock<Vec<Album>>>,
pub artists: Arc<RwLock<Vec<Artist>>>,
pub playlists: Arc<RwLock<Vec<Playlist>>>,
pub shows: Arc<RwLock<Vec<Show>>>,
pub is_done: Arc<RwLock<bool>>,
user_id: Option<String>,
ev: EventManager,
@@ -44,6 +47,7 @@ impl Library {
albums: Arc::new(RwLock::new(Vec::new())),
artists: Arc::new(RwLock::new(Vec::new())),
playlists: Arc::new(RwLock::new(Vec::new())),
shows: Arc::new(RwLock::new(Vec::new())),
is_done: Arc::new(RwLock::new(false)),
user_id,
ev: ev.clone(),
@@ -140,15 +144,15 @@ impl Library {
}
}
pub fn overwrite_playlist(&self, id: &str, tracks: &[Track]) {
debug!("saving {} tracks to {}", tracks.len(), id);
pub fn overwrite_playlist(&self, id: &str, tracks: &[Playable]) {
debug!("saving {} tracks to list {}", tracks.len(), id);
self.spotify.overwrite_playlist(id, &tracks);
self.fetch_playlists();
self.save_cache(config::cache_path(CACHE_PLAYLISTS), self.playlists.clone());
}
pub fn save_playlist(&self, name: &str, tracks: &[Track]) {
pub fn save_playlist(&self, name: &str, tracks: &[Playable]) {
debug!("saving {} tracks to new list {}", tracks.len(), name);
match self.spotify.create_playlist(name, None, None) {
Some(id) => self.overwrite_playlist(&id, &tracks),
@@ -202,6 +206,13 @@ impl Library {
})
};
let t_shows = {
let library = library.clone();
thread::spawn(move || {
library.fetch_shows();
})
};
t_tracks.join().unwrap();
t_artists.join().unwrap();
@@ -210,6 +221,7 @@ impl Library {
t_albums.join().unwrap();
t_playlists.join().unwrap();
t_shows.join().unwrap();
let mut is_done = library.is_done.write().unwrap();
*is_done = true;
@@ -218,6 +230,29 @@ impl Library {
});
}
fn fetch_shows(&self) {
debug!("loading shows");
let mut saved_shows: Vec<Show> = Vec::new();
let mut shows_result = self.spotify.get_saved_shows(0);
while let Some(shows) = shows_result.as_ref() {
saved_shows.extend(shows.items.iter().map(|show| (&show.show).into()));
// load next batch if necessary
shows_result = match shows.next {
Some(_) => {
debug!("requesting shows again..");
self.spotify
.get_saved_shows(shows.offset + shows.items.len() as u32)
}
None => None,
}
}
*self.shows.write().unwrap() = saved_shows;
}
fn fetch_playlists(&self) {
debug!("loading playlists");
let mut stale_lists = self.playlists.read().unwrap().clone();
@@ -512,13 +547,13 @@ impl Library {
}
}
pub fn is_saved_track(&self, track: &Track) -> bool {
pub fn is_saved_track(&self, track: &Playable) -> bool {
if !*self.is_done.read().unwrap() {
return false;
}
let tracks = self.tracks.read().unwrap();
tracks.iter().any(|t| t.id == track.id)
tracks.iter().any(|t| t.id == track.id())
}
pub fn save_tracks(&self, tracks: Vec<&Track>, api: bool) {
@@ -773,6 +808,43 @@ impl Library {
self.save_cache(config::cache_path(CACHE_PLAYLISTS), self.playlists.clone());
}
pub fn is_saved_show(&self, show: &Show) -> bool {
if !*self.is_done.read().unwrap() {
return false;
}
let shows = self.shows.read().unwrap();
shows.iter().any(|s| s.id == show.id)
}
pub fn save_show(&self, show: &Show) {
if !*self.is_done.read().unwrap() {
return;
}
if self.spotify.save_shows(vec![show.id.clone()]) {
{
let mut store = self.shows.write().unwrap();
if !store.iter().any(|s| s.id == show.id) {
store.insert(0, show.clone());
}
}
}
}
pub fn unsave_show(&self, show: &Show) {
if !*self.is_done.read().unwrap() {
return;
}
if self.spotify.unsave_shows(vec![show.id.clone()]) {
{
let mut store = self.shows.write().unwrap();
*store = store.iter().filter(|s| s.id != show.id).cloned().collect();
}
}
}
pub fn trigger_redraw(&self) {
self.ev.trigger();
}