From dde613e0a081295b942c67c81268621141c51f76 Mon Sep 17 00:00:00 2001 From: Paul Lietar Date: Wed, 4 May 2016 09:11:03 +0100 Subject: [PATCH] player: skip unavailable tracks rather than crash Fixes #74 --- Cargo.lock | 48 +++++++++---------- src/metadata.rs | 6 +-- src/player.rs | 120 +++++++++++++++++++++++++++++++----------------- 3 files changed, 106 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 416705f2..be717145 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,14 +4,14 @@ version = "0.1.0" dependencies = [ "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "clippy 0.0.63 (registry+https://github.com/rust-lang/crates.io-index)", + "clippy 0.0.64 (registry+https://github.com/rust-lang/crates.io-index)", "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "eventual 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "json_macros 0.3.0 (git+https://github.com/plietar/json_macros)", - "lazy_static 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libpulse-sys 0.0.0 (git+https://github.com/astro/libpulse-sys)", "librespot-protocol 0.1.0", "lmdb-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -37,7 +37,7 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -91,7 +91,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clippy" -version = "0.0.63" +version = "0.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quine-mc_cluskey 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -103,7 +103,7 @@ dependencies = [ [[package]] name = "cookie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "openssl 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -127,7 +127,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.67 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -142,7 +142,7 @@ dependencies = [ [[package]] name = "gcc" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -176,7 +176,7 @@ name = "hyper" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cookie 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cookie 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -230,7 +230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -284,7 +284,7 @@ dependencies = [ "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "liblmdb-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.67 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.1.69 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -397,7 +397,7 @@ name = "ogg-sys" version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -408,8 +408,8 @@ version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys-extras 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -432,7 +432,7 @@ name = "openssl-sys-extras" version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -479,7 +479,7 @@ name = "protobuf_build" version = "0.1.1" source = "git+https://github.com/plietar/rust-protobuf-build.git#77ea08e75b66433104a035309751d48170a7aed6" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -528,10 +528,10 @@ dependencies = [ [[package]] name = "regex" -version = "0.1.67" +version = "0.1.69" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "aho-corasick 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "aho-corasick 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -559,7 +559,7 @@ name = "rust-crypto" version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -607,7 +607,7 @@ name = "shannon-sys" version = "0.1.0" source = "git+https://github.com/plietar/rust-shannon#7000b3e49a53daaa890727ba2b2bd5a43cc4ffef" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -741,7 +741,7 @@ name = "tremor-sys" version = "0.1.0" source = "git+https://github.com/plietar/rust-tremor#5ced876f3cffb041fcf31238da7f3273178029fe" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -786,7 +786,7 @@ dependencies = [ "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -814,7 +814,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "uuid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -845,7 +845,7 @@ name = "vorbis-sys" version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -856,7 +856,7 @@ name = "vorbisfile-sys" version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gcc 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", "ogg-sys 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/metadata.rs b/src/metadata.rs index d820e8d2..0e5111db 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -31,7 +31,7 @@ pub trait MetadataTrait : Send + 'static { fn parse(msg: &Self::Message, session: &Session) -> Self; } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Track { pub id: SpotifyId, pub name: String, @@ -42,7 +42,7 @@ pub struct Track { pub available: bool, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Album { pub id: SpotifyId, pub name: String, @@ -51,7 +51,7 @@ pub struct Album { pub covers: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Artist { pub id: SpotifyId, pub name: String, diff --git a/src/player.rs b/src/player.rs index 53ce99ba..432a81ba 100644 --- a/src/player.rs +++ b/src/player.rs @@ -9,7 +9,7 @@ use audio_decrypt::AudioDecrypt; use audio_backend::Sink; use metadata::{FileFormat, Track, TrackRef}; use session::{Bitrate, Session}; -use util::{self, SpotifyId, Subfile}; +use util::{self, ReadSeek, SpotifyId, Subfile}; pub use spirc::PlayStatus; #[cfg(not(feature = "with-tremor"))] @@ -162,6 +162,56 @@ fn apply_volume(volume: u16, data: &[i16]) -> Cow<[i16]> { } } +fn find_available_alternative<'a>(session: &Session, track: &'a Track) -> Option> { + if track.available { + Some(Cow::Borrowed(track)) + } else { + let alternatives = track.alternatives + .iter() + .map(|alt_id| { + session.metadata::(*alt_id) + }) + .collect::>(); + + eventual::sequence(alternatives.into_iter()).iter().find(|alt| alt.available).map(Cow::Owned) + } +} + +fn load_track(session: &Session, track_id: SpotifyId) -> Option>>>> { + let track = session.metadata::(track_id).await().unwrap(); + + let track = match find_available_alternative(session, &track) { + Some(track) => track, + None => { + warn!("Track \"{}\" is not available", track.name); + return None; + } + }; + + let format = match session.config().bitrate { + Bitrate::Bitrate96 => FileFormat::OGG_VORBIS_96, + Bitrate::Bitrate160 => FileFormat::OGG_VORBIS_160, + Bitrate::Bitrate320 => FileFormat::OGG_VORBIS_320, + }; + + + + let file_id = match track.files.iter().find(|&&(_, f)| f == format) { + Some(&(file_id, _)) => file_id, + None => { + warn!("Track \"{}\" is not available in format {:?}", track.name, format); + return None; + } + }; + + let key = session.audio_key(track.id, file_id).await().unwrap(); + + let audio_file = Subfile::new(AudioDecrypt::new(key, session.audio_file(file_id)), 0xa7); + let decoder = vorbis::Decoder::new(audio_file).unwrap(); + + Some(decoder) +} + impl PlayerInternal { fn run(self, sink: Box) { let mut decoder = None; @@ -189,51 +239,39 @@ impl PlayerInternal { }); drop(decoder); - let mut track = self.session.metadata::(track_id).await().unwrap(); + decoder = match load_track(&self.session, track_id) { + Some(mut decoder) => { + vorbis_time_seek_ms(&mut decoder, position as i64).unwrap(); - if !track.available { - let alternatives = track.alternatives - .iter() - .map(|alt_id| { - self.session.metadata::(*alt_id) - }) - .collect::>(); + self.update(|state| { + state.status = if play { + sink.start().unwrap(); + PlayStatus::kPlayStatusPlay + } else { + PlayStatus::kPlayStatusPause + }; + state.position_ms = position; + state.position_measured_at = util::now_ms(); - track = eventual::sequence(alternatives.into_iter()) - .iter() - .find(|alt| alt.available) - .unwrap(); + true + }); + + info!("Load Done"); + Some(decoder) + } + + None => { + self.update(|state| { + state.status = PlayStatus::kPlayStatusStop; + state.end_of_track = true; + true + }); + + None + } } - let format = match self.session.config().bitrate { - Bitrate::Bitrate96 => FileFormat::OGG_VORBIS_96, - Bitrate::Bitrate160 => FileFormat::OGG_VORBIS_160, - Bitrate::Bitrate320 => FileFormat::OGG_VORBIS_320, - }; - let (file_id, _) = track.files.into_iter().find(|&(_, f)| f == format).unwrap(); - let key = self.session.audio_key(track.id, file_id).await().unwrap(); - decoder = Some( - vorbis::Decoder::new( - Subfile::new( - AudioDecrypt::new(key, - self.session.audio_file(file_id)), 0xa7)).unwrap()); - - vorbis_time_seek_ms(decoder.as_mut().unwrap(), position as i64).unwrap(); - - self.update(|state| { - state.status = if play { - sink.start().unwrap(); - PlayStatus::kPlayStatusPlay - } else { - PlayStatus::kPlayStatusPause - }; - state.position_ms = position; - state.position_measured_at = util::now_ms(); - - true - }); - info!("Load Done"); } Some(PlayerCommand::Seek(position)) => { vorbis_time_seek_ms(decoder.as_mut().unwrap(), position as i64).unwrap();