Browse Source

Merge pull request #144 from librespot-org/awiouy-core_API

Changes in line with API review.
Sasha Hilton 7 years ago
parent
commit
9dc03b5c34

+ 21 - 18
Cargo.lock

@@ -316,8 +316,8 @@ dependencies = [
  "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.11.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "librespot-audio 0.1.0",
+ "librespot-connect 0.1.0",
  "librespot-core 0.1.0",
- "librespot-discovery 0.1.0",
  "librespot-metadata 0.1.0",
  "librespot-playback 0.1.0",
  "librespot-protocol 0.1.0",
@@ -357,56 +357,59 @@ dependencies = [
 ]
 
 [[package]]
-name = "librespot-core"
+name = "librespot-connect"
 version = "0.1.0"
 dependencies = [
  "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "error-chain 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.11.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "librespot-core 0.1.0",
+ "librespot-playback 0.1.0",
  "librespot-protocol 0.1.0",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "mdns 0.2.0 (git+https://github.com/plietar/rust-mdns)",
  "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
  "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)",
  "rand 0.3.20 (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 (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)",
- "shannon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.4 (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)",
+ "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
-name = "librespot-discovery"
+name = "librespot-core"
 version = "0.1.0"
 dependencies = [
  "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "error-chain 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.11.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "librespot-core 0.1.0",
+ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "librespot-protocol 0.1.0",
  "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "mdns 0.2.0 (git+https://github.com/plietar/rust-mdns)",
  "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
  "protobuf 1.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "protobuf_macros 0.6.0 (git+https://github.com/plietar/rust-protobuf-macros)",
  "rand 0.3.20 (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 (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)",
+ "shannon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tokio-io 0.1.4 (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)",
 ]
 
 [[package]]

+ 3 - 4
Cargo.toml

@@ -2,7 +2,6 @@
 name = "librespot"
 version = "0.1.0"
 authors = ["Paul Liétar <paul@lietar.net>"]
-build = "build.rs"
 license = "MIT"
 description = "Open Source Spotify client library"
 keywords = ["spotify"]
@@ -22,10 +21,10 @@ doc = false
 
 [dependencies.librespot-audio]
 path = "audio"
+[dependencies.librespot-connect]
+path = "connect"
 [dependencies.librespot-core]
 path = "core"
-[dependencies.librespot-discovery]
-path = "discovery"
 [dependencies.librespot-metadata]
 path = "metadata"
 [dependencies.librespot-playback]
@@ -67,7 +66,7 @@ jackaudio-backend = ["librespot-playback/jackaudio-backend"]
 with-tremor = ["librespot-audio/with-tremor"]
 with-vorbis = ["librespot-audio/with-vorbis"]
 
-with-dns-sd = ["librespot-discovery/with-dns-sd"]
+with-dns-sd = ["librespot-connect/with-dns-sd"]
 
 default = ["librespot-playback/portaudio-backend"]
 

+ 9 - 1
discovery/Cargo.toml → connect/Cargo.toml

@@ -1,10 +1,15 @@
 [package]
-name = "librespot-discovery"
+name = "librespot-connect"
 version = "0.1.0"
 authors = ["Paul Lietar <paul@lietar.net>"]
+build = "build.rs"
 
 [dependencies.librespot-core]
 path = "../core"
+[dependencies.librespot-playback]
+path = "../playback"
+[dependencies.librespot-protocol]
+path = "../protocol"
 
 [dependencies]
 base64 = "0.5.0"
@@ -24,6 +29,9 @@ url = "1.3"
 dns-sd = { version = "0.1.3", optional = true }
 mdns = { git = "https://github.com/plietar/rust-mdns", optional = true }
 
+[build-dependencies]
+protobuf_macros = { git = "https://github.com/plietar/rust-protobuf-macros", features = ["with-syntex"] }
+
 [features]
 default = ["mdns"]
 with-dns-sd = ["dns-sd"]

+ 0 - 1
build.rs → connect/build.rs

@@ -10,5 +10,4 @@ fn main() {
 
     println!("cargo:rerun-if-changed=src/lib.in.rs");
     println!("cargo:rerun-if-changed=src/spirc.rs");
-
 }

+ 60 - 45
discovery/src/discovery.rs → connect/src/discovery.rs

@@ -1,11 +1,11 @@
 use base64;
+use crypto;
 use crypto::digest::Digest;
 use crypto::mac::Mac;
-use crypto;
+use futures::{Future, Poll, Stream};
 use futures::sync::mpsc;
-use futures::{Future, Stream, Poll};
-use hyper::server::{Service, Request, Response, Http};
 use hyper::{self, Get, Post, StatusCode};
+use hyper::server::{Http, Request, Response, Service};
 
 #[cfg(feature = "with-dns-sd")]
 use dns_sd::DNSService;
@@ -21,10 +21,10 @@ use std::sync::Arc;
 use tokio_core::reactor::Handle;
 use url;
 
-use core::diffie_hellman::{DH_GENERATOR, DH_PRIME};
 use core::authentication::Credentials;
-use core::util;
 use core::config::ConnectConfig;
+use core::diffie_hellman::{DH_GENERATOR, DH_PRIME};
+use core::util;
 
 #[derive(Clone)]
 struct Discovery(Arc<DiscoveryInner>);
@@ -37,9 +37,10 @@ struct DiscoveryInner {
 }
 
 impl Discovery {
-    fn new(config: ConnectConfig, device_id: String)
-        -> (Discovery, mpsc::UnboundedReceiver<Credentials>)
-    {
+    fn new(
+        config: ConnectConfig,
+        device_id: String,
+    ) -> (Discovery, mpsc::UnboundedReceiver<Credentials>) {
         let (tx, rx) = mpsc::unbounded();
 
         let key_data = util::rand_vec(&mut rand::thread_rng(), 95);
@@ -59,9 +60,10 @@ impl Discovery {
 }
 
 impl Discovery {
-    fn handle_get_info(&self, _params: &BTreeMap<String, String>)
-        -> ::futures::Finished<Response, hyper::Error>
-    {
+    fn handle_get_info(
+        &self,
+        _params: &BTreeMap<String, String>,
+    ) -> ::futures::Finished<Response, hyper::Error> {
         let public_key = self.0.public_key.to_bytes_be();
         let public_key = base64::encode(&public_key);
 
@@ -85,9 +87,10 @@ impl Discovery {
         ::futures::finished(Response::new().with_body(body))
     }
 
-    fn handle_add_user(&self, params: &BTreeMap<String, String>)
-        -> ::futures::Finished<Response, hyper::Error>
-    {
+    fn handle_add_user(
+        &self,
+        params: &BTreeMap<String, String>,
+    ) -> ::futures::Finished<Response, hyper::Error> {
         let username = params.get("userName").unwrap();
         let encrypted_blob = params.get("blob").unwrap();
         let client_key = params.get("clientKey").unwrap();
@@ -133,8 +136,8 @@ impl Discovery {
 
         let decrypted = {
             let mut data = vec![0u8; encrypted.len()];
-            let mut cipher = crypto::aes::ctr(crypto::aes::KeySize::KeySize128,
-                                              &encryption_key[0..16], iv);
+            let mut cipher =
+                crypto::aes::ctr(crypto::aes::KeySize::KeySize128, &encryption_key[0..16], iv);
             cipher.process(encrypted, &mut data);
             String::from_utf8(data).unwrap()
         };
@@ -153,9 +156,7 @@ impl Discovery {
         ::futures::finished(Response::new().with_body(body))
     }
 
-    fn not_found(&self)
-        -> ::futures::Finished<Response, hyper::Error>
-    {
+    fn not_found(&self) -> ::futures::Finished<Response, hyper::Error> {
         ::futures::finished(Response::new().with_status(StatusCode::NotFound))
     }
 }
@@ -179,19 +180,22 @@ impl Service for Discovery {
         }
 
         let this = self.clone();
-        Box::new(body.fold(Vec::new(), |mut acc, chunk| {
-            acc.extend_from_slice(chunk.as_ref());
-            Ok::<_, hyper::Error>(acc)
-        }).map(move |body| {
-            params.extend(url::form_urlencoded::parse(&body).into_owned());
-            params
-        }).and_then(move |params| {
-            match (method, params.get("action").map(AsRef::as_ref)) {
-                (Get, Some("getInfo")) => this.handle_get_info(&params),
-                (Post, Some("addUser")) => this.handle_add_user(&params),
-                _ => this.not_found(),
-            }
-        }))
+        Box::new(
+            body.fold(Vec::new(), |mut acc, chunk| {
+                acc.extend_from_slice(chunk.as_ref());
+                Ok::<_, hyper::Error>(acc)
+            }).map(move |body| {
+                    params.extend(url::form_urlencoded::parse(&body).into_owned());
+                    params
+                })
+                .and_then(
+                    move |params| match (method, params.get("action").map(AsRef::as_ref)) {
+                        (Get, Some("getInfo")) => this.handle_get_info(&params),
+                        (Post, Some("addUser")) => this.handle_add_user(&params),
+                        _ => this.not_found(),
+                    },
+                ),
+        )
     }
 }
 
@@ -207,22 +211,30 @@ pub struct DiscoveryStream {
     _svc: mdns::Service,
 }
 
-pub fn discovery(handle: &Handle, config: ConnectConfig, device_id: String, port: u16)
-    -> io::Result<DiscoveryStream>
-{
+pub fn discovery(
+    handle: &Handle,
+    config: ConnectConfig,
+    device_id: String,
+    port: u16,
+) -> io::Result<DiscoveryStream> {
     let (discovery, creds_rx) = Discovery::new(config.clone(), device_id);
 
     let serve = {
         let http = Http::new();
         debug!("Zeroconf server listening on 0.0.0.0:{}", port);
-        http.serve_addr_handle(&format!("0.0.0.0:{}", port).parse().unwrap(), &handle, move || Ok(discovery.clone())).expect("Unable to bind Zeroconf to port")
+        http.serve_addr_handle(
+            &format!("0.0.0.0:{}", port).parse().unwrap(),
+            &handle,
+            move || Ok(discovery.clone()),
+        ).unwrap()
     };
 
     let s_port = serve.incoming_ref().local_addr().port();
 
     let server_future = {
         let handle = handle.clone();
-        serve.for_each(move |connection| {
+        serve
+            .for_each(move |connection| {
                 handle.spawn(connection.then(|_| Ok(())));
                 Ok(())
             })
@@ -231,22 +243,25 @@ pub fn discovery(handle: &Handle, config: ConnectConfig, device_id: String, port
     handle.spawn(server_future);
 
     #[cfg(feature = "with-dns-sd")]
-    let svc = DNSService::register(Some(&*config.name),
-       "_spotify-connect._tcp",
-       None,
-       None,
-       s_port,
-       &["VERSION=1.0", "CPath=/"]).unwrap();
+    let svc = DNSService::register(
+        Some(&*config.name),
+        "_spotify-connect._tcp",
+        None,
+        None,
+        s_port,
+        &["VERSION=1.0", "CPath=/"],
+    ).unwrap();
 
     #[cfg(not(feature = "with-dns-sd"))]
     let responder = mdns::Responder::spawn(&handle)?;
-    
+
     #[cfg(not(feature = "with-dns-sd"))]
     let svc = responder.register(
         "_spotify-connect._tcp".to_owned(),
         config.name,
         s_port,
-        &["VERSION=1.0", "CPath=/"]);
+        &["VERSION=1.0", "CPath=/"],
+    );
 
     Ok(DiscoveryStream {
         credentials: creds_rx,

+ 0 - 0
src/lib.in.rs → connect/src/lib.in.rs


+ 8 - 2
discovery/src/lib.rs → connect/src/lib.rs

@@ -1,5 +1,7 @@
-#[macro_use] extern crate log;
-#[macro_use] extern crate serde_json;
+#[macro_use]
+extern crate log;
+#[macro_use]
+extern crate serde_json;
 
 extern crate base64;
 extern crate crypto;
@@ -18,5 +20,9 @@ extern crate dns_sd;
 extern crate mdns;
 
 extern crate librespot_core as core;
+extern crate librespot_playback as playback;
+extern crate librespot_protocol as protocol;
 
 pub mod discovery;
+
+include!(concat!(env!("OUT_DIR"), "/lib.rs"));

+ 42 - 37
src/spirc.rs → connect/src/spirc.rs

@@ -1,24 +1,24 @@
+use futures::{Async, Future, Poll, Sink, Stream};
 use futures::future;
-use futures::sync::{oneshot, mpsc};
-use futures::{Future, Stream, Sink, Async, Poll};
+use futures::sync::{mpsc, oneshot};
 use protobuf::{self, Message};
 
 use core::config::ConnectConfig;
 use core::mercury::MercuryError;
 use core::session::Session;
-use core::util::{SpotifyId, SeqGenerator};
+use core::util::{SeqGenerator, SpotifyId};
 use core::version;
 
 use protocol;
-use protocol::spirc::{PlayStatus, State, MessageType, Frame, DeviceState};
+use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State};
 
 use playback::mixer::Mixer;
 use playback::player::Player;
 
-use std;
 use rand;
 use rand::Rng;
-use std::time::{UNIX_EPOCH, SystemTime};
+use std;
+use std::time::{SystemTime, UNIX_EPOCH};
 
 pub struct SpircTask {
     player: Player,
@@ -47,7 +47,7 @@ pub enum SpircCommand {
     Next,
     VolumeUp,
     VolumeDown,
-    Shutdown
+    Shutdown,
 }
 
 pub struct Spirc {
@@ -152,11 +152,13 @@ fn volume_to_mixer(volume: u16) -> u16 {
     val
 }
 
-
 impl Spirc {
-    pub fn new(config: ConnectConfig, session: Session, player: Player, mixer: Box<Mixer>)
-        -> (Spirc, SpircTask)
-    {
+    pub fn new(
+        config: ConnectConfig,
+        session: Session,
+        player: Player,
+        mixer: Box<Mixer>,
+    ) -> (Spirc, SpircTask) {
         debug!("new Spirc[{}]", session.session_id());
 
         let ident = session.device_id().to_owned();
@@ -164,15 +166,20 @@ impl Spirc {
         let uri = format!("hm://remote/3/user/{}/", session.username());
 
         let subscription = session.mercury().subscribe(&uri as &str);
-        let subscription = subscription.map(|stream| stream.map_err(|_| MercuryError)).flatten_stream();
+        let subscription = subscription
+            .map(|stream| stream.map_err(|_| MercuryError))
+            .flatten_stream();
         let subscription = Box::new(subscription.map(|response| -> Frame {
             let data = response.payload.first().unwrap();
             protobuf::parse_from_bytes(data).unwrap()
         }));
 
-        let sender = Box::new(session.mercury().sender(uri).with(|frame: Frame| {
-            Ok(frame.write_to_bytes().unwrap())
-        }));
+        let sender = Box::new(
+            session
+                .mercury()
+                .sender(uri)
+                .with(|frame: Frame| Ok(frame.write_to_bytes().unwrap())),
+        );
 
         let (cmd_tx, cmd_rx) = mpsc::unbounded();
 
@@ -200,9 +207,7 @@ impl Spirc {
             session: session.clone(),
         };
 
-        let spirc = Spirc {
-            commands: cmd_tx,
-        };
+        let spirc = Spirc { commands: cmd_tx };
 
         task.hello();
 
@@ -268,9 +273,7 @@ impl Future for SpircTask {
                         self.handle_end_of_track();
                     }
                     Ok(Async::NotReady) => (),
-                    Err(oneshot::Canceled) => {
-                        self.end_of_track = Box::new(future::empty())
-                    }
+                    Err(oneshot::Canceled) => self.end_of_track = Box::new(future::empty()),
                 }
             }
 
@@ -357,15 +360,18 @@ impl SpircTask {
     }
 
     fn handle_frame(&mut self, frame: Frame) {
-        debug!("{:?} {:?} {} {} {}",
-               frame.get_typ(),
-               frame.get_device_state().get_name(),
-               frame.get_ident(),
-               frame.get_seq_nr(),
-               frame.get_state_update_id());
-
-        if frame.get_ident() == self.ident ||
-           (frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident)) {
+        debug!(
+            "{:?} {:?} {} {} {}",
+            frame.get_typ(),
+            frame.get_device_state().get_name(),
+            frame.get_ident(),
+            frame.get_seq_nr(),
+            frame.get_state_update_id()
+        );
+
+        if frame.get_ident() == self.ident
+            || (frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident))
+        {
             return;
         }
 
@@ -383,7 +389,8 @@ impl SpircTask {
                 self.update_tracks(&frame);
 
                 if self.state.get_track().len() > 0 {
-                    self.state.set_position_ms(frame.get_state().get_position_ms());
+                    self.state
+                        .set_position_ms(frame.get_state().get_position_ms());
                     self.state.set_position_measured_at(now_ms() as u64);
 
                     let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
@@ -437,8 +444,7 @@ impl SpircTask {
 
             MessageType::kMessageTypeShuffle => {
                 self.state.set_shuffle(frame.get_state().get_shuffle());
-                if self.state.get_shuffle()
-                {
+                if self.state.get_shuffle() {
                     let current_index = self.state.get_playing_track_index();
                     {
                         let tracks = self.state.mut_track();
@@ -471,14 +477,13 @@ impl SpircTask {
 
             MessageType::kMessageTypeVolume => {
                 self.device.set_volume(frame.get_volume());
-                self.mixer.set_volume(volume_to_mixer(frame.get_volume() as u16));
+                self.mixer
+                    .set_volume(volume_to_mixer(frame.get_volume() as u16));
                 self.notify(None);
             }
 
             MessageType::kMessageTypeNotify => {
-                if self.device.get_is_active() &&
-                    frame.get_device_state().get_is_active()
-                {
+                if self.device.get_is_active() && frame.get_device_state().get_is_active() {
                     self.device.set_is_active(false);
                     self.state.set_status(PlayStatus::kPlayStatusStop);
                     self.player.stop();

+ 0 - 42
core/src/config.rs

@@ -20,31 +20,6 @@ impl Default for SessionConfig {
     }
 }
 
-#[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,
@@ -99,23 +74,6 @@ impl Default for DeviceType {
     }
 }
 
-#[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,

+ 1 - 1
examples/play.rs

@@ -5,7 +5,7 @@ use std::env;
 use tokio_core::reactor::Core;
 
 use librespot::core::authentication::Credentials;
-use librespot::core::config::{PlayerConfig, SessionConfig};
+use librespot::playback::config::{PlayerConfig, SessionConfig};
 use librespot::core::session::Session;
 use librespot::core::util::SpotifyId;
 

+ 43 - 0
playback/src/config.rs

@@ -0,0 +1,43 @@
+use std::str::FromStr;
+
+#[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, 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,
+        }
+    }
+}

+ 1 - 0
playback/src/lib.rs

@@ -22,5 +22,6 @@ extern crate librespot_core as core;
 extern crate librespot_metadata as metadata;
 
 pub mod audio_backend;
+pub mod config;
 pub mod mixer;
 pub mod player;

+ 1 - 1
playback/src/player.rs

@@ -9,7 +9,7 @@ use std::sync::mpsc::{RecvError, TryRecvError, RecvTimeoutError};
 use std::thread;
 use std::time::Duration;
 
-use core::config::{Bitrate, PlayerConfig};
+use config::{Bitrate, PlayerConfig};
 use core::session::Session;
 use core::util::SpotifyId;
 

+ 1 - 6
src/lib.rs

@@ -2,8 +2,6 @@
 
 #![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))]
 
-#[macro_use] extern crate log;
-
 extern crate base64;
 extern crate crypto;
 extern crate futures;
@@ -15,11 +13,8 @@ extern crate tokio_core;
 extern crate url;
 
 pub extern crate librespot_audio as audio;
+pub extern crate librespot_connect as connect;
 pub extern crate librespot_core as core;
-pub extern crate librespot_discovery as discovery;
 pub extern crate librespot_playback as playback;
 pub extern crate librespot_protocol as protocol;
 pub extern crate librespot_metadata as metadata;
-
-
-include!(concat!(env!("OUT_DIR"), "/lib.rs"));

+ 4 - 3
src/main.rs

@@ -20,15 +20,16 @@ use std::mem;
 
 use librespot::core::authentication::{get_credentials, Credentials};
 use librespot::core::cache::Cache;
-use librespot::core::config::{Bitrate, DeviceType, PlayerConfig, SessionConfig, ConnectConfig};
+use librespot::core::config::{DeviceType, SessionConfig, ConnectConfig};
 use librespot::core::session::Session;
 use librespot::core::version;
 
 use librespot::playback::audio_backend::{self, Sink, BACKENDS};
-use librespot::discovery::discovery::{discovery, DiscoveryStream};
+use librespot::playback::config::{Bitrate, PlayerConfig};
+use librespot::connect::discovery::{discovery, DiscoveryStream};
 use librespot::playback::mixer::{self, Mixer};
 use librespot::playback::player::Player;
-use librespot::spirc::{Spirc, SpircTask};
+use librespot::connect::spirc::{Spirc, SpircTask};
 
 fn usage(program: &str, opts: &getopts::Options) -> String {
     let brief = format!("Usage: {} [options]", program);