Add simple playback example.

This commit is contained in:
Paul Lietar 2017-04-28 23:24:55 +01:00
parent d95c0b3fcd
commit 294a7821d6
8 changed files with 81 additions and 11 deletions

10
Cargo.lock generated
View file

@ -36,6 +36,7 @@ dependencies = [
"tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-signal 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tremor 0.1.0 (git+https://github.com/plietar/rust-tremor)", "tremor 0.1.0 (git+https://github.com/plietar/rust-tremor)",
"url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"vorbis 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "vorbis 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
@ -851,6 +852,14 @@ name = "utf8-ranges"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "uuid"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "vergen" name = "vergen"
version = "0.1.1" version = "0.1.1"
@ -1033,6 +1042,7 @@ dependencies = [
"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
"checksum url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ba8a749fb4479b043733416c244fa9d1d3af3d7c23804944651c8a448cb87e" "checksum url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ba8a749fb4479b043733416c244fa9d1d3af3d7c23804944651c8a448cb87e"
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
"checksum uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7cfec50b0842181ba6e713151b72f4ec84a6a7e2c9c8a8a3ffc37bb1cd16b231"
"checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d" "checksum vergen 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c3365f36c57e5df714a34be40902b27a992eeddb9996eca52d0584611cf885d"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum vorbis 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "760993e54524128b88d4d7aff09c773c2f16a9f18db3c8ae1ccca5afd1287656" "checksum vorbis 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "760993e54524128b88d4d7aff09c773c2f16a9f18db3c8ae1ccca5afd1287656"

View file

@ -59,6 +59,7 @@ futures = "0.1.8"
tokio-core = "0.1.2" tokio-core = "0.1.2"
tokio-proto = "0.1.0" tokio-proto = "0.1.0"
tokio-signal = "0.1" tokio-signal = "0.1"
uuid = { version = "0.4", features = ["v4"] }
[build-dependencies] [build-dependencies]
vergen = "0.1.0" vergen = "0.1.0"

40
examples/play.rs Normal file
View file

@ -0,0 +1,40 @@
extern crate librespot;
extern crate tokio_core;
use std::env;
use tokio_core::reactor::Core;
use librespot::audio_backend;
use librespot::authentication::Credentials;
use librespot::player::Player;
use librespot::session::{Config, Session};
use librespot::util::SpotifyId;
fn main() {
let mut core = Core::new().unwrap();
let handle = core.handle();
let config = Config::default();
let args : Vec<_> = env::args().collect();
if args.len() != 4 {
println!("Usage: {} USERNAME PASSWORD TRACK", args[0]);
}
let username = args[1].to_owned();
let password = args[2].to_owned();
let credentials = Credentials::with_password(username, password);
let track = SpotifyId::from_base62(&args[3]);
let backend = audio_backend::find(None).unwrap();
println!("Connecting ..");
let session = core.run(Session::connect(config, credentials, None, handle)).unwrap();
let player = Player::new(session.clone(), None, move || (backend)(None));
println!("Playing...");
core.run(player.load(track, true, 0)).unwrap();
println!("Done");
}

View file

@ -85,8 +85,8 @@ declare_backends! {
]; ];
} }
pub fn find<T: AsRef<str>>(name: Option<T>) -> Option<fn(Option<String>) -> Box<Sink>> { pub fn find(name: Option<String>) -> Option<fn(Option<String>) -> Box<Sink>> {
if let Some(name) = name.as_ref().map(AsRef::as_ref) { if let Some(name) = name {
BACKENDS.iter().find(|backend| name == backend.0).map(|backend| backend.1) BACKENDS.iter().find(|backend| name == backend.0).map(|backend| backend.1)
} else { } else {
Some(BACKENDS.first().expect("No backends were enabled at build time").1) Some(BACKENDS.first().expect("No backends were enabled at build time").1)

View file

@ -29,6 +29,7 @@ extern crate tempfile;
extern crate tokio_core; extern crate tokio_core;
extern crate tokio_proto; extern crate tokio_proto;
extern crate url; extern crate url;
extern crate uuid;
pub extern crate librespot_protocol as protocol; pub extern crate librespot_protocol as protocol;

View file

@ -73,6 +73,7 @@ struct Setup {
mixer: fn() -> Box<Mixer>, mixer: fn() -> Box<Mixer>,
name: String,
cache: Option<Cache>, cache: Option<Cache>,
config: Config, config: Config,
credentials: Option<Credentials>, credentials: Option<Credentials>,
@ -116,7 +117,7 @@ fn setup(args: &[String]) -> Setup {
exit(0); exit(0);
} }
let backend = audio_backend::find(backend_name.as_ref()) let backend = audio_backend::find(backend_name)
.expect("Invalid backend"); .expect("Invalid backend");
let mixer_name = matches.opt_str("mixer"); let mixer_name = matches.opt_str("mixer");
@ -144,7 +145,6 @@ fn setup(args: &[String]) -> Setup {
let config = Config { let config = Config {
user_agent: version::version_string(), user_agent: version::version_string(),
name: name,
device_id: device_id, device_id: device_id,
bitrate: bitrate, bitrate: bitrate,
onstart: matches.opt_str("onstart"), onstart: matches.opt_str("onstart"),
@ -154,6 +154,7 @@ fn setup(args: &[String]) -> Setup {
let device = matches.opt_str("device"); let device = matches.opt_str("device");
Setup { Setup {
name: name,
backend: backend, backend: backend,
cache: cache, cache: cache,
config: config, config: config,
@ -165,6 +166,7 @@ fn setup(args: &[String]) -> Setup {
} }
struct Main { struct Main {
name: String,
cache: Option<Cache>, cache: Option<Cache>,
config: Config, config: Config,
backend: fn(Option<String>) -> Box<Sink>, backend: fn(Option<String>) -> Box<Sink>,
@ -184,6 +186,7 @@ struct Main {
impl Main { impl Main {
fn new(handle: Handle, fn new(handle: Handle,
name: String,
config: Config, config: Config,
cache: Option<Cache>, cache: Option<Cache>,
backend: fn(Option<String>) -> Box<Sink>, backend: fn(Option<String>) -> Box<Sink>,
@ -192,6 +195,7 @@ impl Main {
{ {
Main { Main {
handle: handle.clone(), handle: handle.clone(),
name: name,
cache: cache, cache: cache,
config: config, config: config,
backend: backend, backend: backend,
@ -208,8 +212,9 @@ impl Main {
} }
fn discovery(&mut self) { fn discovery(&mut self) {
let name = self.config.name.clone();
let device_id = self.config.device_id.clone(); let device_id = self.config.device_id.clone();
let name = self.name.clone();
self.discovery = Some(discovery(&self.handle, name, device_id).unwrap()); self.discovery = Some(discovery(&self.handle, name, device_id).unwrap());
} }
@ -256,7 +261,7 @@ impl Future for Main {
(backend)(device) (backend)(device)
}); });
let (spirc, spirc_task) = Spirc::new(session, player, mixer); let (spirc, spirc_task) = Spirc::new(self.name.clone(), session, player, mixer);
self.spirc = Some(spirc); self.spirc = Some(spirc);
self.spirc_task = Some(spirc_task); self.spirc_task = Some(spirc_task);
@ -298,9 +303,9 @@ fn main() {
let handle = core.handle(); let handle = core.handle();
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
let Setup { backend, config, device, cache, enable_discovery, credentials, mixer } = setup(&args); let Setup { name, backend, config, device, cache, enable_discovery, credentials, mixer } = setup(&args);
let mut task = Main::new(handle, config.clone(), cache, backend, device, mixer); let mut task = Main::new(handle, name, config, cache, backend, device, mixer);
if enable_discovery { if enable_discovery {
task.discovery(); task.discovery();
} }

View file

@ -9,12 +9,14 @@ use std::sync::{RwLock, Arc, Weak};
use tokio_core::io::EasyBuf; use tokio_core::io::EasyBuf;
use tokio_core::reactor::{Handle, Remote}; use tokio_core::reactor::{Handle, Remote};
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use uuid::Uuid;
use apresolve::apresolve_or_fallback; use apresolve::apresolve_or_fallback;
use authentication::Credentials; use authentication::Credentials;
use cache::Cache; use cache::Cache;
use component::Lazy; use component::Lazy;
use connection; use connection;
use version;
use audio_key::AudioKeyManager; use audio_key::AudioKeyManager;
use channel::ChannelManager; use channel::ChannelManager;
@ -43,13 +45,25 @@ impl FromStr for Bitrate {
#[derive(Clone)] #[derive(Clone)]
pub struct Config { pub struct Config {
pub user_agent: String, pub user_agent: String,
pub name: String,
pub device_id: String, pub device_id: String,
pub bitrate: Bitrate, pub bitrate: Bitrate,
pub onstart: Option<String>, pub onstart: Option<String>,
pub onstop: Option<String>, pub onstop: Option<String>,
} }
impl Default for Config {
fn default() -> Config {
let device_id = Uuid::new_v4().hyphenated().to_string();
Config {
user_agent: version::version_string(),
device_id: device_id,
bitrate: Bitrate::Bitrate160,
onstart: None,
onstop: None,
}
}
}
pub struct SessionData { pub struct SessionData {
country: String, country: String,
canonical_username: String, canonical_username: String,

View file

@ -118,13 +118,12 @@ fn initial_device_state(name: String, volume: u16) -> DeviceState {
} }
impl Spirc { impl Spirc {
pub fn new(session: Session, player: Player, mixer: Box<Mixer>) pub fn new(name: String, session: Session, player: Player, mixer: Box<Mixer>)
-> (Spirc, SpircTask) -> (Spirc, SpircTask)
{ {
debug!("new Spirc[{}]", session.session_id()); debug!("new Spirc[{}]", session.session_id());
let ident = session.device_id().to_owned(); let ident = session.device_id().to_owned();
let name = session.config().name.clone();
let uri = format!("hm://remote/user/{}", session.username()); let uri = format!("hm://remote/user/{}", session.username());