Allow device type to be chosen.

Fix #187
This commit is contained in:
Paul Lietar 2017-08-03 19:31:15 +01:00
parent 910974e5e2
commit 72070b6ce0
8 changed files with 244 additions and 134 deletions

6
Cargo.lock generated
View file

@ -25,7 +25,7 @@ dependencies = [
"protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)", "protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)",
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
"rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (git+https://github.com/awmath/rust-crypto.git?branch=avx2)",
"serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 0.9.15 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 0.9.10 (registry+https://github.com/rust-lang/crates.io-index)",
@ -528,7 +528,7 @@ dependencies = [
[[package]] [[package]]
name = "rust-crypto" name = "rust-crypto"
version = "0.2.36" version = "0.2.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/awmath/rust-crypto.git?branch=avx2#394c247254dbe2ac5d44483232cf335d10cf0260"
dependencies = [ dependencies = [
"gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
@ -1019,7 +1019,7 @@ dependencies = [
"checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01" "checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457" "checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
"checksum rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec4bdede957362ec6fdd550f7e79c6d14cad2bc26b2d062786234c6ee0cb27bb" "checksum rpassword 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec4bdede957362ec6fdd550f7e79c6d14cad2bc26b2d062786234c6ee0cb27bb"
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rust-crypto 0.2.36 (git+https://github.com/awmath/rust-crypto.git?branch=avx2)" = "<none>"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" "checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"

View file

@ -19,19 +19,20 @@ use url;
use authentication::Credentials; use authentication::Credentials;
use util; use util;
use config::ConnectConfig;
#[derive(Clone)] #[derive(Clone)]
struct Discovery(Arc<DiscoveryInner>); struct Discovery(Arc<DiscoveryInner>);
struct DiscoveryInner { struct DiscoveryInner {
config: ConnectConfig,
device_id: String,
private_key: BigUint, private_key: BigUint,
public_key: BigUint, public_key: BigUint,
device_id: String,
device_name: String,
tx: mpsc::UnboundedSender<Credentials>, tx: mpsc::UnboundedSender<Credentials>,
} }
impl Discovery { impl Discovery {
pub fn new(device_name: String, device_id: String) pub fn new(config: ConnectConfig, device_id: String)
-> (Discovery, mpsc::UnboundedReceiver<Credentials>) -> (Discovery, mpsc::UnboundedReceiver<Credentials>)
{ {
let (tx, rx) = mpsc::unbounded(); let (tx, rx) = mpsc::unbounded();
@ -41,8 +42,8 @@ impl Discovery {
let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME); let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME);
let discovery = Discovery(Arc::new(DiscoveryInner { let discovery = Discovery(Arc::new(DiscoveryInner {
device_name: device_name.to_owned(), config: config,
device_id: device_id.to_owned(), device_id: device_id,
private_key: private_key, private_key: private_key,
public_key: public_key, public_key: public_key,
tx: tx, tx: tx,
@ -65,10 +66,10 @@ impl Discovery {
"spotifyError": 0, "spotifyError": 0,
"version": "2.1.0", "version": "2.1.0",
"deviceID": (self.0.device_id), "deviceID": (self.0.device_id),
"remoteName": (self.0.device_name), "remoteName": (self.0.config.name),
"activeUser": "", "activeUser": "",
"publicKey": (public_key), "publicKey": (public_key),
"deviceType": "UNKNOWN", "deviceType": (self.0.config.device_type.to_string().to_uppercase()),
"libraryVersion": "0.1.0", "libraryVersion": "0.1.0",
"accountReq": "PREMIUM", "accountReq": "PREMIUM",
"brandDisplayName": "librespot", "brandDisplayName": "librespot",
@ -206,10 +207,10 @@ pub struct DiscoveryStream {
task: Box<Future<Item=(), Error=io::Error>>, task: Box<Future<Item=(), Error=io::Error>>,
} }
pub fn discovery(handle: &Handle, device_name: String, device_id: String) pub fn discovery(handle: &Handle, config: ConnectConfig, device_id: String)
-> io::Result<DiscoveryStream> -> io::Result<DiscoveryStream>
{ {
let (discovery, creds_rx) = Discovery::new(device_name.clone(), device_id); let (discovery, creds_rx) = Discovery::new(config.clone(), device_id);
let listener = TcpListener::bind(&"0.0.0.0:0".parse().unwrap(), handle)?; let listener = TcpListener::bind(&"0.0.0.0:0".parse().unwrap(), handle)?;
let addr = listener.local_addr()?; let addr = listener.local_addr()?;
@ -224,7 +225,7 @@ pub fn discovery(handle: &Handle, device_name: String, device_id: String)
let responder = mdns::Responder::spawn(&handle)?; let responder = mdns::Responder::spawn(&handle)?;
let svc = responder.register( let svc = responder.register(
"_spotify-connect._tcp".to_owned(), "_spotify-connect._tcp".to_owned(),
device_name, config.name,
addr.port(), addr.port(),
&["VERSION=1.0", "CPath=/"]); &["VERSION=1.0", "CPath=/"]);

123
src/config.rs Normal file
View file

@ -0,0 +1,123 @@
use uuid::Uuid;
use std::str::FromStr;
use std::fmt;
use version;
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum Bitrate {
Bitrate96,
Bitrate160,
Bitrate320,
}
impl FromStr for Bitrate {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"96" => Ok(Bitrate::Bitrate96),
"160" => Ok(Bitrate::Bitrate160),
"320" => Ok(Bitrate::Bitrate320),
_ => Err(()),
}
}
}
impl Default for Bitrate {
fn default() -> Bitrate {
Bitrate::Bitrate160
}
}
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub enum DeviceType {
Unknown = 0,
Computer = 1,
Tablet = 2,
Smartphone = 3,
Speaker = 4,
TV = 5,
AVR = 6,
STB = 7,
AudioDongle = 8,
}
impl FromStr for DeviceType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
use self::DeviceType::*;
match s.to_lowercase().as_ref() {
"computer" => Ok(Computer),
"tablet" => Ok(Tablet),
"smartphone" => Ok(Smartphone),
"speaker" => Ok(Speaker),
"tv" => Ok(TV),
"avr" => Ok(AVR),
"stb" => Ok(STB),
"audiodongle" => Ok(AudioDongle),
_ => Err(()),
}
}
}
impl fmt::Display for DeviceType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::DeviceType::*;
match *self {
Unknown => f.write_str("Unknown"),
Computer => f.write_str("Computer"),
Tablet => f.write_str("Tablet"),
Smartphone => f.write_str("Smartphone"),
Speaker => f.write_str("Speaker"),
TV => f.write_str("TV"),
AVR => f.write_str("AVR"),
STB => f.write_str("STB"),
AudioDongle => f.write_str("AudioDongle"),
}
}
}
impl Default for DeviceType {
fn default() -> DeviceType {
DeviceType::Speaker
}
}
#[derive(Clone,Debug)]
pub struct SessionConfig {
pub user_agent: String,
pub device_id: String,
}
impl Default for SessionConfig {
fn default() -> SessionConfig {
let device_id = Uuid::new_v4().hyphenated().to_string();
SessionConfig {
user_agent: version::version_string(),
device_id: device_id,
}
}
}
#[derive(Clone,Debug)]
pub struct PlayerConfig {
pub bitrate: Bitrate,
pub onstart: Option<String>,
pub onstop: Option<String>,
}
impl Default for PlayerConfig {
fn default() -> PlayerConfig {
PlayerConfig {
bitrate: Bitrate::default(),
onstart: None,
onstop: None,
}
}
}
#[derive(Clone,Debug)]
pub struct ConnectConfig {
pub name: String,
pub device_type: DeviceType,
}

View file

@ -61,14 +61,15 @@ pub mod audio_key;
pub mod authentication; pub mod authentication;
pub mod cache; pub mod cache;
pub mod channel; pub mod channel;
pub mod config;
pub mod diffie_hellman; pub mod diffie_hellman;
pub mod keymaster;
pub mod mercury; pub mod mercury;
pub mod metadata; pub mod metadata;
pub mod mixer;
pub mod player; pub mod player;
pub mod session; pub mod session;
pub mod util; pub mod util;
pub mod version; pub mod version;
pub mod mixer;
pub mod keymaster;
include!(concat!(env!("OUT_DIR"), "/lib.rs")); include!(concat!(env!("OUT_DIR"), "/lib.rs"));

View file

@ -26,7 +26,8 @@ use librespot::authentication::discovery::{discovery, DiscoveryStream};
use librespot::audio_backend::{self, Sink, BACKENDS}; use librespot::audio_backend::{self, Sink, BACKENDS};
use librespot::cache::Cache; use librespot::cache::Cache;
use librespot::player::Player; use librespot::player::Player;
use librespot::session::{Bitrate, Config, Session}; use librespot::session::Session;
use librespot::config::{Bitrate, DeviceType, PlayerConfig, SessionConfig, ConnectConfig};
use librespot::mixer::{self, Mixer}; use librespot::mixer::{self, Mixer};
use librespot::version; use librespot::version;
@ -76,9 +77,10 @@ struct Setup {
mixer: fn() -> Box<Mixer>, mixer: fn() -> Box<Mixer>,
name: String,
cache: Option<Cache>, cache: Option<Cache>,
config: Config, player_config: PlayerConfig,
session_config: SessionConfig,
connect_config: ConnectConfig,
credentials: Option<Credentials>, credentials: Option<Credentials>,
enable_discovery: bool, enable_discovery: bool,
} }
@ -88,6 +90,7 @@ fn setup(args: &[String]) -> Setup {
opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE") opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE")
.optflag("", "disable-audio-cache", "Disable caching of the audio data.") .optflag("", "disable-audio-cache", "Disable caching of the audio data.")
.reqopt("n", "name", "Device name", "NAME") .reqopt("n", "name", "Device name", "NAME")
.optopt("", "device-type", "Displayed device type", "DEVICE_TYPE")
.optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE") .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE")
.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM") .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM")
.optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM") .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM")
@ -125,45 +128,69 @@ fn setup(args: &[String]) -> Setup {
let backend = audio_backend::find(backend_name) let backend = audio_backend::find(backend_name)
.expect("Invalid backend"); .expect("Invalid backend");
let device = matches.opt_str("device");
let mixer_name = matches.opt_str("mixer"); let mixer_name = matches.opt_str("mixer");
let mixer = mixer::find(mixer_name.as_ref()) let mixer = mixer::find(mixer_name.as_ref())
.expect("Invalid mixer"); .expect("Invalid mixer");
let bitrate = matches.opt_str("b").as_ref()
.map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
.unwrap_or(Bitrate::Bitrate160);
let name = matches.opt_str("name").unwrap(); let name = matches.opt_str("name").unwrap();
let device_id = librespot::session::device_id(&name);
let use_audio_cache = !matches.opt_present("disable-audio-cache"); let use_audio_cache = !matches.opt_present("disable-audio-cache");
let cache = matches.opt_str("c").map(|cache_location| { let cache = matches.opt_str("c").map(|cache_location| {
Cache::new(PathBuf::from(cache_location), use_audio_cache) Cache::new(PathBuf::from(cache_location), use_audio_cache)
}); });
let cached_credentials = cache.as_ref().and_then(Cache::credentials); let credentials = {
let cached_credentials = cache.as_ref().and_then(Cache::credentials);
let credentials = get_credentials(matches.opt_str("username"), get_credentials(
matches.opt_str("password"), matches.opt_str("username"),
cached_credentials); matches.opt_str("password"),
cached_credentials
)
};
let session_config = {
let device_id = librespot::session::device_id(&name);
SessionConfig {
user_agent: version::version_string(),
device_id: device_id,
}
};
let player_config = {
let bitrate = matches.opt_str("b").as_ref()
.map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate"))
.unwrap_or(Bitrate::default());
PlayerConfig {
bitrate: bitrate,
onstart: matches.opt_str("onstart"),
onstop: matches.opt_str("onstop"),
}
};
let connect_config = {
let device_type = matches.opt_str("device-type").as_ref()
.map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type"))
.unwrap_or(DeviceType::default());
ConnectConfig {
name: name,
device_type: device_type,
}
};
let enable_discovery = !matches.opt_present("disable-discovery"); let enable_discovery = !matches.opt_present("disable-discovery");
let config = Config {
user_agent: version::version_string(),
device_id: device_id,
bitrate: bitrate,
onstart: matches.opt_str("onstart"),
onstop: matches.opt_str("onstop"),
};
let device = matches.opt_str("device");
Setup { Setup {
name: name,
backend: backend, backend: backend,
cache: cache, cache: cache,
config: config, session_config: session_config,
player_config: player_config,
connect_config: connect_config,
credentials: credentials, credentials: credentials,
device: device, device: device,
enable_discovery: enable_discovery, enable_discovery: enable_discovery,
@ -172,9 +199,10 @@ fn setup(args: &[String]) -> Setup {
} }
struct Main { struct Main {
name: String,
cache: Option<Cache>, cache: Option<Cache>,
config: Config, player_config: PlayerConfig,
session_config: SessionConfig,
connect_config: ConnectConfig,
backend: fn(Option<String>) -> Box<Sink>, backend: fn(Option<String>) -> Box<Sink>,
device: Option<String>, device: Option<String>,
mixer: fn() -> Box<Mixer>, mixer: fn() -> Box<Mixer>,
@ -191,22 +219,16 @@ struct Main {
} }
impl Main { impl Main {
fn new(handle: Handle, fn new(handle: Handle, setup: Setup) -> Main {
name: String, let mut task = Main {
config: Config,
cache: Option<Cache>,
backend: fn(Option<String>) -> Box<Sink>,
device: Option<String>,
mixer: fn() -> Box<Mixer>) -> Main
{
Main {
handle: handle.clone(), handle: handle.clone(),
name: name, cache: setup.cache,
cache: cache, session_config: setup.session_config,
config: config, player_config: setup.player_config,
backend: backend, connect_config: setup.connect_config,
device: device, backend: setup.backend,
mixer: mixer, device: setup.device,
mixer: setup.mixer,
connect: Box::new(futures::future::empty()), connect: Box::new(futures::future::empty()),
discovery: None, discovery: None,
@ -214,18 +236,24 @@ impl Main {
spirc_task: None, spirc_task: None,
shutdown: false, shutdown: false,
signal: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(), signal: tokio_signal::ctrl_c(&handle).flatten_stream().boxed(),
};
if setup.enable_discovery {
let config = task.connect_config.clone();
let device_id = task.session_config.device_id.clone();
task.discovery = Some(discovery(&handle, config, device_id).unwrap());
} }
}
fn discovery(&mut self) { if let Some(credentials) = setup.credentials {
let device_id = self.config.device_id.clone(); task.credentials(credentials);
let name = self.name.clone(); }
self.discovery = Some(discovery(&self.handle, name, device_id).unwrap()); task
} }
fn credentials(&mut self, credentials: Credentials) { fn credentials(&mut self, credentials: Credentials) {
let config = self.config.clone(); let config = self.session_config.clone();
let handle = self.handle.clone(); let handle = self.handle.clone();
let connection = Session::connect(config, credentials, self.cache.clone(), handle); let connection = Session::connect(config, credentials, self.cache.clone(), handle);
@ -260,14 +288,16 @@ impl Future for Main {
self.connect = Box::new(futures::future::empty()); self.connect = Box::new(futures::future::empty());
let device = self.device.clone(); let device = self.device.clone();
let mixer = (self.mixer)(); let mixer = (self.mixer)();
let player_config = self.player_config.clone();
let connect_config = self.connect_config.clone();
let audio_filter = mixer.get_audio_filter(); let audio_filter = mixer.get_audio_filter();
let backend = self.backend; let backend = self.backend;
let player = Player::new(session.clone(), audio_filter, move || { let player = Player::new(player_config, session.clone(), audio_filter, move || {
(backend)(device) (backend)(device)
}); });
let (spirc, spirc_task) = Spirc::new(self.name.clone(), session, player, mixer); let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer);
self.spirc = Some(spirc); self.spirc = Some(spirc);
self.spirc_task = Some(spirc_task); self.spirc_task = Some(spirc_task);
@ -309,16 +339,7 @@ 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 { name, backend, config, device, cache, enable_discovery, credentials, mixer } = setup(&args);
let mut task = Main::new(handle, name, config, cache, backend, device, mixer); core.run(Main::new(handle, setup(&args))).unwrap()
if enable_discovery {
task.discovery();
}
if let Some(credentials) = credentials {
task.credentials(credentials);
}
core.run(task).unwrap()
} }

View file

@ -12,9 +12,10 @@ use audio_backend::Sink;
use audio_decrypt::AudioDecrypt; use audio_decrypt::AudioDecrypt;
use audio_file::AudioFile; use audio_file::AudioFile;
use metadata::{FileFormat, Track}; use metadata::{FileFormat, Track};
use session::{Bitrate, Session}; use session::Session;
use mixer::AudioFilter; use mixer::AudioFilter;
use util::{self, SpotifyId, Subfile}; use util::{self, SpotifyId, Subfile};
use config::{Bitrate, PlayerConfig};
#[derive(Clone)] #[derive(Clone)]
pub struct Player { pub struct Player {
@ -23,6 +24,7 @@ pub struct Player {
struct PlayerInternal { struct PlayerInternal {
session: Session, session: Session,
config: PlayerConfig,
commands: std::sync::mpsc::Receiver<PlayerCommand>, commands: std::sync::mpsc::Receiver<PlayerCommand>,
state: PlayerState, state: PlayerState,
@ -39,8 +41,11 @@ enum PlayerCommand {
} }
impl Player { impl Player {
pub fn new<F>(session: Session, audio_filter: Option<Box<AudioFilter + Send>>, sink_builder: F) -> Player pub fn new<F>(config: PlayerConfig, session: Session,
where F: FnOnce() -> Box<Sink> + Send + 'static { audio_filter: Option<Box<AudioFilter + Send>>,
sink_builder: F) -> Player
where F: FnOnce() -> Box<Sink> + Send + 'static
{
let (cmd_tx, cmd_rx) = std::sync::mpsc::channel(); let (cmd_tx, cmd_rx) = std::sync::mpsc::channel();
thread::spawn(move || { thread::spawn(move || {
@ -48,6 +53,7 @@ impl Player {
let internal = PlayerInternal { let internal = PlayerInternal {
session: session, session: session,
config: config,
commands: cmd_rx, commands: cmd_rx,
state: PlayerState::Stopped, state: PlayerState::Stopped,
@ -314,13 +320,13 @@ impl PlayerInternal {
} }
fn run_onstart(&self) { fn run_onstart(&self) {
if let Some(ref program) = self.session.config().onstart { if let Some(ref program) = self.config.onstart {
util::run_program(program) util::run_program(program)
} }
} }
fn run_onstop(&self) { fn run_onstop(&self) {
if let Some(ref program) = self.session.config().onstop { if let Some(ref program) = self.config.onstop {
util::run_program(program) util::run_program(program)
} }
} }
@ -353,7 +359,7 @@ impl PlayerInternal {
} }
}; };
let format = match self.session.config().bitrate { let format = match self.config.bitrate {
Bitrate::Bitrate96 => FileFormat::OGG_VORBIS_96, Bitrate::Bitrate96 => FileFormat::OGG_VORBIS_96,
Bitrate::Bitrate160 => FileFormat::OGG_VORBIS_160, Bitrate::Bitrate160 => FileFormat::OGG_VORBIS_160,
Bitrate::Bitrate320 => FileFormat::OGG_VORBIS_320, Bitrate::Bitrate320 => FileFormat::OGG_VORBIS_320,

View file

@ -3,20 +3,17 @@ use crypto::sha1::Sha1;
use futures::sync::mpsc; use futures::sync::mpsc;
use futures::{Future, Stream, BoxFuture, IntoFuture, Poll, Async}; use futures::{Future, Stream, BoxFuture, IntoFuture, Poll, Async};
use std::io; use std::io;
use std::result::Result;
use std::str::FromStr;
use std::sync::{RwLock, Arc, Weak}; 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 config::SessionConfig;
use audio_key::AudioKeyManager; use audio_key::AudioKeyManager;
use channel::ChannelManager; use channel::ChannelManager;
@ -24,53 +21,13 @@ use mercury::MercuryManager;
use metadata::MetadataManager; use metadata::MetadataManager;
use audio_file::AudioFileManager; use audio_file::AudioFileManager;
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub enum Bitrate {
Bitrate96,
Bitrate160,
Bitrate320,
}
impl FromStr for Bitrate {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"96" => Ok(Bitrate::Bitrate96),
"160" => Ok(Bitrate::Bitrate160),
"320" => Ok(Bitrate::Bitrate320),
_ => Err(s.into()),
}
}
}
#[derive(Clone)]
pub struct Config {
pub user_agent: String,
pub device_id: String,
pub bitrate: Bitrate,
pub onstart: 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,
} }
pub struct SessionInternal { pub struct SessionInternal {
config: Config, config: SessionConfig,
data: RwLock<SessionData>, data: RwLock<SessionData>,
tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>, tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,
@ -99,7 +56,7 @@ pub fn device_id(name: &str) -> String {
} }
impl Session { impl Session {
pub fn connect(config: Config, credentials: Credentials, pub fn connect(config: SessionConfig, credentials: Credentials,
cache: Option<Cache>, handle: Handle) cache: Option<Cache>, handle: Handle)
-> Box<Future<Item=Session, Error=io::Error>> -> Box<Future<Item=Session, Error=io::Error>>
{ {
@ -135,7 +92,7 @@ impl Session {
} }
fn create(handle: &Handle, transport: connection::Transport, fn create(handle: &Handle, transport: connection::Transport,
config: Config, cache: Option<Cache>, username: String) config: SessionConfig, cache: Option<Cache>, username: String)
-> (Session, BoxFuture<(), io::Error>) -> (Session, BoxFuture<(), io::Error>)
{ {
let (sink, stream) = transport.split(); let (sink, stream) = transport.split();
@ -240,7 +197,7 @@ impl Session {
self.0.cache.as_ref() self.0.cache.as_ref()
} }
pub fn config(&self) -> &Config { pub fn config(&self) -> &SessionConfig {
&self.0.config &self.0.config
} }

View file

@ -5,9 +5,10 @@ use futures::sync::{oneshot, mpsc};
use futures::{Future, Stream, Sink, Async, Poll, BoxFuture}; use futures::{Future, Stream, Sink, Async, Poll, BoxFuture};
use protobuf::{self, Message}; use protobuf::{self, Message};
use config::ConnectConfig;
use mercury::MercuryError; use mercury::MercuryError;
use player::Player;
use mixer::Mixer; use mixer::Mixer;
use player::Player;
use session::Session; use session::Session;
use util::{now_ms, SpotifyId, SeqGenerator}; use util::{now_ms, SpotifyId, SeqGenerator};
use version; use version;
@ -59,13 +60,13 @@ fn initial_state() -> State {
}) })
} }
fn initial_device_state(name: String, volume: u16) -> DeviceState { fn initial_device_state(config: ConnectConfig, volume: u16) -> DeviceState {
protobuf_init!(DeviceState::new(), { protobuf_init!(DeviceState::new(), {
sw_version: version::version_string(), sw_version: version::version_string(),
is_active: false, is_active: false,
can_play: true, can_play: true,
volume: volume as u32, volume: volume as u32,
name: name, name: config.name,
capabilities => [ capabilities => [
@{ @{
typ: protocol::spirc::CapabilityType::kCanBePlayer, typ: protocol::spirc::CapabilityType::kCanBePlayer,
@ -73,7 +74,7 @@ fn initial_device_state(name: String, volume: u16) -> DeviceState {
}, },
@{ @{
typ: protocol::spirc::CapabilityType::kDeviceType, typ: protocol::spirc::CapabilityType::kDeviceType,
intValue => [5] intValue => [config.device_type as i64]
}, },
@{ @{
typ: protocol::spirc::CapabilityType::kGaiaEqConnectId, typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
@ -118,7 +119,7 @@ fn initial_device_state(name: String, volume: u16) -> DeviceState {
} }
impl Spirc { impl Spirc {
pub fn new(name: String, session: Session, player: Player, mixer: Box<Mixer>) pub fn new(config: ConnectConfig, session: Session, player: Player, mixer: Box<Mixer>)
-> (Spirc, SpircTask) -> (Spirc, SpircTask)
{ {
debug!("new Spirc[{}]", session.session_id()); debug!("new Spirc[{}]", session.session_id());
@ -141,7 +142,7 @@ impl Spirc {
let (cmd_tx, cmd_rx) = mpsc::unbounded(); let (cmd_tx, cmd_rx) = mpsc::unbounded();
let volume = 0xFFFF; let volume = 0xFFFF;
let device = initial_device_state(name, volume); let device = initial_device_state(config, volume);
mixer.set_volume(volume); mixer.set_volume(volume);
let mut task = SpircTask { let mut task = SpircTask {