Refactor: extract Spotify Worker to separate file

This commit is contained in:
Henrik Friedrichsen
2021-04-03 21:38:42 +02:00
parent 8b5bc64dc6
commit 8483653cde
8 changed files with 258 additions and 248 deletions

View File

@@ -64,6 +64,7 @@ mod sharing;
mod show; mod show;
mod spotify; mod spotify;
mod spotify_url; mod spotify_url;
mod spotify_worker;
mod theme; mod theme;
mod track; mod track;
mod traits; mod traits;

View File

@@ -19,7 +19,7 @@ use crate::playable::Playable;
use crate::playlist::Playlist; use crate::playlist::Playlist;
use crate::queue::{Queue, RepeatSetting}; use crate::queue::{Queue, RepeatSetting};
use crate::show::Show; use crate::show::Show;
use crate::spotify::{PlayerEvent, Spotify, URIType, VOLUME_PERCENT}; use crate::spotify::{PlayerEvent, Spotify, UriType, VOLUME_PERCENT};
use crate::track::Track; use crate::track::Track;
use crate::traits::ListItem; use crate::traits::ListItem;
use regex::Regex; use regex::Regex;
@@ -538,9 +538,9 @@ fn run_dbus_server(
None => "".to_string(), None => "".to_string(),
}; };
let id = &uri[uri.rfind(':').unwrap_or(0) + 1..uri.len()]; let id = &uri[uri.rfind(':').unwrap_or(0) + 1..uri.len()];
let uri_type = URIType::from_uri(&uri); let uri_type = UriType::from_uri(&uri);
match uri_type { match uri_type {
Some(URIType::Album) => { Some(UriType::Album) => {
if let Some(a) = spotify.album(&id) { if let Some(a) = spotify.album(&id) {
if let Some(t) = &Album::from(&a).tracks { if let Some(t) = &Album::from(&a).tracks {
queue.clear(); queue.clear();
@@ -553,14 +553,14 @@ fn run_dbus_server(
} }
} }
} }
Some(URIType::Track) => { Some(UriType::Track) => {
if let Some(t) = spotify.track(&id) { if let Some(t) = spotify.track(&id) {
queue.clear(); queue.clear();
queue.append(Playable::Track(Track::from(&t))); queue.append(Playable::Track(Track::from(&t)));
queue.play(0, false, false) queue.play(0, false, false)
} }
} }
Some(URIType::Playlist) => { Some(UriType::Playlist) => {
if let Some(p) = spotify.playlist(&id) { if let Some(p) = spotify.playlist(&id) {
let mut playlist = Playlist::from(&p); let mut playlist = Playlist::from(&p);
let spotify = spotify.clone(); let spotify = spotify.clone();
@@ -576,7 +576,7 @@ fn run_dbus_server(
} }
} }
} }
Some(URIType::Show) => { Some(UriType::Show) => {
if let Some(s) = spotify.get_show(&id) { if let Some(s) = spotify.get_show(&id) {
let mut show = Show::from(&s); let mut show = Show::from(&s);
let spotify = spotify.clone(); let spotify = spotify.clone();
@@ -594,14 +594,14 @@ fn run_dbus_server(
} }
} }
} }
Some(URIType::Episode) => { Some(UriType::Episode) => {
if let Some(e) = spotify.episode(&id) { if let Some(e) = spotify.episode(&id) {
queue.clear(); queue.clear();
queue.append(Playable::Episode(Episode::from(&e))); queue.append(Playable::Episode(Episode::from(&e)));
queue.play(0, false, false) queue.play(0, false, false)
} }
} }
Some(URIType::Artist) => { Some(UriType::Artist) => {
if let Some(a) = spotify.artist_top_tracks(&id) { if let Some(a) = spotify.artist_top_tracks(&id) {
queue.clear(); queue.clear();
queue.append_next(a.iter().map(|track| Playable::Track(track.clone())).collect()); queue.append_next(a.iter().map(|track| Playable::Track(track.clone())).collect());

View File

@@ -4,13 +4,11 @@ use librespot_core::config::SessionConfig;
use librespot_core::keymaster::Token; use librespot_core::keymaster::Token;
use librespot_core::mercury::MercuryError; use librespot_core::mercury::MercuryError;
use librespot_core::session::Session; use librespot_core::session::Session;
use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId};
use librespot_playback::config::PlayerConfig; use librespot_playback::config::PlayerConfig;
use librespot_playback::audio_backend; use librespot_playback::audio_backend;
use librespot_playback::config::Bitrate; use librespot_playback::config::Bitrate;
use librespot_playback::mixer::Mixer; use librespot_playback::player::Player;
use librespot_playback::player::{Player, PlayerEvent as LibrespotPlayerEvent};
use rspotify::blocking::client::Spotify as SpotifyAPI; use rspotify::blocking::client::Spotify as SpotifyAPI;
use rspotify::model::album::{FullAlbum, SavedAlbum, SimplifiedAlbum}; use rspotify::model::album::{FullAlbum, SavedAlbum, SimplifiedAlbum};
@@ -28,23 +26,15 @@ use serde_json::{json, Map};
use failure::Error; use failure::Error;
use futures_01::future::Future as v01_Future; use futures_01::future::Future as v01_Future;
use futures_01::stream::Stream as v01_Stream;
use futures_01::sync::mpsc::UnboundedReceiver;
use futures_01::Async as v01_Async;
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::channel::oneshot; use futures::channel::oneshot;
use futures::compat::Future01CompatExt; use futures::compat::Future01CompatExt;
use futures::compat::Stream01CompatExt;
use futures::task::Context;
use futures::Future; use futures::Future;
use futures::Stream;
use tokio_core::reactor::Core; use tokio_core::reactor::Core;
use url::Url; use url::Url;
use core::task::Poll;
use std::pin::Pin; use std::pin::Pin;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, RwLock}; use std::sync::{Arc, RwLock};
@@ -56,6 +46,7 @@ use crate::artist::Artist;
use crate::config; use crate::config;
use crate::events::{Event, EventManager}; use crate::events::{Event, EventManager};
use crate::playable::Playable; use crate::playable::Playable;
use crate::spotify_worker::{Worker, WorkerCommand};
use crate::track::Track; use crate::track::Track;
use rspotify::model::recommend::Recommendations; use rspotify::model::recommend::Recommendations;
@@ -63,17 +54,6 @@ use rspotify::model::show::{FullEpisode, FullShow, Show, SimplifiedEpisode};
pub const VOLUME_PERCENT: u16 = ((u16::max_value() as f64) * 1.0 / 100.0) as u16; pub const VOLUME_PERCENT: u16 = ((u16::max_value() as f64) * 1.0 / 100.0) as u16;
enum WorkerCommand {
Load(Playable),
Play,
Pause,
Stop,
Seek(u32),
SetVolume(u16),
RequestToken(oneshot::Sender<Token>),
Shutdown,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum PlayerEvent { pub enum PlayerEvent {
Playing, Playing,
@@ -96,165 +76,6 @@ pub struct Spotify {
country: Option<Country>, country: Option<Country>,
} }
struct Worker {
events: EventManager,
player_events: UnboundedReceiver<LibrespotPlayerEvent>,
commands: Pin<Box<mpsc::UnboundedReceiver<WorkerCommand>>>,
session: Session,
player: Player,
refresh_task: Pin<Box<dyn Stream<Item = Result<(), tokio_timer::Error>>>>,
token_task: Pin<Box<dyn Future<Output = Result<(), MercuryError>>>>,
active: bool,
mixer: Box<dyn Mixer>,
}
impl Worker {
fn new(
events: EventManager,
player_events: UnboundedReceiver<LibrespotPlayerEvent>,
commands: Pin<Box<mpsc::UnboundedReceiver<WorkerCommand>>>,
session: Session,
player: Player,
mixer: Box<dyn Mixer>,
) -> Worker {
Worker {
events,
player_events,
commands,
player,
session,
refresh_task: Box::pin(futures::stream::empty()),
token_task: Box::pin(futures::future::pending()),
active: false,
mixer,
}
}
}
impl Worker {
fn create_refresh(&self) -> Pin<Box<dyn Stream<Item = Result<(), tokio_timer::Error>>>> {
let ev = self.events.clone();
let future =
tokio_timer::Interval::new_interval(Duration::from_millis(400)).map(move |_| {
ev.trigger();
});
Box::pin(future.compat())
}
}
impl futures::Future for Worker {
type Output = Result<(), ()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> futures::task::Poll<Self::Output> {
loop {
let mut progress = false;
if self.session.is_invalid() {
self.events.send(Event::Player(PlayerEvent::Stopped));
return Poll::Ready(Result::Err(()));
}
if let Poll::Ready(Some(cmd)) = self.commands.as_mut().poll_next(cx) {
progress = true;
debug!("message received!");
match cmd {
WorkerCommand::Load(playable) => match SpotifyId::from_uri(&playable.uri()) {
Ok(id) => {
info!("player loading track: {:?}", id);
if id.audio_type == SpotifyAudioType::NonPlayable {
warn!("track is not playable");
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
} else {
self.player.load(id, true, 0);
}
}
Err(e) => {
error!("error parsing uri: {:?}", e);
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
}
},
WorkerCommand::Play => {
self.player.play();
}
WorkerCommand::Pause => {
self.player.pause();
}
WorkerCommand::Stop => {
self.player.stop();
}
WorkerCommand::Seek(pos) => {
self.player.seek(pos);
}
WorkerCommand::SetVolume(volume) => {
self.mixer.set_volume(volume);
}
WorkerCommand::RequestToken(sender) => {
self.token_task = Spotify::get_token(&self.session, sender);
progress = true;
}
WorkerCommand::Shutdown => {
self.player.stop();
self.session.shutdown();
}
}
}
if let Ok(v01_Async::Ready(Some(event))) = self.player_events.poll() {
debug!("librespot player event: {:?}", event);
match event {
LibrespotPlayerEvent::Started { .. }
| LibrespotPlayerEvent::Loading { .. }
| LibrespotPlayerEvent::Changed { .. } => {
progress = true;
}
LibrespotPlayerEvent::Playing { .. } => {
self.events.send(Event::Player(PlayerEvent::Playing));
self.refresh_task = self.create_refresh();
self.active = true;
}
LibrespotPlayerEvent::Paused { .. } => {
self.events.send(Event::Player(PlayerEvent::Paused));
self.active = false;
}
LibrespotPlayerEvent::Stopped { .. } => {
self.events.send(Event::Player(PlayerEvent::Stopped));
self.active = false;
}
LibrespotPlayerEvent::EndOfTrack { .. } => {
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
progress = true;
}
_ => {}
}
}
if let Poll::Ready(Some(Ok(_))) = self.refresh_task.as_mut().poll_next(cx) {
self.refresh_task = if self.active {
progress = true;
self.create_refresh()
} else {
Box::pin(futures::stream::empty())
};
}
match self.token_task.as_mut().poll(cx) {
Poll::Ready(Ok(_)) => {
info!("token updated!");
self.token_task = Box::pin(futures::future::pending())
}
Poll::Ready(Err(e)) => {
error!("could not generate token: {:?}", e);
}
_ => (),
}
if !progress {
return Poll::Pending;
}
}
}
}
impl Spotify { impl Spotify {
pub fn new( pub fn new(
events: EventManager, events: EventManager,
@@ -365,7 +186,7 @@ impl Spotify {
.expect("could not open spotify session") .expect("could not open spotify session")
} }
fn get_token( pub(crate) fn get_token(
session: &Session, session: &Session,
sender: oneshot::Sender<Token>, sender: oneshot::Sender<Token>,
) -> Pin<Box<dyn Future<Output = Result<(), MercuryError>>>> { ) -> Pin<Box<dyn Future<Output = Result<(), MercuryError>>>> {
@@ -955,7 +776,7 @@ impl Spotify {
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum URIType { pub enum UriType {
Album, Album,
Artist, Artist,
Track, Track,
@@ -964,20 +785,20 @@ pub enum URIType {
Episode, Episode,
} }
impl URIType { impl UriType {
pub fn from_uri(s: &str) -> Option<URIType> { pub fn from_uri(s: &str) -> Option<UriType> {
if s.starts_with("spotify:album:") { if s.starts_with("spotify:album:") {
Some(URIType::Album) Some(UriType::Album)
} else if s.starts_with("spotify:artist:") { } else if s.starts_with("spotify:artist:") {
Some(URIType::Artist) Some(UriType::Artist)
} else if s.starts_with("spotify:track:") { } else if s.starts_with("spotify:track:") {
Some(URIType::Track) Some(UriType::Track)
} else if s.starts_with("spotify:") && s.contains(":playlist:") { } else if s.starts_with("spotify:") && s.contains(":playlist:") {
Some(URIType::Playlist) Some(UriType::Playlist)
} else if s.starts_with("spotify:show:") { } else if s.starts_with("spotify:show:") {
Some(URIType::Show) Some(UriType::Show)
} else if s.starts_with("spotify:episode:") { } else if s.starts_with("spotify:episode:") {
Some(URIType::Episode) Some(UriType::Episode)
} else { } else {
None None
} }

View File

@@ -1,15 +1,15 @@
use crate::spotify::URIType; use crate::spotify::UriType;
use url::{Host, Url}; use url::{Host, Url};
pub struct SpotifyURL { pub struct SpotifyUrl {
pub id: String, pub id: String,
pub uri_type: URIType, pub uri_type: UriType,
} }
impl SpotifyURL { impl SpotifyUrl {
fn new(id: &str, uri_type: URIType) -> SpotifyURL { fn new(id: &str, uri_type: UriType) -> SpotifyUrl {
SpotifyURL { SpotifyUrl {
id: id.to_string(), id: id.to_string(),
uri_type, uri_type,
} }
@@ -22,7 +22,7 @@ impl SpotifyURL {
/// assert_eq!(result.id, "4uLU6hMCjMI75M1A2tKUQC"); /// assert_eq!(result.id, "4uLU6hMCjMI75M1A2tKUQC");
/// assert_eq!(result.uri_type, URIType::Track); /// assert_eq!(result.uri_type, URIType::Track);
/// ``` /// ```
pub fn from_url(s: &str) -> Option<SpotifyURL> { pub fn from_url(s: &str) -> Option<SpotifyUrl> {
let url = Url::parse(s).ok()?; let url = Url::parse(s).ok()?;
if url.host() != Some(Host::Domain("open.spotify.com")) { if url.host() != Some(Host::Domain("open.spotify.com")) {
return None; return None;
@@ -33,12 +33,12 @@ impl SpotifyURL {
let entity = path_segments.next()?; let entity = path_segments.next()?;
let uri_type = match entity.to_lowercase().as_str() { let uri_type = match entity.to_lowercase().as_str() {
"album" => Some(URIType::Album), "album" => Some(UriType::Album),
"artist" => Some(URIType::Artist), "artist" => Some(UriType::Artist),
"episode" => Some(URIType::Episode), "episode" => Some(UriType::Episode),
"playlist" => Some(URIType::Playlist), "playlist" => Some(UriType::Playlist),
"show" => Some(URIType::Show), "show" => Some(UriType::Show),
"track" => Some(URIType::Track), "track" => Some(UriType::Track),
"user" => { "user" => {
let _user_id = path_segments.next()?; let _user_id = path_segments.next()?;
let entity = path_segments.next()?; let entity = path_segments.next()?;
@@ -47,14 +47,14 @@ impl SpotifyURL {
return None; return None;
} }
Some(URIType::Playlist) Some(UriType::Playlist)
} }
_ => None, _ => None,
}?; }?;
let id = path_segments.next()?; let id = path_segments.next()?;
Some(SpotifyURL::new(id, uri_type)) Some(SpotifyUrl::new(id, uri_type))
} }
} }
@@ -62,39 +62,39 @@ impl SpotifyURL {
mod tests { mod tests {
use std::collections::HashMap; use std::collections::HashMap;
use super::SpotifyURL; use super::SpotifyUrl;
use crate::spotify::URIType; use crate::spotify::UriType;
#[test] #[test]
fn test_urls() { fn test_urls() {
let mut test_cases = HashMap::new(); let mut test_cases = HashMap::new();
test_cases.insert( test_cases.insert(
"https://open.spotify.com/playlist/1XFxe8bkTryTODn0lk4CNa?si=FfSpZ6KPQdieClZbwHakOQ", "https://open.spotify.com/playlist/1XFxe8bkTryTODn0lk4CNa?si=FfSpZ6KPQdieClZbwHakOQ",
SpotifyURL::new("1XFxe8bkTryTODn0lk4CNa", URIType::Playlist), SpotifyUrl::new("1XFxe8bkTryTODn0lk4CNa", UriType::Playlist),
); );
test_cases.insert( test_cases.insert(
"https://open.spotify.com/track/6fRJg3R90w0juYoCJXxj2d", "https://open.spotify.com/track/6fRJg3R90w0juYoCJXxj2d",
SpotifyURL::new("6fRJg3R90w0juYoCJXxj2d", URIType::Track), SpotifyUrl::new("6fRJg3R90w0juYoCJXxj2d", UriType::Track),
); );
test_cases.insert( test_cases.insert(
"https://open.spotify.com/user/~villainy~/playlist/0OgoSs65CLDPn6AF6tsZVg", "https://open.spotify.com/user/~villainy~/playlist/0OgoSs65CLDPn6AF6tsZVg",
SpotifyURL::new("0OgoSs65CLDPn6AF6tsZVg", URIType::Playlist), SpotifyUrl::new("0OgoSs65CLDPn6AF6tsZVg", UriType::Playlist),
); );
test_cases.insert( test_cases.insert(
"https://open.spotify.com/show/4MZfJbM2MXzZdPbv6gi5lJ", "https://open.spotify.com/show/4MZfJbM2MXzZdPbv6gi5lJ",
SpotifyURL::new("4MZfJbM2MXzZdPbv6gi5lJ", URIType::Show), SpotifyUrl::new("4MZfJbM2MXzZdPbv6gi5lJ", UriType::Show),
); );
test_cases.insert( test_cases.insert(
"https://open.spotify.com/episode/3QE6rfmjRaeqXSqeWcIWF6", "https://open.spotify.com/episode/3QE6rfmjRaeqXSqeWcIWF6",
SpotifyURL::new("3QE6rfmjRaeqXSqeWcIWF6", URIType::Episode), SpotifyUrl::new("3QE6rfmjRaeqXSqeWcIWF6", UriType::Episode),
); );
test_cases.insert( test_cases.insert(
"https://open.spotify.com/artist/6LEeAFiJF8OuPx747e1wxR", "https://open.spotify.com/artist/6LEeAFiJF8OuPx747e1wxR",
SpotifyURL::new("6LEeAFiJF8OuPx747e1wxR", URIType::Artist), SpotifyUrl::new("6LEeAFiJF8OuPx747e1wxR", UriType::Artist),
); );
for case in test_cases { for case in test_cases {
let result = SpotifyURL::from_url(case.0).unwrap(); let result = SpotifyUrl::from_url(case.0).unwrap();
assert_eq!(result.id, case.1.id); assert_eq!(result.id, case.1.id);
assert_eq!(result.uri_type, case.1.uri_type); assert_eq!(result.uri_type, case.1.uri_type);
} }

188
src/spotify_worker.rs Normal file
View File

@@ -0,0 +1,188 @@
use crate::events::{Event, EventManager};
use crate::playable::Playable;
use crate::spotify::{PlayerEvent, Spotify};
use futures::channel::{mpsc, oneshot};
use futures::compat::Stream01CompatExt;
use futures::task::{Context, Poll};
use futures::{Future, Stream};
use futures_01::stream::Stream as v01_Stream;
use futures_01::sync::mpsc::UnboundedReceiver;
use futures_01::Async as v01_Async;
use librespot_core::keymaster::Token;
use librespot_core::mercury::MercuryError;
use librespot_core::session::Session;
use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId};
use librespot_playback::mixer::Mixer;
use librespot_playback::player::{Player, PlayerEvent as LibrespotPlayerEvent};
use std::pin::Pin;
use std::time::Duration;
pub(crate) enum WorkerCommand {
Load(Playable),
Play,
Pause,
Stop,
Seek(u32),
SetVolume(u16),
RequestToken(oneshot::Sender<Token>),
Shutdown,
}
pub struct Worker {
events: EventManager,
player_events: UnboundedReceiver<LibrespotPlayerEvent>,
commands: Pin<Box<mpsc::UnboundedReceiver<WorkerCommand>>>,
session: Session,
player: Player,
refresh_task: Pin<Box<dyn Stream<Item = Result<(), tokio_timer::Error>>>>,
token_task: Pin<Box<dyn Future<Output = Result<(), MercuryError>>>>,
active: bool,
mixer: Box<dyn Mixer>,
}
impl Worker {
pub(crate) fn new(
events: EventManager,
player_events: UnboundedReceiver<LibrespotPlayerEvent>,
commands: Pin<Box<mpsc::UnboundedReceiver<WorkerCommand>>>,
session: Session,
player: Player,
mixer: Box<dyn Mixer>,
) -> Worker {
Worker {
events,
player_events,
commands,
player,
session,
refresh_task: Box::pin(futures::stream::empty()),
token_task: Box::pin(futures::future::pending()),
active: false,
mixer,
}
}
}
impl Worker {
fn create_refresh(&self) -> Pin<Box<dyn Stream<Item = Result<(), tokio_timer::Error>>>> {
let ev = self.events.clone();
let future =
tokio_timer::Interval::new_interval(Duration::from_millis(400)).map(move |_| {
ev.trigger();
});
Box::pin(future.compat())
}
}
impl futures::Future for Worker {
type Output = Result<(), ()>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> futures::task::Poll<Self::Output> {
loop {
let mut progress = false;
if self.session.is_invalid() {
self.events.send(Event::Player(PlayerEvent::Stopped));
return Poll::Ready(Result::Err(()));
}
if let Poll::Ready(Some(cmd)) = self.commands.as_mut().poll_next(cx) {
progress = true;
debug!("message received!");
match cmd {
WorkerCommand::Load(playable) => match SpotifyId::from_uri(&playable.uri()) {
Ok(id) => {
info!("player loading track: {:?}", id);
if id.audio_type == SpotifyAudioType::NonPlayable {
warn!("track is not playable");
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
} else {
self.player.load(id, true, 0);
}
}
Err(e) => {
error!("error parsing uri: {:?}", e);
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
}
},
WorkerCommand::Play => {
self.player.play();
}
WorkerCommand::Pause => {
self.player.pause();
}
WorkerCommand::Stop => {
self.player.stop();
}
WorkerCommand::Seek(pos) => {
self.player.seek(pos);
}
WorkerCommand::SetVolume(volume) => {
self.mixer.set_volume(volume);
}
WorkerCommand::RequestToken(sender) => {
self.token_task = Spotify::get_token(&self.session, sender);
progress = true;
}
WorkerCommand::Shutdown => {
self.player.stop();
self.session.shutdown();
}
}
}
if let Ok(v01_Async::Ready(Some(event))) = self.player_events.poll() {
debug!("librespot player event: {:?}", event);
match event {
LibrespotPlayerEvent::Started { .. }
| LibrespotPlayerEvent::Loading { .. }
| LibrespotPlayerEvent::Changed { .. } => {
progress = true;
}
LibrespotPlayerEvent::Playing { .. } => {
self.events.send(Event::Player(PlayerEvent::Playing));
self.refresh_task = self.create_refresh();
self.active = true;
}
LibrespotPlayerEvent::Paused { .. } => {
self.events.send(Event::Player(PlayerEvent::Paused));
self.active = false;
}
LibrespotPlayerEvent::Stopped { .. } => {
self.events.send(Event::Player(PlayerEvent::Stopped));
self.active = false;
}
LibrespotPlayerEvent::EndOfTrack { .. } => {
self.events.send(Event::Player(PlayerEvent::FinishedTrack));
progress = true;
}
_ => {}
}
}
if let Poll::Ready(Some(Ok(_))) = self.refresh_task.as_mut().poll_next(cx) {
self.refresh_task = if self.active {
progress = true;
self.create_refresh()
} else {
Box::pin(futures::stream::empty())
};
}
match self.token_task.as_mut().poll(cx) {
Poll::Ready(Ok(_)) => {
info!("token updated!");
self.token_task = Box::pin(futures::future::pending())
}
Poll::Ready(Err(e)) => {
error!("could not generate token: {:?}", e);
}
_ => (),
}
if !progress {
return Poll::Pending;
}
}
}
}

View File

@@ -26,7 +26,7 @@ use crate::ui::album::AlbumView;
use crate::ui::artist::ArtistView; use crate::ui::artist::ArtistView;
use crate::ui::contextmenu::ContextMenu; use crate::ui::contextmenu::ContextMenu;
use crate::ui::pagination::Pagination; use crate::ui::pagination::Pagination;
use crate::{album::Album, spotify::URIType, spotify_url::SpotifyURL}; use crate::{album::Album, spotify::UriType, spotify_url::SpotifyUrl};
pub struct ListView<I: ListItem> { pub struct ListView<I: ListItem> {
content: Arc<RwLock<Vec<I>>>, content: Arc<RwLock<Vec<I>>>,
@@ -535,26 +535,26 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
let spotify = self.queue.get_spotify(); let spotify = self.queue.get_spotify();
let url = SpotifyURL::from_url(&url); let url = SpotifyUrl::from_url(&url);
if let Some(url) = url { if let Some(url) = url {
let target: Option<Box<dyn ListItem>> = match url.uri_type { let target: Option<Box<dyn ListItem>> = match url.uri_type {
URIType::Track => spotify UriType::Track => spotify
.track(&url.id) .track(&url.id)
.map(|track| Track::from(&track).as_listitem()), .map(|track| Track::from(&track).as_listitem()),
URIType::Album => spotify UriType::Album => spotify
.album(&url.id) .album(&url.id)
.map(|album| Album::from(&album).as_listitem()), .map(|album| Album::from(&album).as_listitem()),
URIType::Playlist => spotify UriType::Playlist => spotify
.playlist(&url.id) .playlist(&url.id)
.map(|playlist| Playlist::from(&playlist).as_listitem()), .map(|playlist| Playlist::from(&playlist).as_listitem()),
URIType::Artist => spotify UriType::Artist => spotify
.artist(&url.id) .artist(&url.id)
.map(|artist| Artist::from(&artist).as_listitem()), .map(|artist| Artist::from(&artist).as_listitem()),
URIType::Episode => spotify UriType::Episode => spotify
.episode(&url.id) .episode(&url.id)
.map(|episode| Episode::from(&episode).as_listitem()), .map(|episode| Episode::from(&episode).as_listitem()),
URIType::Show => spotify UriType::Show => spotify
.get_show(&url.id) .get_show(&url.id)
.map(|show| Show::from(&show).as_listitem()), .map(|show| Show::from(&show).as_listitem()),
}; };

View File

@@ -19,7 +19,7 @@ use crate::library::Library;
use crate::playlist::Playlist; use crate::playlist::Playlist;
use crate::queue::Queue; use crate::queue::Queue;
use crate::show::Show; use crate::show::Show;
use crate::spotify::{Spotify, URIType}; use crate::spotify::{Spotify, UriType};
use crate::track::Track; use crate::track::Track;
use crate::traits::{ListItem, ViewExt}; use crate::traits::{ListItem, ViewExt};
use crate::ui::layout::Layout; use crate::ui::layout::Layout;

View File

@@ -8,8 +8,8 @@ use crate::library::Library;
use crate::playlist::Playlist; use crate::playlist::Playlist;
use crate::queue::Queue; use crate::queue::Queue;
use crate::show::Show; use crate::show::Show;
use crate::spotify::{Spotify, URIType}; use crate::spotify::{Spotify, UriType};
use crate::spotify_url::SpotifyURL; use crate::spotify_url::SpotifyUrl;
use crate::track::Track; use crate::track::Track;
use crate::traits::{ListItem, ViewExt}; use crate::traits::{ListItem, ViewExt};
use crate::ui::listview::ListView; use crate::ui::listview::ListView;
@@ -381,9 +381,9 @@ impl SearchResultsView {
self.spotify.refresh_token(); self.spotify.refresh_token();
// is the query a Spotify URI? // is the query a Spotify URI?
if let Some(uritype) = URIType::from_uri(&query) { if let Some(uritype) = UriType::from_uri(&query) {
match uritype { match uritype {
URIType::Track => { UriType::Track => {
self.perform_search( self.perform_search(
Box::new(Self::get_track), Box::new(Self::get_track),
&self.results_tracks, &self.results_tracks,
@@ -392,7 +392,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(0); self.tabs.move_focus_to(0);
} }
URIType::Album => { UriType::Album => {
self.perform_search( self.perform_search(
Box::new(Self::get_album), Box::new(Self::get_album),
&self.results_albums, &self.results_albums,
@@ -401,7 +401,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(1); self.tabs.move_focus_to(1);
} }
URIType::Artist => { UriType::Artist => {
self.perform_search( self.perform_search(
Box::new(Self::get_artist), Box::new(Self::get_artist),
&self.results_artists, &self.results_artists,
@@ -410,7 +410,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(2); self.tabs.move_focus_to(2);
} }
URIType::Playlist => { UriType::Playlist => {
self.perform_search( self.perform_search(
Box::new(Self::get_playlist), Box::new(Self::get_playlist),
&self.results_playlists, &self.results_playlists,
@@ -419,7 +419,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(3); self.tabs.move_focus_to(3);
} }
URIType::Show => { UriType::Show => {
self.perform_search( self.perform_search(
Box::new(Self::get_show), Box::new(Self::get_show),
&self.results_shows, &self.results_shows,
@@ -428,7 +428,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(4); self.tabs.move_focus_to(4);
} }
URIType::Episode => { UriType::Episode => {
self.perform_search( self.perform_search(
Box::new(Self::get_episode), Box::new(Self::get_episode),
&self.results_episodes, &self.results_episodes,
@@ -440,9 +440,9 @@ impl SearchResultsView {
} }
// Is the query a spotify URL? // Is the query a spotify URL?
// https://open.spotify.com/track/4uLU6hMCjMI75M1A2tKUQC // https://open.spotify.com/track/4uLU6hMCjMI75M1A2tKUQC
} else if let Some(url) = SpotifyURL::from_url(&query) { } else if let Some(url) = SpotifyUrl::from_url(&query) {
match url.uri_type { match url.uri_type {
URIType::Track => { UriType::Track => {
self.perform_search( self.perform_search(
Box::new(Self::get_track), Box::new(Self::get_track),
&self.results_tracks, &self.results_tracks,
@@ -451,7 +451,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(0); self.tabs.move_focus_to(0);
} }
URIType::Album => { UriType::Album => {
self.perform_search( self.perform_search(
Box::new(Self::get_album), Box::new(Self::get_album),
&self.results_albums, &self.results_albums,
@@ -460,7 +460,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(1); self.tabs.move_focus_to(1);
} }
URIType::Artist => { UriType::Artist => {
self.perform_search( self.perform_search(
Box::new(Self::get_artist), Box::new(Self::get_artist),
&self.results_artists, &self.results_artists,
@@ -469,7 +469,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(2); self.tabs.move_focus_to(2);
} }
URIType::Playlist => { UriType::Playlist => {
self.perform_search( self.perform_search(
Box::new(Self::get_playlist), Box::new(Self::get_playlist),
&self.results_playlists, &self.results_playlists,
@@ -478,7 +478,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(3); self.tabs.move_focus_to(3);
} }
URIType::Show => { UriType::Show => {
self.perform_search( self.perform_search(
Box::new(Self::get_show), Box::new(Self::get_show),
&self.results_shows, &self.results_shows,
@@ -487,7 +487,7 @@ impl SearchResultsView {
); );
self.tabs.move_focus_to(4); self.tabs.move_focus_to(4);
} }
URIType::Episode => { UriType::Episode => {
self.perform_search( self.perform_search(
Box::new(Self::get_episode), Box::new(Self::get_episode),
&self.results_episodes, &self.results_episodes,