Refactor: extract Spotify Worker to separate file
This commit is contained in:
@@ -64,6 +64,7 @@ mod sharing;
|
||||
mod show;
|
||||
mod spotify;
|
||||
mod spotify_url;
|
||||
mod spotify_worker;
|
||||
mod theme;
|
||||
mod track;
|
||||
mod traits;
|
||||
|
||||
16
src/mpris.rs
16
src/mpris.rs
@@ -19,7 +19,7 @@ use crate::playable::Playable;
|
||||
use crate::playlist::Playlist;
|
||||
use crate::queue::{Queue, RepeatSetting};
|
||||
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::traits::ListItem;
|
||||
use regex::Regex;
|
||||
@@ -538,9 +538,9 @@ fn run_dbus_server(
|
||||
None => "".to_string(),
|
||||
};
|
||||
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 {
|
||||
Some(URIType::Album) => {
|
||||
Some(UriType::Album) => {
|
||||
if let Some(a) = spotify.album(&id) {
|
||||
if let Some(t) = &Album::from(&a).tracks {
|
||||
queue.clear();
|
||||
@@ -553,14 +553,14 @@ fn run_dbus_server(
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(URIType::Track) => {
|
||||
Some(UriType::Track) => {
|
||||
if let Some(t) = spotify.track(&id) {
|
||||
queue.clear();
|
||||
queue.append(Playable::Track(Track::from(&t)));
|
||||
queue.play(0, false, false)
|
||||
}
|
||||
}
|
||||
Some(URIType::Playlist) => {
|
||||
Some(UriType::Playlist) => {
|
||||
if let Some(p) = spotify.playlist(&id) {
|
||||
let mut playlist = Playlist::from(&p);
|
||||
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) {
|
||||
let mut show = Show::from(&s);
|
||||
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) {
|
||||
queue.clear();
|
||||
queue.append(Playable::Episode(Episode::from(&e)));
|
||||
queue.play(0, false, false)
|
||||
}
|
||||
}
|
||||
Some(URIType::Artist) => {
|
||||
Some(UriType::Artist) => {
|
||||
if let Some(a) = spotify.artist_top_tracks(&id) {
|
||||
queue.clear();
|
||||
queue.append_next(a.iter().map(|track| Playable::Track(track.clone())).collect());
|
||||
|
||||
203
src/spotify.rs
203
src/spotify.rs
@@ -4,13 +4,11 @@ use librespot_core::config::SessionConfig;
|
||||
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::config::PlayerConfig;
|
||||
|
||||
use librespot_playback::audio_backend;
|
||||
use librespot_playback::config::Bitrate;
|
||||
use librespot_playback::mixer::Mixer;
|
||||
use librespot_playback::player::{Player, PlayerEvent as LibrespotPlayerEvent};
|
||||
use librespot_playback::player::Player;
|
||||
|
||||
use rspotify::blocking::client::Spotify as SpotifyAPI;
|
||||
use rspotify::model::album::{FullAlbum, SavedAlbum, SimplifiedAlbum};
|
||||
@@ -28,23 +26,15 @@ use serde_json::{json, Map};
|
||||
use failure::Error;
|
||||
|
||||
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::oneshot;
|
||||
use futures::compat::Future01CompatExt;
|
||||
use futures::compat::Stream01CompatExt;
|
||||
use futures::task::Context;
|
||||
use futures::Future;
|
||||
use futures::Stream;
|
||||
|
||||
use tokio_core::reactor::Core;
|
||||
use url::Url;
|
||||
|
||||
use core::task::Poll;
|
||||
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
@@ -56,6 +46,7 @@ use crate::artist::Artist;
|
||||
use crate::config;
|
||||
use crate::events::{Event, EventManager};
|
||||
use crate::playable::Playable;
|
||||
use crate::spotify_worker::{Worker, WorkerCommand};
|
||||
use crate::track::Track;
|
||||
|
||||
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;
|
||||
|
||||
enum WorkerCommand {
|
||||
Load(Playable),
|
||||
Play,
|
||||
Pause,
|
||||
Stop,
|
||||
Seek(u32),
|
||||
SetVolume(u16),
|
||||
RequestToken(oneshot::Sender<Token>),
|
||||
Shutdown,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum PlayerEvent {
|
||||
Playing,
|
||||
@@ -96,165 +76,6 @@ pub struct Spotify {
|
||||
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 {
|
||||
pub fn new(
|
||||
events: EventManager,
|
||||
@@ -365,7 +186,7 @@ impl Spotify {
|
||||
.expect("could not open spotify session")
|
||||
}
|
||||
|
||||
fn get_token(
|
||||
pub(crate) fn get_token(
|
||||
session: &Session,
|
||||
sender: oneshot::Sender<Token>,
|
||||
) -> Pin<Box<dyn Future<Output = Result<(), MercuryError>>>> {
|
||||
@@ -955,7 +776,7 @@ impl Spotify {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum URIType {
|
||||
pub enum UriType {
|
||||
Album,
|
||||
Artist,
|
||||
Track,
|
||||
@@ -964,20 +785,20 @@ pub enum URIType {
|
||||
Episode,
|
||||
}
|
||||
|
||||
impl URIType {
|
||||
pub fn from_uri(s: &str) -> Option<URIType> {
|
||||
impl UriType {
|
||||
pub fn from_uri(s: &str) -> Option<UriType> {
|
||||
if s.starts_with("spotify:album:") {
|
||||
Some(URIType::Album)
|
||||
Some(UriType::Album)
|
||||
} else if s.starts_with("spotify:artist:") {
|
||||
Some(URIType::Artist)
|
||||
Some(UriType::Artist)
|
||||
} else if s.starts_with("spotify:track:") {
|
||||
Some(URIType::Track)
|
||||
Some(UriType::Track)
|
||||
} else if s.starts_with("spotify:") && s.contains(":playlist:") {
|
||||
Some(URIType::Playlist)
|
||||
Some(UriType::Playlist)
|
||||
} else if s.starts_with("spotify:show:") {
|
||||
Some(URIType::Show)
|
||||
Some(UriType::Show)
|
||||
} else if s.starts_with("spotify:episode:") {
|
||||
Some(URIType::Episode)
|
||||
Some(UriType::Episode)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use crate::spotify::URIType;
|
||||
use crate::spotify::UriType;
|
||||
|
||||
use url::{Host, Url};
|
||||
|
||||
pub struct SpotifyURL {
|
||||
pub struct SpotifyUrl {
|
||||
pub id: String,
|
||||
pub uri_type: URIType,
|
||||
pub uri_type: UriType,
|
||||
}
|
||||
|
||||
impl SpotifyURL {
|
||||
fn new(id: &str, uri_type: URIType) -> SpotifyURL {
|
||||
SpotifyURL {
|
||||
impl SpotifyUrl {
|
||||
fn new(id: &str, uri_type: UriType) -> SpotifyUrl {
|
||||
SpotifyUrl {
|
||||
id: id.to_string(),
|
||||
uri_type,
|
||||
}
|
||||
@@ -22,7 +22,7 @@ impl SpotifyURL {
|
||||
/// assert_eq!(result.id, "4uLU6hMCjMI75M1A2tKUQC");
|
||||
/// 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()?;
|
||||
if url.host() != Some(Host::Domain("open.spotify.com")) {
|
||||
return None;
|
||||
@@ -33,12 +33,12 @@ impl SpotifyURL {
|
||||
let entity = path_segments.next()?;
|
||||
|
||||
let uri_type = match entity.to_lowercase().as_str() {
|
||||
"album" => Some(URIType::Album),
|
||||
"artist" => Some(URIType::Artist),
|
||||
"episode" => Some(URIType::Episode),
|
||||
"playlist" => Some(URIType::Playlist),
|
||||
"show" => Some(URIType::Show),
|
||||
"track" => Some(URIType::Track),
|
||||
"album" => Some(UriType::Album),
|
||||
"artist" => Some(UriType::Artist),
|
||||
"episode" => Some(UriType::Episode),
|
||||
"playlist" => Some(UriType::Playlist),
|
||||
"show" => Some(UriType::Show),
|
||||
"track" => Some(UriType::Track),
|
||||
"user" => {
|
||||
let _user_id = path_segments.next()?;
|
||||
let entity = path_segments.next()?;
|
||||
@@ -47,14 +47,14 @@ impl SpotifyURL {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(URIType::Playlist)
|
||||
Some(UriType::Playlist)
|
||||
}
|
||||
_ => None,
|
||||
}?;
|
||||
|
||||
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 {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::SpotifyURL;
|
||||
use crate::spotify::URIType;
|
||||
use super::SpotifyUrl;
|
||||
use crate::spotify::UriType;
|
||||
|
||||
#[test]
|
||||
fn test_urls() {
|
||||
let mut test_cases = HashMap::new();
|
||||
test_cases.insert(
|
||||
"https://open.spotify.com/playlist/1XFxe8bkTryTODn0lk4CNa?si=FfSpZ6KPQdieClZbwHakOQ",
|
||||
SpotifyURL::new("1XFxe8bkTryTODn0lk4CNa", URIType::Playlist),
|
||||
SpotifyUrl::new("1XFxe8bkTryTODn0lk4CNa", UriType::Playlist),
|
||||
);
|
||||
test_cases.insert(
|
||||
"https://open.spotify.com/track/6fRJg3R90w0juYoCJXxj2d",
|
||||
SpotifyURL::new("6fRJg3R90w0juYoCJXxj2d", URIType::Track),
|
||||
SpotifyUrl::new("6fRJg3R90w0juYoCJXxj2d", UriType::Track),
|
||||
);
|
||||
test_cases.insert(
|
||||
"https://open.spotify.com/user/~villainy~/playlist/0OgoSs65CLDPn6AF6tsZVg",
|
||||
SpotifyURL::new("0OgoSs65CLDPn6AF6tsZVg", URIType::Playlist),
|
||||
SpotifyUrl::new("0OgoSs65CLDPn6AF6tsZVg", UriType::Playlist),
|
||||
);
|
||||
test_cases.insert(
|
||||
"https://open.spotify.com/show/4MZfJbM2MXzZdPbv6gi5lJ",
|
||||
SpotifyURL::new("4MZfJbM2MXzZdPbv6gi5lJ", URIType::Show),
|
||||
SpotifyUrl::new("4MZfJbM2MXzZdPbv6gi5lJ", UriType::Show),
|
||||
);
|
||||
test_cases.insert(
|
||||
"https://open.spotify.com/episode/3QE6rfmjRaeqXSqeWcIWF6",
|
||||
SpotifyURL::new("3QE6rfmjRaeqXSqeWcIWF6", URIType::Episode),
|
||||
SpotifyUrl::new("3QE6rfmjRaeqXSqeWcIWF6", UriType::Episode),
|
||||
);
|
||||
test_cases.insert(
|
||||
"https://open.spotify.com/artist/6LEeAFiJF8OuPx747e1wxR",
|
||||
SpotifyURL::new("6LEeAFiJF8OuPx747e1wxR", URIType::Artist),
|
||||
SpotifyUrl::new("6LEeAFiJF8OuPx747e1wxR", UriType::Artist),
|
||||
);
|
||||
|
||||
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.uri_type, case.1.uri_type);
|
||||
}
|
||||
|
||||
188
src/spotify_worker.rs
Normal file
188
src/spotify_worker.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ use crate::ui::album::AlbumView;
|
||||
use crate::ui::artist::ArtistView;
|
||||
use crate::ui::contextmenu::ContextMenu;
|
||||
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> {
|
||||
content: Arc<RwLock<Vec<I>>>,
|
||||
@@ -535,26 +535,26 @@ impl<I: ListItem + Clone> ViewExt for ListView<I> {
|
||||
|
||||
let spotify = self.queue.get_spotify();
|
||||
|
||||
let url = SpotifyURL::from_url(&url);
|
||||
let url = SpotifyUrl::from_url(&url);
|
||||
|
||||
if let Some(url) = url {
|
||||
let target: Option<Box<dyn ListItem>> = match url.uri_type {
|
||||
URIType::Track => spotify
|
||||
UriType::Track => spotify
|
||||
.track(&url.id)
|
||||
.map(|track| Track::from(&track).as_listitem()),
|
||||
URIType::Album => spotify
|
||||
UriType::Album => spotify
|
||||
.album(&url.id)
|
||||
.map(|album| Album::from(&album).as_listitem()),
|
||||
URIType::Playlist => spotify
|
||||
UriType::Playlist => spotify
|
||||
.playlist(&url.id)
|
||||
.map(|playlist| Playlist::from(&playlist).as_listitem()),
|
||||
URIType::Artist => spotify
|
||||
UriType::Artist => spotify
|
||||
.artist(&url.id)
|
||||
.map(|artist| Artist::from(&artist).as_listitem()),
|
||||
URIType::Episode => spotify
|
||||
UriType::Episode => spotify
|
||||
.episode(&url.id)
|
||||
.map(|episode| Episode::from(&episode).as_listitem()),
|
||||
URIType::Show => spotify
|
||||
UriType::Show => spotify
|
||||
.get_show(&url.id)
|
||||
.map(|show| Show::from(&show).as_listitem()),
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::library::Library;
|
||||
use crate::playlist::Playlist;
|
||||
use crate::queue::Queue;
|
||||
use crate::show::Show;
|
||||
use crate::spotify::{Spotify, URIType};
|
||||
use crate::spotify::{Spotify, UriType};
|
||||
use crate::track::Track;
|
||||
use crate::traits::{ListItem, ViewExt};
|
||||
use crate::ui::layout::Layout;
|
||||
|
||||
@@ -8,8 +8,8 @@ use crate::library::Library;
|
||||
use crate::playlist::Playlist;
|
||||
use crate::queue::Queue;
|
||||
use crate::show::Show;
|
||||
use crate::spotify::{Spotify, URIType};
|
||||
use crate::spotify_url::SpotifyURL;
|
||||
use crate::spotify::{Spotify, UriType};
|
||||
use crate::spotify_url::SpotifyUrl;
|
||||
use crate::track::Track;
|
||||
use crate::traits::{ListItem, ViewExt};
|
||||
use crate::ui::listview::ListView;
|
||||
@@ -381,9 +381,9 @@ impl SearchResultsView {
|
||||
self.spotify.refresh_token();
|
||||
|
||||
// is the query a Spotify URI?
|
||||
if let Some(uritype) = URIType::from_uri(&query) {
|
||||
if let Some(uritype) = UriType::from_uri(&query) {
|
||||
match uritype {
|
||||
URIType::Track => {
|
||||
UriType::Track => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_track),
|
||||
&self.results_tracks,
|
||||
@@ -392,7 +392,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(0);
|
||||
}
|
||||
URIType::Album => {
|
||||
UriType::Album => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_album),
|
||||
&self.results_albums,
|
||||
@@ -401,7 +401,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(1);
|
||||
}
|
||||
URIType::Artist => {
|
||||
UriType::Artist => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_artist),
|
||||
&self.results_artists,
|
||||
@@ -410,7 +410,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(2);
|
||||
}
|
||||
URIType::Playlist => {
|
||||
UriType::Playlist => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_playlist),
|
||||
&self.results_playlists,
|
||||
@@ -419,7 +419,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(3);
|
||||
}
|
||||
URIType::Show => {
|
||||
UriType::Show => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_show),
|
||||
&self.results_shows,
|
||||
@@ -428,7 +428,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(4);
|
||||
}
|
||||
URIType::Episode => {
|
||||
UriType::Episode => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_episode),
|
||||
&self.results_episodes,
|
||||
@@ -440,9 +440,9 @@ impl SearchResultsView {
|
||||
}
|
||||
// Is the query a spotify URL?
|
||||
// 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 {
|
||||
URIType::Track => {
|
||||
UriType::Track => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_track),
|
||||
&self.results_tracks,
|
||||
@@ -451,7 +451,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(0);
|
||||
}
|
||||
URIType::Album => {
|
||||
UriType::Album => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_album),
|
||||
&self.results_albums,
|
||||
@@ -460,7 +460,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(1);
|
||||
}
|
||||
URIType::Artist => {
|
||||
UriType::Artist => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_artist),
|
||||
&self.results_artists,
|
||||
@@ -469,7 +469,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(2);
|
||||
}
|
||||
URIType::Playlist => {
|
||||
UriType::Playlist => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_playlist),
|
||||
&self.results_playlists,
|
||||
@@ -478,7 +478,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(3);
|
||||
}
|
||||
URIType::Show => {
|
||||
UriType::Show => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_show),
|
||||
&self.results_shows,
|
||||
@@ -487,7 +487,7 @@ impl SearchResultsView {
|
||||
);
|
||||
self.tabs.move_focus_to(4);
|
||||
}
|
||||
URIType::Episode => {
|
||||
UriType::Episode => {
|
||||
self.perform_search(
|
||||
Box::new(Self::get_episode),
|
||||
&self.results_episodes,
|
||||
|
||||
Reference in New Issue
Block a user