Преглед изворни кода

player: skip unavailable tracks rather than crash

Fixes #74
Paul Lietar пре 9 година
родитељ
комит
dde613e0a0
3 измењених фајлова са 106 додато и 68 уклоњено
  1. 24 24
      Cargo.lock
  2. 3 3
      src/metadata.rs
  3. 79 41
      src/player.rs

+ 24 - 24
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)",

+ 3 - 3
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<FileId>,
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Artist {
     pub id: SpotifyId,
     pub name: String,

+ 79 - 41
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<Cow<'a, Track>> {
+    if track.available {
+        Some(Cow::Borrowed(track))
+    } else {
+        let alternatives = track.alternatives
+            .iter()
+            .map(|alt_id| {
+                session.metadata::<Track>(*alt_id)
+            })
+        .collect::<Vec<TrackRef>>();
+
+        eventual::sequence(alternatives.into_iter()).iter().find(|alt| alt.available).map(Cow::Owned)
+    }
+}
+
+fn load_track(session: &Session, track_id: SpotifyId) -> Option<vorbis::Decoder<Subfile<AudioDecrypt<Box<ReadSeek>>>>> {
+    let track = session.metadata::<Track>(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<Sink>) {
         let mut decoder = None;
@@ -189,51 +239,39 @@ impl PlayerInternal {
                     });
                     drop(decoder);
 
-                    let mut track = self.session.metadata::<Track>(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();
+
+                            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(decoder)
+                        }
 
-                    if !track.available {
-                        let alternatives = track.alternatives
-                                                .iter()
-                                                .map(|alt_id| {
-                                                    self.session.metadata::<Track>(*alt_id)
-                                                })
-                                                .collect::<Vec<TrackRef>>();
+                        None => {
+                            self.update(|state| {
+                                state.status = PlayStatus::kPlayStatusStop;
+                                state.end_of_track = true;
+                                true
+                            });
 
-                        track = eventual::sequence(alternatives.into_iter())
-                                    .iter()
-                                    .find(|alt| alt.available)
-                                    .unwrap();
+                            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();