mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
parent
910974e5e2
commit
72070b6ce0
8 changed files with 244 additions and 134 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -25,7 +25,7 @@ dependencies = [
|
|||
"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)",
|
||||
"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_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)",
|
||||
|
@ -528,7 +528,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rust-crypto"
|
||||
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 = [
|
||||
"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)",
|
||||
|
@ -1019,7 +1019,7 @@ dependencies = [
|
|||
"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 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_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"
|
||||
|
|
|
@ -19,19 +19,20 @@ use url;
|
|||
|
||||
use authentication::Credentials;
|
||||
use util;
|
||||
use config::ConnectConfig;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Discovery(Arc<DiscoveryInner>);
|
||||
struct DiscoveryInner {
|
||||
config: ConnectConfig,
|
||||
device_id: String,
|
||||
private_key: BigUint,
|
||||
public_key: BigUint,
|
||||
device_id: String,
|
||||
device_name: String,
|
||||
tx: mpsc::UnboundedSender<Credentials>,
|
||||
}
|
||||
|
||||
impl Discovery {
|
||||
pub fn new(device_name: String, device_id: String)
|
||||
pub fn new(config: ConnectConfig, device_id: String)
|
||||
-> (Discovery, mpsc::UnboundedReceiver<Credentials>)
|
||||
{
|
||||
let (tx, rx) = mpsc::unbounded();
|
||||
|
@ -41,8 +42,8 @@ impl Discovery {
|
|||
let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME);
|
||||
|
||||
let discovery = Discovery(Arc::new(DiscoveryInner {
|
||||
device_name: device_name.to_owned(),
|
||||
device_id: device_id.to_owned(),
|
||||
config: config,
|
||||
device_id: device_id,
|
||||
private_key: private_key,
|
||||
public_key: public_key,
|
||||
tx: tx,
|
||||
|
@ -65,10 +66,10 @@ impl Discovery {
|
|||
"spotifyError": 0,
|
||||
"version": "2.1.0",
|
||||
"deviceID": (self.0.device_id),
|
||||
"remoteName": (self.0.device_name),
|
||||
"remoteName": (self.0.config.name),
|
||||
"activeUser": "",
|
||||
"publicKey": (public_key),
|
||||
"deviceType": "UNKNOWN",
|
||||
"deviceType": (self.0.config.device_type.to_string().to_uppercase()),
|
||||
"libraryVersion": "0.1.0",
|
||||
"accountReq": "PREMIUM",
|
||||
"brandDisplayName": "librespot",
|
||||
|
@ -206,10 +207,10 @@ pub struct DiscoveryStream {
|
|||
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>
|
||||
{
|
||||
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 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 svc = responder.register(
|
||||
"_spotify-connect._tcp".to_owned(),
|
||||
device_name,
|
||||
config.name,
|
||||
addr.port(),
|
||||
&["VERSION=1.0", "CPath=/"]);
|
||||
|
||||
|
|
123
src/config.rs
Normal file
123
src/config.rs
Normal 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,
|
||||
}
|
|
@ -61,14 +61,15 @@ pub mod audio_key;
|
|||
pub mod authentication;
|
||||
pub mod cache;
|
||||
pub mod channel;
|
||||
pub mod config;
|
||||
pub mod diffie_hellman;
|
||||
pub mod keymaster;
|
||||
pub mod mercury;
|
||||
pub mod metadata;
|
||||
pub mod mixer;
|
||||
pub mod player;
|
||||
pub mod session;
|
||||
pub mod util;
|
||||
pub mod version;
|
||||
pub mod mixer;
|
||||
pub mod keymaster;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||
|
|
139
src/main.rs
139
src/main.rs
|
@ -26,7 +26,8 @@ use librespot::authentication::discovery::{discovery, DiscoveryStream};
|
|||
use librespot::audio_backend::{self, Sink, BACKENDS};
|
||||
use librespot::cache::Cache;
|
||||
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::version;
|
||||
|
@ -76,9 +77,10 @@ struct Setup {
|
|||
|
||||
mixer: fn() -> Box<Mixer>,
|
||||
|
||||
name: String,
|
||||
cache: Option<Cache>,
|
||||
config: Config,
|
||||
player_config: PlayerConfig,
|
||||
session_config: SessionConfig,
|
||||
connect_config: ConnectConfig,
|
||||
credentials: Option<Credentials>,
|
||||
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")
|
||||
.optflag("", "disable-audio-cache", "Disable caching of the audio data.")
|
||||
.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("", "onstart", "Run PROGRAM when playback is about to begin.", "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)
|
||||
.expect("Invalid backend");
|
||||
|
||||
let device = matches.opt_str("device");
|
||||
|
||||
let mixer_name = matches.opt_str("mixer");
|
||||
let mixer = mixer::find(mixer_name.as_ref())
|
||||
.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 device_id = librespot::session::device_id(&name);
|
||||
let use_audio_cache = !matches.opt_present("disable-audio-cache");
|
||||
|
||||
let cache = matches.opt_str("c").map(|cache_location| {
|
||||
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"),
|
||||
matches.opt_str("password"),
|
||||
cached_credentials);
|
||||
get_credentials(
|
||||
matches.opt_str("username"),
|
||||
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 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 {
|
||||
name: name,
|
||||
backend: backend,
|
||||
cache: cache,
|
||||
config: config,
|
||||
session_config: session_config,
|
||||
player_config: player_config,
|
||||
connect_config: connect_config,
|
||||
credentials: credentials,
|
||||
device: device,
|
||||
enable_discovery: enable_discovery,
|
||||
|
@ -172,9 +199,10 @@ fn setup(args: &[String]) -> Setup {
|
|||
}
|
||||
|
||||
struct Main {
|
||||
name: String,
|
||||
cache: Option<Cache>,
|
||||
config: Config,
|
||||
player_config: PlayerConfig,
|
||||
session_config: SessionConfig,
|
||||
connect_config: ConnectConfig,
|
||||
backend: fn(Option<String>) -> Box<Sink>,
|
||||
device: Option<String>,
|
||||
mixer: fn() -> Box<Mixer>,
|
||||
|
@ -191,22 +219,16 @@ struct Main {
|
|||
}
|
||||
|
||||
impl Main {
|
||||
fn new(handle: Handle,
|
||||
name: String,
|
||||
config: Config,
|
||||
cache: Option<Cache>,
|
||||
backend: fn(Option<String>) -> Box<Sink>,
|
||||
device: Option<String>,
|
||||
mixer: fn() -> Box<Mixer>) -> Main
|
||||
{
|
||||
Main {
|
||||
fn new(handle: Handle, setup: Setup) -> Main {
|
||||
let mut task = Main {
|
||||
handle: handle.clone(),
|
||||
name: name,
|
||||
cache: cache,
|
||||
config: config,
|
||||
backend: backend,
|
||||
device: device,
|
||||
mixer: mixer,
|
||||
cache: setup.cache,
|
||||
session_config: setup.session_config,
|
||||
player_config: setup.player_config,
|
||||
connect_config: setup.connect_config,
|
||||
backend: setup.backend,
|
||||
device: setup.device,
|
||||
mixer: setup.mixer,
|
||||
|
||||
connect: Box::new(futures::future::empty()),
|
||||
discovery: None,
|
||||
|
@ -214,18 +236,24 @@ impl Main {
|
|||
spirc_task: None,
|
||||
shutdown: false,
|
||||
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) {
|
||||
let device_id = self.config.device_id.clone();
|
||||
let name = self.name.clone();
|
||||
if let Some(credentials) = setup.credentials {
|
||||
task.credentials(credentials);
|
||||
}
|
||||
|
||||
self.discovery = Some(discovery(&self.handle, name, device_id).unwrap());
|
||||
task
|
||||
}
|
||||
|
||||
fn credentials(&mut self, credentials: Credentials) {
|
||||
let config = self.config.clone();
|
||||
let config = self.session_config.clone();
|
||||
let handle = self.handle.clone();
|
||||
|
||||
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());
|
||||
let device = self.device.clone();
|
||||
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 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)
|
||||
});
|
||||
|
||||
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_task = Some(spirc_task);
|
||||
|
||||
|
@ -309,16 +339,7 @@ fn main() {
|
|||
let handle = core.handle();
|
||||
|
||||
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);
|
||||
if enable_discovery {
|
||||
task.discovery();
|
||||
}
|
||||
if let Some(credentials) = credentials {
|
||||
task.credentials(credentials);
|
||||
}
|
||||
|
||||
core.run(task).unwrap()
|
||||
core.run(Main::new(handle, setup(&args))).unwrap()
|
||||
}
|
||||
|
||||
|
|
|
@ -12,9 +12,10 @@ use audio_backend::Sink;
|
|||
use audio_decrypt::AudioDecrypt;
|
||||
use audio_file::AudioFile;
|
||||
use metadata::{FileFormat, Track};
|
||||
use session::{Bitrate, Session};
|
||||
use session::Session;
|
||||
use mixer::AudioFilter;
|
||||
use util::{self, SpotifyId, Subfile};
|
||||
use config::{Bitrate, PlayerConfig};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Player {
|
||||
|
@ -23,6 +24,7 @@ pub struct Player {
|
|||
|
||||
struct PlayerInternal {
|
||||
session: Session,
|
||||
config: PlayerConfig,
|
||||
commands: std::sync::mpsc::Receiver<PlayerCommand>,
|
||||
|
||||
state: PlayerState,
|
||||
|
@ -39,8 +41,11 @@ enum PlayerCommand {
|
|||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new<F>(session: Session, audio_filter: Option<Box<AudioFilter + Send>>, sink_builder: F) -> Player
|
||||
where F: FnOnce() -> Box<Sink> + Send + 'static {
|
||||
pub fn new<F>(config: PlayerConfig, session: Session,
|
||||
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();
|
||||
|
||||
thread::spawn(move || {
|
||||
|
@ -48,6 +53,7 @@ impl Player {
|
|||
|
||||
let internal = PlayerInternal {
|
||||
session: session,
|
||||
config: config,
|
||||
commands: cmd_rx,
|
||||
|
||||
state: PlayerState::Stopped,
|
||||
|
@ -314,13 +320,13 @@ impl PlayerInternal {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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::Bitrate160 => FileFormat::OGG_VORBIS_160,
|
||||
Bitrate::Bitrate320 => FileFormat::OGG_VORBIS_320,
|
||||
|
|
|
@ -3,20 +3,17 @@ use crypto::sha1::Sha1;
|
|||
use futures::sync::mpsc;
|
||||
use futures::{Future, Stream, BoxFuture, IntoFuture, Poll, Async};
|
||||
use std::io;
|
||||
use std::result::Result;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{RwLock, Arc, Weak};
|
||||
use tokio_core::io::EasyBuf;
|
||||
use tokio_core::reactor::{Handle, Remote};
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use uuid::Uuid;
|
||||
|
||||
use apresolve::apresolve_or_fallback;
|
||||
use authentication::Credentials;
|
||||
use cache::Cache;
|
||||
use component::Lazy;
|
||||
use connection;
|
||||
use version;
|
||||
use config::SessionConfig;
|
||||
|
||||
use audio_key::AudioKeyManager;
|
||||
use channel::ChannelManager;
|
||||
|
@ -24,53 +21,13 @@ use mercury::MercuryManager;
|
|||
use metadata::MetadataManager;
|
||||
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 {
|
||||
country: String,
|
||||
canonical_username: String,
|
||||
}
|
||||
|
||||
pub struct SessionInternal {
|
||||
config: Config,
|
||||
config: SessionConfig,
|
||||
data: RwLock<SessionData>,
|
||||
|
||||
tx_connection: mpsc::UnboundedSender<(u8, Vec<u8>)>,
|
||||
|
@ -99,7 +56,7 @@ pub fn device_id(name: &str) -> String {
|
|||
}
|
||||
|
||||
impl Session {
|
||||
pub fn connect(config: Config, credentials: Credentials,
|
||||
pub fn connect(config: SessionConfig, credentials: Credentials,
|
||||
cache: Option<Cache>, handle: Handle)
|
||||
-> Box<Future<Item=Session, Error=io::Error>>
|
||||
{
|
||||
|
@ -135,7 +92,7 @@ impl Session {
|
|||
}
|
||||
|
||||
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>)
|
||||
{
|
||||
let (sink, stream) = transport.split();
|
||||
|
@ -240,7 +197,7 @@ impl Session {
|
|||
self.0.cache.as_ref()
|
||||
}
|
||||
|
||||
pub fn config(&self) -> &Config {
|
||||
pub fn config(&self) -> &SessionConfig {
|
||||
&self.0.config
|
||||
}
|
||||
|
||||
|
|
13
src/spirc.rs
13
src/spirc.rs
|
@ -5,9 +5,10 @@ use futures::sync::{oneshot, mpsc};
|
|||
use futures::{Future, Stream, Sink, Async, Poll, BoxFuture};
|
||||
use protobuf::{self, Message};
|
||||
|
||||
use config::ConnectConfig;
|
||||
use mercury::MercuryError;
|
||||
use player::Player;
|
||||
use mixer::Mixer;
|
||||
use player::Player;
|
||||
use session::Session;
|
||||
use util::{now_ms, SpotifyId, SeqGenerator};
|
||||
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(), {
|
||||
sw_version: version::version_string(),
|
||||
is_active: false,
|
||||
can_play: true,
|
||||
volume: volume as u32,
|
||||
name: name,
|
||||
name: config.name,
|
||||
capabilities => [
|
||||
@{
|
||||
typ: protocol::spirc::CapabilityType::kCanBePlayer,
|
||||
|
@ -73,7 +74,7 @@ fn initial_device_state(name: String, volume: u16) -> DeviceState {
|
|||
},
|
||||
@{
|
||||
typ: protocol::spirc::CapabilityType::kDeviceType,
|
||||
intValue => [5]
|
||||
intValue => [config.device_type as i64]
|
||||
},
|
||||
@{
|
||||
typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
|
||||
|
@ -118,7 +119,7 @@ fn initial_device_state(name: String, volume: u16) -> DeviceState {
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
debug!("new Spirc[{}]", session.session_id());
|
||||
|
@ -141,7 +142,7 @@ impl Spirc {
|
|||
let (cmd_tx, cmd_rx) = mpsc::unbounded();
|
||||
|
||||
let volume = 0xFFFF;
|
||||
let device = initial_device_state(name, volume);
|
||||
let device = initial_device_state(config, volume);
|
||||
mixer.set_volume(volume);
|
||||
|
||||
let mut task = SpircTask {
|
||||
|
|
Loading…
Reference in a new issue