player: skip unavailable tracks rather than crash

Fixes #74
This commit is contained in:
Paul Lietar 2016-05-04 09:11:03 +01:00
parent 8d8aad8191
commit dde613e0a0
3 changed files with 106 additions and 68 deletions

48
Cargo.lock generated
View file

@ -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)",

View file

@ -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,

View file

@ -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();
if !track.available {
let alternatives = track.alternatives
.iter()
.map(|alt_id| {
self.session.metadata::<Track>(*alt_id)
})
.collect::<Vec<TrackRef>>();
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();