From 8b8032634a7a478020c7253def63f3ba18aa794e Mon Sep 17 00:00:00 2001 From: Will Stott Date: Sat, 19 Oct 2019 12:42:23 +0100 Subject: [PATCH 01/29] Fix shuffle and repeat when changing contexts to match Android behaviour Seems strange that these fields return true in the has_ methods when as far as I can tell it's impossible to remove the shuffle/repeat state in the client without explicitly toggling them with their dedicated buttons --- connect/src/spirc.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 37576a9..edb7c29 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -807,8 +807,16 @@ impl SpircTask { self.state.set_playing_track_index(index); self.state.set_track(tracks.into_iter().cloned().collect()); self.state.set_context_uri(context_uri); - self.state.set_repeat(frame.get_state().get_repeat()); - self.state.set_shuffle(frame.get_state().get_shuffle()); + // has_shuffle/repeat seem to always be true in these replace msgs, + // but to replicate the behaviour of the Android client we have to + // ignore false values. + let state = frame.get_state(); + if state.get_repeat() { + self.state.set_repeat(true); + } + if state.get_shuffle() { + self.state.set_shuffle(true); + } } fn load_track(&mut self, play: bool) { From 63a5c4de7fae85962dd035e4c356dce1fd98bb40 Mon Sep 17 00:00:00 2001 From: dbr/Ben Date: Sat, 28 Dec 2019 20:33:06 +1030 Subject: [PATCH 02/29] Update readme regarding libspotify deprecation --- README.md | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 0144bdd..69a5ccc 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,10 @@ [![Gitter chat](https://badges.gitter.im/librespot-org/librespot.png)](https://gitter.im/librespot-org/spotify-connect-resources) [![Crates.io](https://img.shields.io/crates/v/librespot.svg)](https://crates.io/crates/librespot) -Current maintainer is @awiouy folks. +Current maintainer is [@awiouy](https://github.com/awiouy) folks. # librespot -*librespot* is an open source client library for Spotify. It enables -applications to use Spotify's service, without using the official but -closed-source `libspotify`. Additionally, it will provide extra features -which are not available in the official library. +*librespot* is an open source client library for Spotify. It enables applications to use Spotify's service to control and play music via various backends, and to act as a Spotify Connect receiver. It is an alternative to the official and [now deprecated](https://github.com/mopidy/mopidy-spotify/issues/110) closed-source `libspotify`. Additionally, it will provide extra features which are not available in the official library. _Note: librespot only works with Spotify Premium. This will remain the case for the foreseeable future, as we are unlikely to work on implementing the features such as limited skips and adverts that would be required to make librespot compliant with free accounts._ @@ -31,7 +28,6 @@ There is some brief documentation on how the protocol works in the [docs](https: If you wish to learn more about how librespot works overall, the best way is to simply read the code, and ask any questions you have in our [Gitter Room](https://gitter.im/librespot-org/spotify-connect-resources). # Issues - If you run into a bug when using librespot, please search the existing issues before opening a new one. Chances are, we've encountered it before, and have provided a resolution. If not, please open a new one, and where possible, include the backtrace librespot generates on crashing, along with anything we can use to reproduce the issue, eg. the Spotify URI of the song that caused the crash. # Building From becb3146666d9bfc1eca6e01fce04b0a2b847c83 Mon Sep 17 00:00:00 2001 From: dbr/Ben Date: Sat, 28 Dec 2019 23:06:55 +1030 Subject: [PATCH 03/29] Update deprecation link to better summary --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 69a5ccc..c7dd05e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Current maintainer is [@awiouy](https://github.com/awiouy) folks. # librespot -*librespot* is an open source client library for Spotify. It enables applications to use Spotify's service to control and play music via various backends, and to act as a Spotify Connect receiver. It is an alternative to the official and [now deprecated](https://github.com/mopidy/mopidy-spotify/issues/110) closed-source `libspotify`. Additionally, it will provide extra features which are not available in the official library. +*librespot* is an open source client library for Spotify. It enables applications to use Spotify's service to control and play music via various backends, and to act as a Spotify Connect receiver. It is an alternative to the official and [now deprecated](https://pyspotify.mopidy.com/en/latest/#libspotify-s-deprecation) closed-source `libspotify`. Additionally, it will provide extra features which are not available in the official library. _Note: librespot only works with Spotify Premium. This will remain the case for the foreseeable future, as we are unlikely to work on implementing the features such as limited skips and adverts that would be required to make librespot compliant with free accounts._ From 6c6ba88590db475c0194143acb2549176121cb79 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Tue, 7 Jan 2020 12:13:49 +0100 Subject: [PATCH 04/29] Don't panic when no autoplay uri is obtained For example from Liked Songs (`spotify:user:xxx:collection`) --- connect/src/spirc.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 4fd4288..9594082 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -795,10 +795,14 @@ impl SpircTask { let query_uri = format!("hm://autoplay-enabled/query?uri={}", uri); let request = self.session.mercury().get(query_uri); Box::new(request.and_then(move |response| { - let data = response.payload.first().expect("Empty autoplay uri").to_vec(); - let autoplay_uri = String::from_utf8(data).unwrap(); - - Ok(autoplay_uri) + if response.status_code == 200 { + let data = response.payload.first().expect("Empty autoplay uri").to_vec(); + let autoplay_uri = String::from_utf8(data).unwrap(); + Ok(autoplay_uri) + } else { + warn!("No autoplay_uri found"); + Err(MercuryError) + } })) } From be2ad9059a47135768259454b7659984c6c28894 Mon Sep 17 00:00:00 2001 From: marcelbuesing Date: Fri, 17 Jan 2020 15:35:46 +0100 Subject: [PATCH 05/29] Migrate to rust 2018 edition --- Cargo.toml | 1 + examples/play.rs | 3 --- examples/playlist_tracks.rs | 8 +------- src/lib.rs | 9 --------- src/main.rs | 28 +++++++--------------------- src/player_event_handler.rs | 1 + 6 files changed, 10 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a42e2b2..6605b8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ description = "An open source client library for Spotify, with support for Spoti keywords = ["spotify"] repository = "https://github.com/librespot-org/librespot" readme = "README.md" +edition = "2018" [workspace] diff --git a/examples/play.rs b/examples/play.rs index 87f6882..c619b59 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -1,6 +1,3 @@ -extern crate librespot; -extern crate tokio_core; - use std::env; use tokio_core::reactor::Core; diff --git a/examples/playlist_tracks.rs b/examples/playlist_tracks.rs index 5d6aec5..3e4202d 100644 --- a/examples/playlist_tracks.rs +++ b/examples/playlist_tracks.rs @@ -1,11 +1,5 @@ -extern crate log; -extern crate env_logger; - -extern crate librespot; -extern crate tokio_core; -extern crate tokio_io; -extern crate futures; +use env_logger; use std::env; use tokio_core::reactor::Core; diff --git a/src/lib.rs b/src/lib.rs index f73db1a..610062e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,15 +1,6 @@ #![crate_name = "librespot"] #![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))] -extern crate base64; -extern crate futures; -extern crate hyper; -extern crate num_bigint; -extern crate protobuf; -extern crate rand; -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; diff --git a/src/main.rs b/src/main.rs index e193257..db19726 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,21 +1,7 @@ -extern crate env_logger; -extern crate futures; -extern crate getopts; -extern crate librespot; -#[macro_use] -extern crate log; -extern crate hex; -extern crate rpassword; -extern crate sha1; -extern crate tokio_core; -extern crate tokio_io; -extern crate tokio_process; -extern crate tokio_signal; -extern crate url; - use futures::sync::mpsc::UnboundedReceiver; use futures::{Async, Future, Poll, Stream}; use sha1::{Digest, Sha1}; +use log::{error, info, trace, warn}; use std::env; use std::io::{self, stderr, Write}; use std::mem; @@ -40,7 +26,7 @@ use librespot::playback::mixer::{self, Mixer, MixerConfig}; use librespot::playback::player::{Player, PlayerEvent}; mod player_event_handler; -use player_event_handler::run_program_on_events; +use crate::player_event_handler::run_program_on_events; fn device_id(name: &str) -> String { hex::encode(Sha1::digest(name.as_bytes())) @@ -86,10 +72,10 @@ fn list_backends() { #[derive(Clone)] struct Setup { - backend: fn(Option) -> Box, + backend: fn(Option) -> Box, device: Option, - mixer: fn(Option) -> Box, + mixer: fn(Option) -> Box, cache: Option, player_config: PlayerConfig, @@ -367,9 +353,9 @@ struct Main { player_config: PlayerConfig, session_config: SessionConfig, connect_config: ConnectConfig, - backend: fn(Option) -> Box, + backend: fn(Option) -> Box, device: Option, - mixer: fn(Option) -> Box, + mixer: fn(Option) -> Box, mixer_config: MixerConfig, handle: Handle, @@ -378,7 +364,7 @@ struct Main { spirc: Option, spirc_task: Option, - connect: Box>, + connect: Box>, shutdown: bool, diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index 1e682b9..6da600b 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -1,3 +1,4 @@ +use log::info; use librespot::playback::player::PlayerEvent; use tokio_process::{Child, CommandExt}; use std::collections::HashMap; From d26590afc536128d22ef560a842aef87964a2d20 Mon Sep 17 00:00:00 2001 From: ashthespy Date: Tue, 8 Oct 2019 11:31:18 +0200 Subject: [PATCH 06/29] Update to Rust 2018 - Fix deprecated Error::cause warnings and missing dyn - Reset max_width - Add rustfmt to Travis - Run rustfmt on full codebase with `cargo fmt --all` - Add rustfmt to Travis - Complete migration to edition 2018 - Replace try! shorthand - Use explicit `dyn Trait` --- .travis.yml | 2 + audio/src/decrypt.rs | 13 +++--- audio/src/fetch.rs | 4 +- audio/src/lib.rs | 2 +- audio/src/libvorbis_decoder.rs | 4 +- connect/src/context.rs | 4 +- connect/src/discovery.rs | 55 ++++++++++++------------ connect/src/lib.rs | 4 +- connect/src/spirc.rs | 51 +++++++++++++--------- core/src/apresolve.rs | 19 ++++---- core/src/audio_key.rs | 10 +++-- core/src/authentication.rs | 31 ++++++------- core/src/cache.rs | 8 ++-- core/src/channel.rs | 2 +- core/src/config.rs | 2 +- core/src/connection/codec.rs | 3 +- core/src/connection/handshake.rs | 26 +++++------ core/src/connection/mod.rs | 46 ++++++++++++-------- core/src/diffie_hellman.rs | 21 +++++---- core/src/keymaster.rs | 6 +-- core/src/lib.rs | 8 ++-- core/src/mercury/mod.rs | 7 +-- core/src/mercury/types.rs | 5 ++- core/src/proxytunnel.rs | 7 ++- core/src/session.rs | 45 +++++++++++-------- core/src/spotify_id.rs | 3 +- core/src/util/mod.rs | 5 ++- core/src/volume.rs | 4 +- examples/play.rs | 4 +- metadata/src/lib.rs | 2 +- playback/src/audio_backend/alsa.rs | 3 +- playback/src/audio_backend/jackaudio.rs | 15 ++++--- playback/src/audio_backend/mod.rs | 8 ++-- playback/src/audio_backend/pipe.rs | 7 ++- playback/src/audio_backend/portaudio.rs | 10 +++-- playback/src/audio_backend/pulseaudio.rs | 6 ++- playback/src/audio_backend/rodio.rs | 17 ++++---- playback/src/mixer/alsamixer.rs | 14 +++--- playback/src/mixer/mod.rs | 17 ++++---- playback/src/mixer/softmixer.rs | 2 +- playback/src/player.rs | 29 ++++++++----- protocol/build.rs | 14 +++--- rustfmt.toml | 3 +- src/main.rs | 19 ++++++-- src/player_event_handler.rs | 2 +- 45 files changed, 331 insertions(+), 238 deletions(-) diff --git a/.travis.yml b/.travis.yml index 182adfd..0d6d19f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,12 +28,14 @@ addons: - libsdl2-dev before_script: + - rustup component add rustfmt - mkdir -p ~/.cargo - echo '[target.armv7-unknown-linux-gnueabihf]' > ~/.cargo/config - echo 'linker = "arm-linux-gnueabihf-gcc"' >> ~/.cargo/config - rustup target add armv7-unknown-linux-gnueabihf script: + - cargo fmt --all -- --check - cargo build --locked --no-default-features - cargo build --locked --examples - cargo build --locked --no-default-features --features "with-tremor" diff --git a/audio/src/decrypt.rs b/audio/src/decrypt.rs index d2697e7..818eb34 100644 --- a/audio/src/decrypt.rs +++ b/audio/src/decrypt.rs @@ -1,16 +1,13 @@ use std::io; -use aes_ctr::Aes128Ctr; -use aes_ctr::stream_cipher::{ - NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek -}; use aes_ctr::stream_cipher::generic_array::GenericArray; +use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; +use aes_ctr::Aes128Ctr; use librespot_core::audio_key::AudioKey; const AUDIO_AESIV: [u8; 16] = [ - 0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, - 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93, + 0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93, ]; pub struct AudioDecrypt { @@ -30,7 +27,7 @@ impl AudioDecrypt { impl io::Read for AudioDecrypt { fn read(&mut self, output: &mut [u8]) -> io::Result { - let len = try!(self.reader.read(output)); + let len = self.reader.read(output)?; self.cipher.apply_keystream(&mut output[..len]); @@ -40,7 +37,7 @@ impl io::Read for AudioDecrypt { impl io::Seek for AudioDecrypt { fn seek(&mut self, pos: io::SeekFrom) -> io::Result { - let newpos = try!(self.reader.seek(pos)); + let newpos = self.reader.seek(pos)?; self.cipher.seek(newpos); diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index 53c9545..a7f8090 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -1009,7 +1009,7 @@ impl Read for AudioFileStreaming { self.position = self.read_file.seek(SeekFrom::Start(offset as u64)).unwrap(); let read_len = min(length, available_length); - let read_len = try!(self.read_file.read(&mut output[..read_len])); + let read_len = self.read_file.read(&mut output[..read_len])?; if download_message_printed { debug!( @@ -1031,7 +1031,7 @@ impl Read for AudioFileStreaming { impl Seek for AudioFileStreaming { fn seek(&mut self, pos: SeekFrom) -> io::Result { - self.position = try!(self.read_file.seek(pos)); + self.position = self.read_file.seek(pos)?; // Do not seek past EOF self.shared .read_position diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 9a82f90..3e13c07 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -31,6 +31,6 @@ pub use fetch::{ }; #[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))] -pub use lewton_decoder::{VorbisDecoder, VorbisError, VorbisPacket}; +pub use crate::lewton_decoder::{VorbisDecoder, VorbisError, VorbisPacket}; #[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] pub use libvorbis_decoder::{VorbisDecoder, VorbisError, VorbisPacket}; diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index eef1708..c219825 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -77,7 +77,7 @@ impl error::Error for VorbisError { error::Error::description(&self.0) } - fn cause(&self) -> Option<&error::Error> { - error::Error::cause(&self.0) + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + error::Error::source(&self.0) } } diff --git a/connect/src/context.rs b/connect/src/context.rs index c59e389..5a94f6c 100644 --- a/connect/src/context.rs +++ b/connect/src/context.rs @@ -1,5 +1,5 @@ +use crate::protocol::spirc::TrackRef; use librespot_core::spotify_id::SpotifyId; -use protocol::spirc::TrackRef; use serde; @@ -69,7 +69,7 @@ fn deserialize_protobuf_TrackRef<'d, D>(de: D) -> Result, D::Error where D: serde::Deserializer<'d>, { - let v: Vec = try!(serde::Deserialize::deserialize(de)); + let v: Vec = serde::Deserialize::deserialize(de)?; let track_vec = v .iter() .map(|v| { diff --git a/connect/src/discovery.rs b/connect/src/discovery.rs index de575b1..9779e6f 100644 --- a/connect/src/discovery.rs +++ b/connect/src/discovery.rs @@ -1,13 +1,13 @@ -use base64; -use sha1::{Sha1, Digest}; -use hmac::{Hmac, Mac}; -use aes_ctr::Aes128Ctr; -use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher}; use aes_ctr::stream_cipher::generic_array::GenericArray; +use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher}; +use aes_ctr::Aes128Ctr; +use base64; use futures::sync::mpsc; use futures::{Future, Poll, Stream}; +use hmac::{Hmac, Mac}; use hyper::server::{Http, Request, Response, Service}; use hyper::{self, Get, Post, StatusCode}; +use sha1::{Digest, Sha1}; #[cfg(feature = "with-dns-sd")] use dns_sd::DNSService; @@ -114,21 +114,18 @@ impl Discovery { let base_key = &base_key[..16]; let checksum_key = { - let mut h = HmacSha1::new_varkey(base_key) - .expect("HMAC can take key of any size"); + let mut h = HmacSha1::new_varkey(base_key).expect("HMAC can take key of any size"); h.input(b"checksum"); h.result().code() }; let encryption_key = { - let mut h = HmacSha1::new_varkey(&base_key) - .expect("HMAC can take key of any size"); + let mut h = HmacSha1::new_varkey(&base_key).expect("HMAC can take key of any size"); h.input(b"encryption"); h.result().code() }; - let mut h = HmacSha1::new_varkey(&checksum_key) - .expect("HMAC can take key of any size"); + let mut h = HmacSha1::new_varkey(&checksum_key).expect("HMAC can take key of any size"); h.input(encrypted); if let Err(_) = h.verify(cksum) { warn!("Login error for user {:?}: MAC mismatch", username); @@ -139,7 +136,7 @@ impl Discovery { }); let body = result.to_string(); - return ::futures::finished(Response::new().with_body(body)) + return ::futures::finished(Response::new().with_body(body)); } let decrypted = { @@ -152,7 +149,8 @@ impl Discovery { String::from_utf8(data).unwrap() }; - let credentials = Credentials::with_blob(username.to_owned(), &decrypted, &self.0.device_id); + let credentials = + Credentials::with_blob(username.to_owned(), &decrypted, &self.0.device_id); self.0.tx.unbounded_send(credentials).unwrap(); @@ -175,7 +173,7 @@ impl Service for Discovery { type Request = Request; type Response = Response; type Error = hyper::Error; - type Future = Box>; + type Future = Box>; fn call(&self, request: Request) -> Self::Future { let mut params = BTreeMap::new(); @@ -194,17 +192,18 @@ impl Service for Discovery { 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(¶ms), - (Post, Some("addUser")) => this.handle_add_user(¶ms), - _ => this.not_found(), - }, - ), + }) + .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(¶ms), + (Post, Some("addUser")) => this.handle_add_user(¶ms), + _ => this.not_found(), + } + }), ) } } @@ -235,7 +234,8 @@ pub fn discovery( &format!("0.0.0.0:{}", port).parse().unwrap(), &handle, move || Ok(discovery.clone()), - ).unwrap() + ) + .unwrap() }; let s_port = serve.incoming_ref().local_addr().port(); @@ -260,7 +260,8 @@ pub fn discovery( None, s_port, &["VERSION=1.0", "CPath=/"], - ).unwrap(); + ) + .unwrap(); #[cfg(not(feature = "with-dns-sd"))] let responder = libmdns::Responder::spawn(&handle)?; diff --git a/connect/src/lib.rs b/connect/src/lib.rs index d029058..118c85d 100644 --- a/connect/src/lib.rs +++ b/connect/src/lib.rs @@ -15,10 +15,10 @@ extern crate rand; extern crate tokio_core; extern crate url; -extern crate sha1; -extern crate hmac; extern crate aes_ctr; extern crate block_modes; +extern crate hmac; +extern crate sha1; #[cfg(feature = "with-dns-sd")] extern crate dns_sd; diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 9594082..29e6b1d 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -9,7 +9,11 @@ use rand; use rand::seq::SliceRandom; use serde_json; -use context::StationContext; +use crate::context::StationContext; +use crate::playback::mixer::Mixer; +use crate::playback::player::Player; +use crate::protocol; +use crate::protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef}; use librespot_core::config::ConnectConfig; use librespot_core::mercury::MercuryError; use librespot_core::session::Session; @@ -17,14 +21,10 @@ use librespot_core::spotify_id::{SpotifyAudioType, SpotifyId, SpotifyIdError}; use librespot_core::util::SeqGenerator; use librespot_core::version; use librespot_core::volume::Volume; -use playback::mixer::Mixer; -use playback::player::Player; -use protocol; -use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State, TrackRef}; pub struct SpircTask { player: Player, - mixer: Box, + mixer: Box, config: SpircTaskConfig, sequence: SeqGenerator, @@ -33,15 +33,15 @@ pub struct SpircTask { device: DeviceState, state: State, - subscription: Box>, - sender: Box>, + subscription: Box>, + sender: Box>, commands: mpsc::UnboundedReceiver, - end_of_track: Box>, + end_of_track: Box>, shutdown: bool, session: Session, - context_fut: Box>, - autoplay_fut: Box>, + context_fut: Box>, + autoplay_fut: Box>, context: Option, } @@ -221,7 +221,7 @@ impl Spirc { config: ConnectConfig, session: Session, player: Player, - mixer: Box, + mixer: Box, ) -> (Spirc, SpircTask) { debug!("new Spirc[{}]", session.session_id()); @@ -526,7 +526,8 @@ impl SpircTask { if self.state.get_track().len() > 0 { let now = self.now_ms(); - 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 as u64); let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay; @@ -689,7 +690,8 @@ impl SpircTask { tracks_len - new_index < CONTEXT_FETCH_THRESHOLD ); let context_uri = self.state.get_context_uri().to_owned(); - if (context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:")) + if (context_uri.starts_with("spotify:station:") + || context_uri.starts_with("spotify:dailymix:")) && ((self.state.get_track().len() as u32) - new_index) < CONTEXT_FETCH_THRESHOLD { self.context_fut = self.resolve_station(&context_uri); @@ -785,13 +787,16 @@ impl SpircTask { self.state.get_position_ms() + diff as u32 } - fn resolve_station(&self, uri: &str) -> Box> { + fn resolve_station( + &self, + uri: &str, + ) -> Box> { let radio_uri = format!("hm://radio-apollo/v3/stations/{}", uri); self.resolve_uri(&radio_uri) } - fn resolve_autoplay_uri(&self, uri: &str) -> Box> { + fn resolve_autoplay_uri(&self, uri: &str) -> Box> { let query_uri = format!("hm://autoplay-enabled/query?uri={}", uri); let request = self.session.mercury().get(query_uri); Box::new(request.and_then(move |response| { @@ -806,11 +811,14 @@ impl SpircTask { })) } - fn resolve_uri(&self, uri: &str) -> Box> { + fn resolve_uri(&self, uri: &str) -> Box> { let request = self.session.mercury().get(uri); Box::new(request.and_then(move |response| { - let data = response.payload.first().expect("Empty payload on context uri"); + let data = response + .payload + .first() + .expect("Empty payload on context uri"); let response: serde_json::Value = serde_json::from_slice(&data).unwrap(); Ok(response) @@ -828,7 +836,8 @@ impl SpircTask { track_vec.drain(0..head); } track_vec.extend_from_slice(&new_tracks); - self.state.set_track(protobuf::RepeatedField::from_vec(track_vec)); + self.state + .set_track(protobuf::RepeatedField::from_vec(track_vec)); // Update playing index if let Some(new_index) = self @@ -849,7 +858,9 @@ impl SpircTask { let context_uri = frame.get_state().get_context_uri().to_owned(); let tracks = frame.get_state().get_track(); debug!("Frame has {:?} tracks", tracks.len()); - if context_uri.starts_with("spotify:station:") || context_uri.starts_with("spotify:dailymix:") { + if context_uri.starts_with("spotify:station:") + || context_uri.starts_with("spotify:dailymix:") + { self.context_fut = self.resolve_station(&context_uri); } else if self.config.autoplay { info!("Fetching autoplay context uri"); diff --git a/core/src/apresolve.rs b/core/src/apresolve.rs index bcb7bfe..94d9424 100644 --- a/core/src/apresolve.rs +++ b/core/src/apresolve.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use tokio_core::reactor::Handle; use url::Url; -error_chain!{} +error_chain! {} #[derive(Clone, Debug, Serialize, Deserialize)] pub struct APResolveData { @@ -21,7 +21,7 @@ fn apresolve( handle: &Handle, proxy: &Option, ap_port: &Option, -) -> Box> { +) -> Box> { let url = Uri::from_str(APRESOLVE_ENDPOINT).expect("invalid AP resolve URL"); let use_proxy = proxy.is_some(); @@ -52,19 +52,20 @@ fn apresolve( }) }); let body = body.then(|result| result.chain_err(|| "HTTP error")); - let body = body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response")); + let body = + body.and_then(|body| String::from_utf8(body).chain_err(|| "invalid UTF8 in response")); - let data = - body.and_then(|body| serde_json::from_str::(&body).chain_err(|| "invalid JSON")); + let data = body + .and_then(|body| serde_json::from_str::(&body).chain_err(|| "invalid JSON")); let p = ap_port.clone(); let ap = data.and_then(move |data| { let mut aps = data.ap_list.iter().filter(|ap| { if p.is_some() { - Uri::from_str(ap) - .ok() - .map_or(false, |uri| uri.port().map_or(false, |port| port == p.unwrap())) + Uri::from_str(ap).ok().map_or(false, |uri| { + uri.port().map_or(false, |port| port == p.unwrap()) + }) } else if use_proxy { // It is unlikely that the proxy will accept CONNECT on anything other than 443. Uri::from_str(ap) @@ -86,7 +87,7 @@ pub(crate) fn apresolve_or_fallback( handle: &Handle, proxy: &Option, ap_port: &Option, -) -> Box> +) -> Box> where E: 'static, { diff --git a/core/src/audio_key.rs b/core/src/audio_key.rs index 410cc2c..1e5310c 100644 --- a/core/src/audio_key.rs +++ b/core/src/audio_key.rs @@ -5,8 +5,8 @@ use futures::{Async, Future, Poll}; use std::collections::HashMap; use std::io::Write; -use spotify_id::{FileId, SpotifyId}; -use util::SeqGenerator; +use crate::spotify_id::{FileId, SpotifyId}; +use crate::util::SeqGenerator; #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] pub struct AudioKey(pub [u8; 16]); @@ -35,7 +35,11 @@ impl AudioKeyManager { let _ = sender.send(Ok(AudioKey(key))); } 0xe => { - warn!("error audio key {:x} {:x}", data.as_ref()[0], data.as_ref()[1]); + warn!( + "error audio key {:x} {:x}", + data.as_ref()[0], + data.as_ref()[1] + ); let _ = sender.send(Err(AudioKeyError)); } _ => (), diff --git a/core/src/authentication.rs b/core/src/authentication.rs index 07c014f..36cbd43 100644 --- a/core/src/authentication.rs +++ b/core/src/authentication.rs @@ -1,18 +1,18 @@ +use aes::Aes192; use base64; use byteorder::{BigEndian, ByteOrder}; -use aes::Aes192; use hmac::Hmac; -use sha1::{Sha1, Digest}; use pbkdf2::pbkdf2; use protobuf::ProtobufEnum; use serde; use serde_json; +use sha1::{Digest, Sha1}; use std::fs::File; use std::io::{self, Read, Write}; use std::ops::FnOnce; use std::path::Path; -use protocol::authentication::AuthenticationType; +use crate::protocol::authentication::AuthenticationType; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Credentials { @@ -40,24 +40,24 @@ impl Credentials { pub fn with_blob(username: String, encrypted_blob: &str, device_id: &str) -> Credentials { fn read_u8(stream: &mut R) -> io::Result { let mut data = [0u8]; - try!(stream.read_exact(&mut data)); + stream.read_exact(&mut data)?; Ok(data[0]) } fn read_int(stream: &mut R) -> io::Result { - let lo = try!(read_u8(stream)) as u32; + let lo = read_u8(stream)? as u32; if lo & 0x80 == 0 { return Ok(lo); } - let hi = try!(read_u8(stream)) as u32; + let hi = read_u8(stream)? as u32; Ok(lo & 0x7f | hi << 7) } fn read_bytes(stream: &mut R) -> io::Result> { - let length = try!(read_int(stream)); + let length = read_int(stream)?; let mut data = vec![0u8; length as usize]; - try!(stream.read_exact(&mut data)); + stream.read_exact(&mut data)?; Ok(data) } @@ -76,9 +76,9 @@ impl Credentials { // decrypt data using ECB mode without padding let blob = { - use aes::block_cipher_trait::BlockCipher; - use aes::block_cipher_trait::generic_array::GenericArray; use aes::block_cipher_trait::generic_array::typenum::Unsigned; + use aes::block_cipher_trait::generic_array::GenericArray; + use aes::block_cipher_trait::BlockCipher; let mut data = base64::decode(encrypted_blob).unwrap(); let cipher = Aes192::new(GenericArray::from_slice(&key)); @@ -148,7 +148,7 @@ where T: ProtobufEnum, D: serde::Deserializer<'de>, { - let v: i32 = try!(serde::Deserialize::deserialize(de)); + let v: i32 = serde::Deserialize::deserialize(de)?; T::from_i32(v).ok_or_else(|| serde::de::Error::custom("Invalid enum value")) } @@ -164,7 +164,7 @@ fn deserialize_base64<'de, D>(de: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { - let v: String = try!(serde::Deserialize::deserialize(de)); + let v: String = serde::Deserialize::deserialize(de)?; base64::decode(&v).map_err(|e| serde::de::Error::custom(e.to_string())) } @@ -181,9 +181,10 @@ pub fn get_credentials String>( Some(credentials.clone()) } - (Some(username), None, _) => { - Some(Credentials::with_password(username.clone(), prompt(&username))) - } + (Some(username), None, _) => Some(Credentials::with_password( + username.clone(), + prompt(&username), + )), (None, _, Some(credentials)) => Some(credentials), diff --git a/core/src/cache.rs b/core/src/cache.rs index 908ba29..9ee0ae1 100644 --- a/core/src/cache.rs +++ b/core/src/cache.rs @@ -5,9 +5,9 @@ use std::io::Read; use std::path::Path; use std::path::PathBuf; -use authentication::Credentials; -use spotify_id::FileId; -use volume::Volume; +use crate::authentication::Credentials; +use crate::spotify_id::FileId; +use crate::volume::Volume; #[derive(Clone)] pub struct Cache { @@ -80,7 +80,7 @@ impl Cache { File::open(self.file_path(file)).ok() } - pub fn save_file(&self, file: FileId, contents: &mut Read) { + pub fn save_file(&self, file: FileId, contents: &mut dyn Read) { if self.use_audio_cache { let path = self.file_path(file); diff --git a/core/src/channel.rs b/core/src/channel.rs index a4785eb..06488b9 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -5,7 +5,7 @@ use futures::{Async, Poll, Stream}; use std::collections::HashMap; use std::time::Instant; -use util::SeqGenerator; +use crate::util::SeqGenerator; component! { ChannelManager : ChannelManagerInner { diff --git a/core/src/config.rs b/core/src/config.rs index 7c05321..38be459 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -3,7 +3,7 @@ use std::str::FromStr; use url::Url; use uuid::Uuid; -use version; +use crate::version; #[derive(Clone, Debug)] pub struct SessionConfig { diff --git a/core/src/connection/codec.rs b/core/src/connection/codec.rs index 60634fc..fa4cd9d 100644 --- a/core/src/connection/codec.rs +++ b/core/src/connection/codec.rs @@ -88,7 +88,8 @@ impl Decoder for APCodec { let mut payload = buf.split_to(size + MAC_SIZE); - self.decode_cipher.decrypt(&mut payload.get_mut(..size).unwrap()); + self.decode_cipher + .decrypt(&mut payload.get_mut(..size).unwrap()); let mac = payload.split_off(size); self.decode_cipher.check_mac(mac.as_ref())?; diff --git a/core/src/connection/handshake.rs b/core/src/connection/handshake.rs index f291f13..220ab6e 100644 --- a/core/src/connection/handshake.rs +++ b/core/src/connection/handshake.rs @@ -1,9 +1,9 @@ use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; -use hmac::{Hmac, Mac}; -use sha1::Sha1; use futures::{Async, Future, Poll}; +use hmac::{Hmac, Mac}; use protobuf::{self, Message}; use rand::thread_rng; +use sha1::Sha1; use std::io::{self, Read}; use std::marker::PhantomData; use tokio_codec::{Decoder, Framed}; @@ -11,10 +11,10 @@ use tokio_io::io::{read_exact, write_all, ReadExact, Window, WriteAll}; use tokio_io::{AsyncRead, AsyncWrite}; use super::codec::APCodec; -use diffie_hellman::DHLocalKeys; -use protocol; -use protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}; -use util; +use crate::diffie_hellman::DHLocalKeys; +use crate::protocol; +use crate::protocol::keyexchange::{APResponseMessage, ClientHello, ClientResponsePlaintext}; +use crate::util; pub struct Handshake { keys: DHLocalKeys, @@ -62,7 +62,8 @@ impl Future for Handshake { .to_owned(); let shared_secret = self.keys.shared_secret(&remote_key); - let (challenge, send_key, recv_key) = compute_keys(&shared_secret, &accumulator); + let (challenge, send_key, recv_key) = + compute_keys(&shared_secret, &accumulator); let codec = APCodec::new(&send_key, &recv_key); let write = client_response(connection, challenge); @@ -92,7 +93,10 @@ fn client_hello(connection: T, gc: Vec) -> WriteAll (Vec, Vec, Vec< let mut data = Vec::with_capacity(0x64); for i in 1..6 { - let mut mac = HmacSha1::new_varkey(&shared_secret) - .expect("HMAC can take key of any size"); + let mut mac = HmacSha1::new_varkey(&shared_secret).expect("HMAC can take key of any size"); mac.input(packets); mac.input(&[i]); data.extend_from_slice(&mac.result().code()); } - let mut mac = HmacSha1::new_varkey(&data[..0x14]) - .expect("HMAC can take key of any size");; + let mut mac = HmacSha1::new_varkey(&data[..0x14]).expect("HMAC can take key of any size"); mac.input(packets); ( diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 91b46c8..5f9b3dc 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -8,15 +8,15 @@ use futures::{Future, Sink, Stream}; use protobuf::{self, Message}; use std::io; use std::net::ToSocketAddrs; +use tokio_codec::Framed; use tokio_core::net::TcpStream; use tokio_core::reactor::Handle; -use tokio_codec::Framed; use url::Url; -use authentication::Credentials; -use version; +use crate::authentication::Credentials; +use crate::version; -use proxytunnel; +use crate::proxytunnel; pub type Transport = Framed; @@ -24,7 +24,7 @@ pub fn connect( addr: String, handle: &Handle, proxy: &Option, -) -> Box> { +) -> Box> { let (addr, connect_url) = match *proxy { Some(ref url) => { info!("Using proxy \"{}\"", url); @@ -48,23 +48,31 @@ pub fn authenticate( transport: Transport, credentials: Credentials, device_id: String, -) -> Box> { - use protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; - use protocol::keyexchange::APLoginFailed; +) -> Box> { + use crate::protocol::authentication::{APWelcome, ClientResponseEncrypted, CpuFamily, Os}; + use crate::protocol::keyexchange::APLoginFailed; let mut packet = ClientResponseEncrypted::new(); - packet.mut_login_credentials().set_username(credentials.username); - packet.mut_login_credentials().set_typ(credentials.auth_type); + packet + .mut_login_credentials() + .set_username(credentials.username); + packet + .mut_login_credentials() + .set_typ(credentials.auth_type); packet .mut_login_credentials() .set_auth_data(credentials.auth_data); - packet.mut_system_info().set_cpu_family(CpuFamily::CPU_UNKNOWN); + packet + .mut_system_info() + .set_cpu_family(CpuFamily::CPU_UNKNOWN); packet.mut_system_info().set_os(Os::OS_UNKNOWN); - packet.mut_system_info().set_system_information_string(format!( - "librespot_{}_{}", - version::short_sha(), - version::build_id() - )); + packet + .mut_system_info() + .set_system_information_string(format!( + "librespot_{}_{}", + version::short_sha(), + version::build_id() + )); packet.mut_system_info().set_device_id(device_id); packet.set_version_string(version::version_string()); @@ -77,7 +85,8 @@ pub fn authenticate( .and_then(|transport| transport.into_future().map_err(|(err, _stream)| err)) .and_then(|(packet, transport)| match packet { Some((0xac, data)) => { - let welcome_data: APWelcome = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + let welcome_data: APWelcome = + protobuf::parse_from_bytes(data.as_ref()).unwrap(); let reusable_credentials = Credentials { username: welcome_data.get_canonical_username().to_owned(), @@ -89,7 +98,8 @@ pub fn authenticate( } Some((0xad, data)) => { - let error_data: APLoginFailed = protobuf::parse_from_bytes(data.as_ref()).unwrap(); + let error_data: APLoginFailed = + protobuf::parse_from_bytes(data.as_ref()).unwrap(); panic!( "Authentication failed with reason: {:?}", error_data.get_error_code() diff --git a/core/src/diffie_hellman.rs b/core/src/diffie_hellman.rs index b8e4142..dec34a3 100644 --- a/core/src/diffie_hellman.rs +++ b/core/src/diffie_hellman.rs @@ -2,17 +2,18 @@ use num_bigint::BigUint; use num_traits::FromPrimitive; use rand::Rng; -use util; +use crate::util; lazy_static! { pub static ref DH_GENERATOR: BigUint = BigUint::from_u64(0x2).unwrap(); pub static ref DH_PRIME: BigUint = BigUint::from_bytes_be(&[ - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, - 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, - 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, - 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, - 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, - 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, + 0x34, 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, + 0xcc, 0x74, 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, + 0x34, 0x04, 0xdd, 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, + 0xf2, 0x5f, 0x14, 0x37, 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, + 0x76, 0x62, 0x5e, 0x7e, 0xc6, 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x3a, 0x36, 0x20, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ]); } @@ -39,7 +40,11 @@ impl DHLocalKeys { } pub fn shared_secret(&self, remote_key: &[u8]) -> Vec { - let shared_key = util::powm(&BigUint::from_bytes_be(remote_key), &self.private_key, &DH_PRIME); + let shared_key = util::powm( + &BigUint::from_bytes_be(remote_key), + &self.private_key, + &DH_PRIME, + ); shared_key.to_bytes_be() } } diff --git a/core/src/keymaster.rs b/core/src/keymaster.rs index 4d82ae0..f2d7b77 100644 --- a/core/src/keymaster.rs +++ b/core/src/keymaster.rs @@ -1,8 +1,8 @@ use futures::Future; use serde_json; -use mercury::MercuryError; -use session::Session; +use crate::mercury::MercuryError; +use crate::session::Session; #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "camelCase")] @@ -17,7 +17,7 @@ pub fn get_token( session: &Session, client_id: &str, scopes: &str, -) -> Box> { +) -> Box> { let url = format!( "hm://keymaster/token/authenticated?client_id={}&scope={}", client_id, scopes diff --git a/core/src/lib.rs b/core/src/lib.rs index e384d14..c65878c 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -11,29 +11,29 @@ extern crate log; #[macro_use] extern crate serde_derive; +extern crate aes; extern crate base64; extern crate byteorder; extern crate bytes; +extern crate hmac; extern crate httparse; extern crate hyper; extern crate hyper_proxy; extern crate num_bigint; extern crate num_integer; extern crate num_traits; +extern crate pbkdf2; extern crate protobuf; extern crate rand; extern crate serde; extern crate serde_json; +extern crate sha1; extern crate shannon; extern crate tokio_codec; extern crate tokio_core; extern crate tokio_io; extern crate url; extern crate uuid; -extern crate sha1; -extern crate hmac; -extern crate pbkdf2; -extern crate aes; extern crate librespot_protocol as protocol; diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 0b69d8e..89a1ff1 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -1,13 +1,13 @@ +use crate::protocol; use byteorder::{BigEndian, ByteOrder}; use bytes::Bytes; use futures::sync::{mpsc, oneshot}; use futures::{Async, Future, Poll}; use protobuf; -use protocol; use std::collections::HashMap; use std::mem; -use util::SeqGenerator; +use crate::util::SeqGenerator; mod types; pub use self::types::*; @@ -95,7 +95,8 @@ impl MercuryManager { pub fn subscribe>( &self, uri: T, - ) -> Box, Error = MercuryError>> { + ) -> Box, Error = MercuryError>> + { let uri = uri.into(); let request = self.request(MercuryRequest { method: MercuryMethod::SUB, diff --git a/core/src/mercury/types.rs b/core/src/mercury/types.rs index 23f64c4..57cedce 100644 --- a/core/src/mercury/types.rs +++ b/core/src/mercury/types.rs @@ -2,7 +2,7 @@ use byteorder::{BigEndian, WriteBytesExt}; use protobuf::Message; use std::io::Write; -use protocol; +use crate::protocol; #[derive(Debug, PartialEq, Eq)] pub enum MercuryMethod { @@ -37,7 +37,8 @@ impl ToString for MercuryMethod { MercuryMethod::SUB => "SUB", MercuryMethod::UNSUB => "UNSUB", MercuryMethod::SEND => "SEND", - }.to_owned() + } + .to_owned() } } diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index 5e07db9..2b9df5a 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -57,7 +57,9 @@ impl Future for ProxyTunnel { let mut response = httparse::Response::new(&mut headers); let status = match response.parse(&buf) { Ok(status) => status, - Err(err) => return Err(io::Error::new(io::ErrorKind::Other, err.description())), + Err(err) => { + return Err(io::Error::new(io::ErrorKind::Other, err.description())) + } }; if status.is_complete() { @@ -102,7 +104,8 @@ fn proxy_connect(connection: T, connect_url: &str) -> WriteAll, handle: Handle, - ) -> Box> { - let access_point = apresolve_or_fallback::(&handle, &config.proxy, &config.ap_port); + ) -> Box> { + let access_point = + apresolve_or_fallback::(&handle, &config.proxy, &config.ap_port); let handle_ = handle.clone(); let proxy = config.proxy.clone(); @@ -64,8 +65,9 @@ impl Session { }); let device_id = config.device_id.clone(); - let authentication = connection - .and_then(move |connection| connection::authenticate(connection, credentials, device_id)); + let authentication = connection.and_then(move |connection| { + connection::authenticate(connection, credentials, device_id) + }); let result = authentication.map(move |(transport, reusable_credentials)| { info!("Authenticated as \"{}\" !", reusable_credentials.username); @@ -97,7 +99,7 @@ impl Session { config: SessionConfig, cache: Option, username: String, - ) -> (Session, Box>) { + ) -> (Session, Box>) { let (sink, stream) = transport.split(); let (sender_tx, sender_rx) = mpsc::unbounded(); @@ -133,7 +135,11 @@ impl Session { .map(|_| ()); let receiver_task = DispatchTask(stream, session.weak()); - let task = Box::new((receiver_task, sender_task).into_future().map(|((), ())| ())); + let task = Box::new( + (receiver_task, sender_task) + .into_future() + .map(|((), ())| ()), + ); (session, task) } @@ -197,7 +203,7 @@ impl Session { 0x9 | 0xa => self.channel().dispatch(cmd, data), 0xd | 0xe => self.audio_key().dispatch(cmd, data), - 0xb2...0xb6 => self.mercury().dispatch(cmd, data), + 0xb2..=0xb6 => self.mercury().dispatch(cmd, data), _ => (), } } @@ -289,7 +295,8 @@ where session.shutdown(); return Err(From::from(e)); } - }.expect("connection closed"); + } + .expect("connection closed"); session.dispatch(cmd, data); } diff --git a/core/src/spotify_id.rs b/core/src/spotify_id.rs index e6f0cdd..1a5fcd2 100644 --- a/core/src/spotify_id.rs +++ b/core/src/spotify_id.rs @@ -17,7 +17,8 @@ pub struct SpotifyId { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct SpotifyIdError; -const BASE62_DIGITS: &'static [u8] = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const BASE62_DIGITS: &'static [u8] = + b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; const BASE16_DIGITS: &'static [u8] = b"0123456789abcdef"; impl SpotifyId { diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index c91cac9..5c1e50f 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -6,7 +6,10 @@ use std::mem; use std::ops::{Mul, Rem, Shr}; pub fn rand_vec(rng: &mut G, size: usize) -> Vec { - ::std::iter::repeat(()).map(|()| rng.gen()).take(size).collect() + ::std::iter::repeat(()) + .map(|()| rng.gen()) + .take(size) + .collect() } pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { diff --git a/core/src/volume.rs b/core/src/volume.rs index 24a3d3f..6b456d1 100644 --- a/core/src/volume.rs +++ b/core/src/volume.rs @@ -21,7 +21,9 @@ impl Volume { // write volume to file fn save_to_writer(&self, writer: &mut W) { - writer.write_all(self.volume.to_string().as_bytes()).unwrap(); + writer + .write_all(self.volume.to_string().as_bytes()) + .unwrap(); } pub(crate) fn save_to_file>(&self, path: P) { diff --git a/examples/play.rs b/examples/play.rs index c619b59..6888ebb 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -34,7 +34,9 @@ fn main() { .run(Session::connect(session_config, credentials, None, handle)) .unwrap(); - let (player, _) = Player::new(player_config, session.clone(), None, move || (backend)(None)); + let (player, _) = Player::new(player_config, session.clone(), None, move || { + (backend)(None) + }); println!("Playing..."); core.run(player.load(track, true, 0)).unwrap(); diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 677e466..6ba32f4 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -19,7 +19,7 @@ use librespot_core::mercury::MercuryError; use librespot_core::session::Session; use librespot_core::spotify_id::{FileId, SpotifyAudioType, SpotifyId}; -pub use protocol::metadata::AudioFile_Format as FileFormat; +pub use crate::protocol::metadata::AudioFile_Format as FileFormat; fn countrylist_contains(list: &str, country: &str) -> bool { list.chunks(2).any(|cc| cc == country) diff --git a/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 98e7c8f..8bdcb9d 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -64,7 +64,8 @@ impl Open for AlsaSink { } Some(device) => device, None => "default", - }.to_string(); + } + .to_string(); AlsaSink(None, name) } diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 3b118f3..54cf8b4 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -1,7 +1,7 @@ use super::{Open, Sink}; use jack::prelude::{ - client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port, ProcessHandler, - ProcessScope, + client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port, + ProcessHandler, ProcessScope, }; use std::io; use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; @@ -45,9 +45,14 @@ impl Open for JackSink { info!("Using jack sink!"); let client_name = client_name.unwrap_or("librespot".to_string()); - let (client, _status) = Client::new(&client_name[..], client_options::NO_START_SERVER).unwrap(); - let ch_r = client.register_port("out_0", AudioOutSpec::default()).unwrap(); - let ch_l = client.register_port("out_1", AudioOutSpec::default()).unwrap(); + let (client, _status) = + Client::new(&client_name[..], client_options::NO_START_SERVER).unwrap(); + let ch_r = client + .register_port("out_0", AudioOutSpec::default()) + .unwrap(); + let ch_l = client + .register_port("out_1", AudioOutSpec::default()) + .unwrap(); // buffer for samples from librespot (~10ms) let (tx, rx) = sync_channel(2 * 1024 * 4); let jack_data = JackData { diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index bdf309a..441252c 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -1,7 +1,7 @@ use std::io; pub trait Open { - fn open(Option) -> Self; + fn open(_: Option) -> Self; } pub trait Sink { @@ -10,7 +10,7 @@ pub trait Sink { fn write(&mut self, data: &[i16]) -> io::Result<()>; } -fn mk_sink(device: Option) -> Box { +fn mk_sink(device: Option) -> Box { Box::new(S::open(device)) } @@ -46,7 +46,7 @@ use self::sdl::SdlSink; mod pipe; use self::pipe::StdoutSink; -pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box)] = &[ +pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box)] = &[ #[cfg(feature = "alsa-backend")] ("alsa", mk_sink::), #[cfg(feature = "portaudio-backend")] @@ -62,7 +62,7 @@ pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box)] = ("pipe", mk_sink::), ]; -pub fn find(name: Option) -> Option) -> Box> { +pub fn find(name: Option) -> Option) -> Box> { if let Some(name) = name { BACKENDS .iter() diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 414e192..2adafe1 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -4,7 +4,7 @@ use std::io::{self, Write}; use std::mem; use std::slice; -pub struct StdoutSink(Box); +pub struct StdoutSink(Box); impl Open for StdoutSink { fn open(path: Option) -> StdoutSink { @@ -28,7 +28,10 @@ impl Sink for StdoutSink { fn write(&mut self, data: &[i16]) -> io::Result<()> { let data: &[u8] = unsafe { - slice::from_raw_parts(data.as_ptr() as *const u8, data.len() * mem::size_of::()) + slice::from_raw_parts( + data.as_ptr() as *const u8, + data.len() * mem::size_of::(), + ) }; self.0.write_all(data)?; diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 19a0bf0..31397bf 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -11,7 +11,7 @@ pub struct PortAudioSink<'a>( StreamParameters, ); -fn output_devices() -> Box> { +fn output_devices() -> Box> { let count = portaudio_rs::device::get_count().unwrap(); let devices = (0..count) .filter_map(|idx| portaudio_rs::device::get_info(idx).map(|info| (idx, info))) @@ -51,7 +51,8 @@ impl<'a> Open for PortAudioSink<'a> { } Some(device) => find_output(device), None => get_default_output_index(), - }.expect("Could not find device"); + } + .expect("Could not find device"); let info = portaudio_rs::device::get_info(device_idx); let latency = match info { @@ -81,8 +82,9 @@ impl<'a> Sink for PortAudioSink<'a> { FRAMES_PER_BUFFER_UNSPECIFIED, StreamFlags::empty(), None, - ).unwrap(), - );; + ) + .unwrap(), + ); } self.0.as_mut().unwrap().start().unwrap(); diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index 88f6280..e844b0d 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -14,7 +14,11 @@ pub struct PulseAudioSink { desc: CString, } -fn call_pulseaudio(f: F, fail_check: FailCheck, kind: io::ErrorKind) -> io::Result +fn call_pulseaudio( + f: F, + fail_check: FailCheck, + kind: io::ErrorKind, +) -> io::Result where T: Copy, F: Fn(*mut libc::c_int) -> T, diff --git a/playback/src/audio_backend/rodio.rs b/playback/src/audio_backend/rodio.rs index c4b9c92..4ce09f6 100644 --- a/playback/src/audio_backend/rodio.rs +++ b/playback/src/audio_backend/rodio.rs @@ -1,8 +1,8 @@ use super::{Open, Sink}; -extern crate rodio; extern crate cpal; -use std::{io, thread, time}; +extern crate rodio; use std::process::exit; +use std::{io, thread, time}; pub struct RodioSink { rodio_sink: rodio::Sink, @@ -14,7 +14,7 @@ fn list_formats(ref device: &rodio::Device) { Err(e) => { warn!("Error getting default rodio::Sink format: {:?}", e); return; - }, + } }; let mut output_formats = match device.supported_output_formats() { @@ -22,13 +22,16 @@ fn list_formats(ref device: &rodio::Device) { Err(e) => { warn!("Error getting supported rodio::Sink formats: {:?}", e); return; - }, + } }; if output_formats.peek().is_some() { debug!(" Available formats:"); for format in output_formats { - let s = format!("{}ch, {:?}, min {:?}, max {:?}", format.channels, format.data_type, format.min_sample_rate, format.max_sample_rate); + let s = format!( + "{}ch, {:?}, min {:?}, max {:?}", + format.channels, format.data_type, format.min_sample_rate, format.max_sample_rate + ); if format == default_fmt { debug!(" (default) {}", s); } else { @@ -79,9 +82,7 @@ impl Open for RodioSink { } let sink = rodio::Sink::new(&rodio_device); - RodioSink { - rodio_sink: sink, - } + RodioSink { rodio_sink: sink } } } diff --git a/playback/src/mixer/alsamixer.rs b/playback/src/mixer/alsamixer.rs index 5c77d47..a906c2e 100644 --- a/playback/src/mixer/alsamixer.rs +++ b/playback/src/mixer/alsamixer.rs @@ -10,13 +10,17 @@ pub struct AlsaMixer { } impl AlsaMixer { - fn map_volume(&self, set_volume: Option) -> Result<(u16), Box> { + fn map_volume(&self, set_volume: Option) -> Result<(u16), Box> { let mixer = alsa::mixer::Mixer::new(&self.config.card, false)?; let sid = alsa::mixer::SelemId::new(&*self.config.mixer, self.config.index); - let selem = mixer - .find_selem(&sid) - .expect(format!("Couldn't find simple mixer control for {}", self.config.mixer).as_str()); + let selem = mixer.find_selem(&sid).expect( + format!( + "Couldn't find simple mixer control for {}", + self.config.mixer + ) + .as_str(), + ); let (min, max) = selem.get_playback_volume_range(); let range = (max - min) as f64; @@ -72,7 +76,7 @@ impl Mixer for AlsaMixer { } } - fn get_audio_filter(&self) -> Option> { + fn get_audio_filter(&self) -> Option> { None } } diff --git a/playback/src/mixer/mod.rs b/playback/src/mixer/mod.rs index f19a866..4fc01b5 100644 --- a/playback/src/mixer/mod.rs +++ b/playback/src/mixer/mod.rs @@ -1,12 +1,12 @@ pub trait Mixer: Send { - fn open(Option) -> Self + fn open(_: Option) -> Self where Self: Sized; fn start(&self); fn stop(&self); fn set_volume(&self, volume: u16); fn volume(&self) -> u16; - fn get_audio_filter(&self) -> Option> { + fn get_audio_filter(&self) -> Option> { None } } @@ -28,10 +28,11 @@ pub struct MixerConfig { } impl Default for MixerConfig { - fn default() -> MixerConfig { MixerConfig { - card: String::from("default"), - mixer: String::from("PCM"), - index: 0, + fn default() -> MixerConfig { + MixerConfig { + card: String::from("default"), + mixer: String::from("PCM"), + index: 0, } } } @@ -39,11 +40,11 @@ impl Default for MixerConfig { pub mod softmixer; use self::softmixer::SoftMixer; -fn mk_sink(device: Option) -> Box { +fn mk_sink(device: Option) -> Box { Box::new(M::open(device)) } -pub fn find>(name: Option) -> Option) -> Box> { +pub fn find>(name: Option) -> Option) -> Box> { match name.as_ref().map(AsRef::as_ref) { None | Some("softvol") => Some(mk_sink::), #[cfg(feature = "alsa-backend")] diff --git a/playback/src/mixer/softmixer.rs b/playback/src/mixer/softmixer.rs index 4b96978..28e1cf5 100644 --- a/playback/src/mixer/softmixer.rs +++ b/playback/src/mixer/softmixer.rs @@ -23,7 +23,7 @@ impl Mixer for SoftMixer { fn set_volume(&self, volume: u16) { self.volume.store(volume as usize, Ordering::Relaxed); } - fn get_audio_filter(&self) -> Option> { + fn get_audio_filter(&self) -> Option> { Some(Box::new(SoftVolumeApplier { volume: self.volume.clone(), })) diff --git a/playback/src/player.rs b/playback/src/player.rs index a54a577..eab8f43 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -11,7 +11,7 @@ use std::sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError}; use std::thread; use std::time::Duration; -use config::{Bitrate, PlayerConfig}; +use crate::config::{Bitrate, PlayerConfig}; use librespot_core::session::Session; use librespot_core::spotify_id::SpotifyId; @@ -36,9 +36,9 @@ struct PlayerInternal { commands: std::sync::mpsc::Receiver, state: PlayerState, - sink: Box, + sink: Box, sink_running: bool, - audio_filter: Option>, + audio_filter: Option>, event_sender: futures::sync::mpsc::UnboundedSender, } @@ -98,8 +98,10 @@ impl NormalisationData { } fn get_factor(config: &PlayerConfig, data: NormalisationData) -> f32 { - let mut normalisation_factor = - f32::powf(10.0, (data.track_gain_db + config.normalisation_pregain) / 20.0); + let mut normalisation_factor = f32::powf( + 10.0, + (data.track_gain_db + config.normalisation_pregain) / 20.0, + ); if normalisation_factor * data.track_peak > 1.0 { warn!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid."); @@ -117,11 +119,11 @@ impl Player { pub fn new( config: PlayerConfig, session: Session, - audio_filter: Option>, + audio_filter: Option>, sink_builder: F, ) -> (Player, PlayerEventChannel) where - F: FnOnce() -> Box + Send + 'static, + F: FnOnce() -> Box + Send + 'static, { let (cmd_tx, cmd_rx) = std::sync::mpsc::channel(); let (event_sender, event_receiver) = futures::sync::mpsc::unbounded(); @@ -238,7 +240,12 @@ impl PlayerState { use self::PlayerState::*; match *self { Stopped | EndOfTrack { .. } => None, - Paused { ref mut decoder, .. } | Playing { ref mut decoder, .. } => Some(decoder), + Paused { + ref mut decoder, .. + } + | Playing { + ref mut decoder, .. + } => Some(decoder), Invalid => panic!("invalid state"), } } @@ -689,7 +696,9 @@ impl PlayerInternal { let mut decrypted_file = AudioDecrypt::new(key, encrypted_file); let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) { - Ok(normalisation_data) => NormalisationData::get_factor(&self.config, normalisation_data), + Ok(normalisation_data) => { + NormalisationData::get_factor(&self.config, normalisation_data) + } Err(_) => { warn!("Unable to extract normalisation data, using default value."); 1.0 as f32 @@ -768,7 +777,7 @@ impl Seek for Subfile { x => x, }; - let newpos = try!(self.stream.seek(pos)); + let newpos = self.stream.seek(pos)?; if newpos > self.offset { Ok(newpos - self.offset) } else { diff --git a/protocol/build.rs b/protocol/build.rs index c7f19ac..3b632f6 100644 --- a/protocol/build.rs +++ b/protocol/build.rs @@ -1,14 +1,16 @@ extern crate protobuf_codegen; // Does the business extern crate protobuf_codegen_pure; // Helper function -use std::path::Path; use std::fs::{read_to_string, write}; +use std::path::Path; -use protobuf_codegen_pure::Customize; use protobuf_codegen_pure::parse_and_typecheck; +use protobuf_codegen_pure::Customize; fn main() { - let customizations = Customize { ..Default::default() }; + let customizations = Customize { + ..Default::default() + }; let lib_str = read_to_string("src/lib.rs").unwrap(); @@ -44,11 +46,7 @@ fn main() { let p = parse_and_typecheck(&["proto"], &[src]).expect("protoc"); // But generate them with the protobuf-codegen crate directly. // Then we can keep the result in-memory. - let result = protobuf_codegen::gen( - &p.file_descriptors, - &p.relative_paths, - &customizations, - ); + let result = protobuf_codegen::gen(&p.file_descriptors, &p.relative_paths, &customizations); // Protoc result as a byte array. let new = &result.first().unwrap().content; // Convert to utf8 to compare with existing. diff --git a/rustfmt.toml b/rustfmt.toml index 627f7c4..25c1fc1 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,3 @@ -max_width = 105 +# max_width = 105 reorder_imports = true -reorder_imports_in_group = true reorder_modules = true diff --git a/src/main.rs b/src/main.rs index db19726..52452d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -184,7 +184,13 @@ fn setup(args: &[String]) -> Setup { let matches = match opts.parse(&args[1..]) { Ok(m) => m, Err(f) => { - writeln!(stderr(), "error: {}\n{}", f.to_string(), usage(&args[0], &opts)).unwrap(); + writeln!( + stderr(), + "error: {}\n{}", + f.to_string(), + usage(&args[0], &opts) + ) + .unwrap(); exit(1); } }; @@ -218,7 +224,9 @@ fn setup(args: &[String]) -> Setup { let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer"); let mixer_config = MixerConfig { - card: matches.opt_str("mixer-card").unwrap_or(String::from("default")), + card: matches + .opt_str("mixer-card") + .unwrap_or(String::from("default")), mixer: matches.opt_str("mixer-name").unwrap_or(String::from("PCM")), index: matches .opt_str("mixer-index") @@ -400,7 +408,8 @@ impl Main { let config = task.connect_config.clone(); let device_id = task.session_config.device_id.clone(); - task.discovery = Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap()); + task.discovery = + Some(discovery(&handle, config, device_id, setup.zeroconf_port).unwrap()); } if let Some(credentials) = setup.credentials { @@ -433,7 +442,9 @@ impl Future for Main { loop { let mut progress = false; - if let Some(Async::Ready(Some(creds))) = self.discovery.as_mut().map(|d| d.poll().unwrap()) { + if let Some(Async::Ready(Some(creds))) = + self.discovery.as_mut().map(|d| d.poll().unwrap()) + { if let Some(ref spirc) = self.spirc { spirc.shutdown(); } diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index 6da600b..a4ef4da 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -1,9 +1,9 @@ use log::info; use librespot::playback::player::PlayerEvent; -use tokio_process::{Child, CommandExt}; use std::collections::HashMap; use std::io; use std::process::Command; +use tokio_process::{Child, CommandExt}; fn run_program(program: &str, env_vars: HashMap<&str, String>) -> io::Result { let mut v: Vec<&str> = program.split_whitespace().collect(); From 38d82f2dc26d15c17f97585f68ce3575bac5c503 Mon Sep 17 00:00:00 2001 From: marcelbuesing Date: Fri, 17 Jan 2020 18:11:07 +0100 Subject: [PATCH 07/29] Rerun rustfmt on full codebase --- audio/src/fetch.rs | 56 ++++++++++++++++++++++++++----------- audio/src/range_set.rs | 17 ++++++++--- connect/src/spirc.rs | 19 ++++++++++--- core/src/proxytunnel.rs | 2 +- examples/playlist_tracks.rs | 11 ++++---- metadata/src/lib.rs | 9 ++++-- playback/src/player.rs | 18 +++++++++--- protocol/build.rs | 7 ++--- src/main.rs | 2 +- src/player_event_handler.rs | 2 +- 10 files changed, 99 insertions(+), 44 deletions(-) diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index a7f8090..ef68ed8 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -446,7 +446,7 @@ impl AudioFile { channel_tx: None, stream_shared: None, file_size: file.metadata().unwrap().len() as usize, - } + }; } } } @@ -514,7 +514,10 @@ impl AudioFileFetchDataReceiver { request_length: usize, request_sent_time: Instant, ) -> AudioFileFetchDataReceiver { - let measure_ping_time = shared.number_of_open_requests.load(atomic::Ordering::SeqCst) == 0; + let measure_ping_time = shared + .number_of_open_requests + .load(atomic::Ordering::SeqCst) + == 0; shared .number_of_open_requests @@ -562,7 +565,8 @@ impl Future for AudioFileFetchDataReceiver { if let Some(request_sent_time) = self.request_sent_time { let duration = Instant::now() - request_sent_time; let duration_ms: u64; - if 0.001 * (duration.as_millis() as f64) > MAXIMUM_ASSUMED_PING_TIME_SECONDS + if 0.001 * (duration.as_millis() as f64) + > MAXIMUM_ASSUMED_PING_TIME_SECONDS { duration_ms = (MAXIMUM_ASSUMED_PING_TIME_SECONDS * 1000.0) as u64; } else { @@ -714,8 +718,13 @@ impl AudioFileFetch { ranges_to_request.subtract_range_set(&download_status.requested); for range in ranges_to_request.iter() { - let (_headers, data) = - request_range(&self.session, self.shared.file_id, range.start, range.length).split(); + let (_headers, data) = request_range( + &self.session, + self.shared.file_id, + range.start, + range.length, + ) + .split(); download_status.requested.add_range(range); @@ -749,7 +758,10 @@ impl AudioFileFetch { // download data from after the current read position first let mut tail_end = RangeSet::new(); let read_position = self.shared.read_position.load(atomic::Ordering::Relaxed); - tail_end.add_range(&Range::new(read_position, self.shared.file_size - read_position)); + tail_end.add_range(&Range::new( + read_position, + self.shared.file_size - read_position, + )); let tail_end = tail_end.intersection(&missing_data); if !tail_end.is_empty() { @@ -794,8 +806,9 @@ impl AudioFileFetch { let ping_time_ms: usize = match self.network_response_times_ms.len() { 1 => self.network_response_times_ms[0] as usize, 2 => { - ((self.network_response_times_ms[0] + self.network_response_times_ms[1]) / 2) - as usize + ((self.network_response_times_ms[0] + + self.network_response_times_ms[1]) + / 2) as usize } 3 => { let mut times = self.network_response_times_ms.clone(); @@ -863,10 +876,12 @@ impl AudioFileFetch { self.download_range(request.start, request.length); } Ok(Async::Ready(Some(StreamLoaderCommand::RandomAccessMode()))) => { - *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::RandomAccess(); + *(self.shared.download_strategy.lock().unwrap()) = + DownloadStrategy::RandomAccess(); } Ok(Async::Ready(Some(StreamLoaderCommand::StreamMode()))) => { - *(self.shared.download_strategy.lock().unwrap()) = DownloadStrategy::Streaming(); + *(self.shared.download_strategy.lock().unwrap()) = + DownloadStrategy::Streaming(); } Ok(Async::Ready(Some(StreamLoaderCommand::Close()))) => { return Ok(Async::Ready(())); @@ -908,15 +923,20 @@ impl Future for AudioFileFetch { } if let DownloadStrategy::Streaming() = self.get_download_strategy() { - let number_of_open_requests = - self.shared.number_of_open_requests.load(atomic::Ordering::SeqCst); + let number_of_open_requests = self + .shared + .number_of_open_requests + .load(atomic::Ordering::SeqCst); let max_requests_to_send = MAX_PREFETCH_REQUESTS - min(MAX_PREFETCH_REQUESTS, number_of_open_requests); if max_requests_to_send > 0 { let bytes_pending: usize = { let download_status = self.shared.download_status.lock().unwrap(); - download_status.requested.minus(&download_status.downloaded).len() + download_status + .requested + .minus(&download_status.downloaded) + .len() }; let ping_time_seconds = @@ -924,9 +944,11 @@ impl Future for AudioFileFetch { let download_rate = self.session.channel().get_download_rate_estimate(); let desired_pending_bytes = max( - (PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * self.shared.stream_data_rate as f64) + (PREFETCH_THRESHOLD_FACTOR + * ping_time_seconds + * self.shared.stream_data_rate as f64) as usize, + (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) as usize, - (FAST_PREFETCH_THRESHOLD_FACTOR * ping_time_seconds * download_rate as f64) as usize, ); if bytes_pending < desired_pending_bytes { @@ -1003,7 +1025,9 @@ impl Read for AudioFileStreaming { .unwrap() .0; } - let available_length = download_status.downloaded.contained_length_from_value(offset); + let available_length = download_status + .downloaded + .contained_length_from_value(offset); assert!(available_length > 0); drop(download_status); diff --git a/audio/src/range_set.rs b/audio/src/range_set.rs index 448c097..4495538 100644 --- a/audio/src/range_set.rs +++ b/audio/src/range_set.rs @@ -113,7 +113,8 @@ impl RangeSet { // the new range starts after anything we already passed and ends before the next range starts (they don't touch) -> insert it. self.ranges.insert(index, range.clone()); return; - } else if range.start <= self.ranges[index].end() && self.ranges[index].start <= range.end() + } else if range.start <= self.ranges[index].end() + && self.ranges[index].start <= range.end() { // the new range overlaps (or touches) the first range. They are to be merged. // In addition we might have to merge further ranges in as well. @@ -161,7 +162,9 @@ impl RangeSet { if range.end() <= self.ranges[index].start { // the remaining ranges are past the one to subtract. -> we're done. return; - } else if range.start <= self.ranges[index].start && self.ranges[index].start < range.end() { + } else if range.start <= self.ranges[index].start + && self.ranges[index].start < range.end() + { // the range to subtract started before the current range and reaches into the current range // -> we have to remove the beginning of the range or the entire range and do the same for following ranges. @@ -223,8 +226,14 @@ impl RangeSet { other_index += 1; } else { // the two intervals overlap. Add the union and advance the index of the one that ends first. - let new_start = max(self.ranges[self_index].start, other.ranges[other_index].start); - let new_end = min(self.ranges[self_index].end(), other.ranges[other_index].end()); + let new_start = max( + self.ranges[self_index].start, + other.ranges[other_index].start, + ); + let new_end = min( + self.ranges[self_index].end(), + other.ranges[other_index].end(), + ); assert!(new_start <= new_end); result.add_range(&Range::new(new_start, new_end - new_start)); if self.ranges[self_index].end() <= other.ranges[other_index].end() { diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 29e6b1d..ea24f6b 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -796,12 +796,19 @@ impl SpircTask { self.resolve_uri(&radio_uri) } - fn resolve_autoplay_uri(&self, uri: &str) -> Box> { + fn resolve_autoplay_uri( + &self, + uri: &str, + ) -> Box> { let query_uri = format!("hm://autoplay-enabled/query?uri={}", uri); let request = self.session.mercury().get(query_uri); Box::new(request.and_then(move |response| { if response.status_code == 200 { - let data = response.payload.first().expect("Empty autoplay uri").to_vec(); + let data = response + .payload + .first() + .expect("Empty autoplay uri") + .to_vec(); let autoplay_uri = String::from_utf8(data).unwrap(); Ok(autoplay_uri) } else { @@ -811,7 +818,10 @@ impl SpircTask { })) } - fn resolve_uri(&self, uri: &str) -> Box> { + fn resolve_uri( + &self, + uri: &str, + ) -> Box> { let request = self.session.mercury().get(uri); Box::new(request.and_then(move |response| { @@ -900,7 +910,8 @@ impl SpircTask { let track = { let mut track_ref = self.state.get_track()[index as usize].clone(); let mut track_id = self.get_spotify_id_for_track(&track_ref); - while track_id.is_err() || track_id.unwrap().audio_type == SpotifyAudioType::NonPlayable { + while track_id.is_err() || track_id.unwrap().audio_type == SpotifyAudioType::NonPlayable + { warn!( "Skipping track <{:?}> at position [{}] of {}", track_ref.get_uri(), diff --git a/core/src/proxytunnel.rs b/core/src/proxytunnel.rs index 2b9df5a..ea84bc8 100644 --- a/core/src/proxytunnel.rs +++ b/core/src/proxytunnel.rs @@ -58,7 +58,7 @@ impl Future for ProxyTunnel { let status = match response.parse(&buf) { Ok(status) => status, Err(err) => { - return Err(io::Error::new(io::ErrorKind::Other, err.description())) + return Err(io::Error::new(io::ErrorKind::Other, err.description())); } }; diff --git a/examples/playlist_tracks.rs b/examples/playlist_tracks.rs index 3e4202d..fc288d1 100644 --- a/examples/playlist_tracks.rs +++ b/examples/playlist_tracks.rs @@ -1,4 +1,3 @@ - use env_logger; use std::env; use tokio_core::reactor::Core; @@ -7,7 +6,7 @@ use librespot::core::authentication::Credentials; use librespot::core::config::SessionConfig; use librespot::core::session::Session; use librespot::core::spotify_id::SpotifyId; -use librespot::metadata::{Metadata, Track, Playlist}; +use librespot::metadata::{Metadata, Playlist, Track}; fn main() { env_logger::init(); @@ -26,16 +25,16 @@ fn main() { let uri_split = args[3].split(":"); let uri_parts: Vec<&str> = uri_split.collect(); - println!("{}, {}, {}",uri_parts[0], uri_parts[1], uri_parts[2]); - + println!("{}, {}, {}", uri_parts[0], uri_parts[1], uri_parts[2]); + let plist_uri = SpotifyId::from_base62(uri_parts[2]).unwrap(); - + let session = core .run(Session::connect(session_config, credentials, None, handle)) .unwrap(); let plist = core.run(Playlist::get(&session, plist_uri)).unwrap(); - println!("{:?}",plist); + println!("{:?}", plist); for track_id in plist.tracks { let plist_track = core.run(Track::get(&session, track_id)).unwrap(); println!("track: {} ", plist_track.name); diff --git a/metadata/src/lib.rs b/metadata/src/lib.rs index 6ba32f4..d4bd797 100644 --- a/metadata/src/lib.rs +++ b/metadata/src/lib.rs @@ -301,7 +301,6 @@ impl Metadata for Playlist { } fn parse(msg: &Self::Message, _: &Session) -> Self { - let tracks = msg .get_contents() .get_items() @@ -312,9 +311,13 @@ impl Metadata for Playlist { SpotifyId::from_base62(uri_parts[2]).unwrap() }) .collect::>(); - + if tracks.len() != msg.get_length() as usize { - warn!("Got {} tracks, but the playlist should contain {} tracks.", tracks.len(), msg.get_length()); + warn!( + "Got {} tracks, but the playlist should contain {} tracks.", + tracks.len(), + msg.get_length() + ); } Playlist { diff --git a/playback/src/player.rs b/playback/src/player.rs index eab8f43..ba61471 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -525,7 +525,10 @@ impl PlayerInternal { if let Some(stream_loader_controller) = self.state.stream_loader_controller() { stream_loader_controller.set_stream_mode(); } - if let PlayerState::Playing { bytes_per_second, .. } = self.state { + if let PlayerState::Playing { + bytes_per_second, .. + } = self.state + { if let Some(stream_loader_controller) = self.state.stream_loader_controller() { // Request our read ahead range let request_data_length = max( @@ -599,7 +602,10 @@ impl PlayerInternal { .iter() .map(|alt_id| AudioItem::get_audio_item(&self.session, *alt_id)); let alternatives = future::join_all(alternatives).wait().unwrap(); - alternatives.into_iter().find(|alt| alt.available).map(Cow::Owned) + alternatives + .into_iter() + .find(|alt| alt.available) + .map(Cow::Owned) } else { None } @@ -677,8 +683,12 @@ impl PlayerInternal { let play_from_beginning = position == 0; let key = self.session.audio_key().request(spotify_id, file_id); - let encrypted_file = - AudioFile::open(&self.session, file_id, bytes_per_second, play_from_beginning); + let encrypted_file = AudioFile::open( + &self.session, + file_id, + bytes_per_second, + play_from_beginning, + ); let encrypted_file = encrypted_file.wait().unwrap(); diff --git a/protocol/build.rs b/protocol/build.rs index 3b632f6..3ea25e7 100644 --- a/protocol/build.rs +++ b/protocol/build.rs @@ -23,10 +23,9 @@ fn main() { let name; if line.starts_with("pub mod ") { - name = &line[8..len-1]; // Remove keywords and semi-colon - } - else { - name = &line[4..len-1]; // Remove keywords and semi-colon + name = &line[8..len - 1]; // Remove keywords and semi-colon + } else { + name = &line[4..len - 1]; // Remove keywords and semi-colon } // Build the paths to relevant files. diff --git a/src/main.rs b/src/main.rs index 52452d4..f94a2ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use futures::sync::mpsc::UnboundedReceiver; use futures::{Async, Future, Poll, Stream}; -use sha1::{Digest, Sha1}; use log::{error, info, trace, warn}; +use sha1::{Digest, Sha1}; use std::env; use std::io::{self, stderr, Write}; use std::mem; diff --git a/src/player_event_handler.rs b/src/player_event_handler.rs index a4ef4da..03bae14 100644 --- a/src/player_event_handler.rs +++ b/src/player_event_handler.rs @@ -1,5 +1,5 @@ -use log::info; use librespot::playback::player::PlayerEvent; +use log::info; use std::collections::HashMap; use std::io; use std::process::Command; From c9147d31ab47d8daf989d2450465d7f8ee4ef965 Mon Sep 17 00:00:00 2001 From: marcelbuesing Date: Fri, 17 Jan 2020 19:09:10 +0100 Subject: [PATCH 08/29] Turn child crates into rust 2018 edition --- audio/Cargo.toml | 1 + audio/src/fetch.rs | 2 +- connect/Cargo.toml | 1 + core/Cargo.toml | 1 + metadata/Cargo.toml | 1 + playback/Cargo.toml | 1 + playback/src/player.rs | 12 ++++++------ protocol/Cargo.toml | 1 + 8 files changed, 13 insertions(+), 7 deletions(-) diff --git a/audio/Cargo.toml b/audio/Cargo.toml index 6c485e2..a0f9531 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Paul Lietar "] description="The audio fetching and processing logic for librespot" license="MIT" +edition = "2018" [dependencies.librespot-core] path = "../core" diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index ef68ed8..1ab6ce7 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -1,9 +1,9 @@ +use crate::range_set::{Range, RangeSet}; use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use bytes::Bytes; use futures::sync::{mpsc, oneshot}; use futures::Stream; use futures::{Async, Future, Poll}; -use range_set::{Range, RangeSet}; use std::cmp::{max, min}; use std::fs; use std::io::{self, Read, Seek, SeekFrom, Write}; diff --git a/connect/Cargo.toml b/connect/Cargo.toml index 270f605..c468d2d 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Paul Lietar "] description="The discovery and Spotify Connect logic for librespot" license="MIT" +edition = "2018" [dependencies.librespot-core] path = "../core" diff --git a/core/Cargo.toml b/core/Cargo.toml index 908b907..9e9ccce 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Paul Lietar "] build = "build.rs" description="The core functionality provided by librespot" license="MIT" +edition = "2018" [dependencies.librespot-protocol] path = "../protocol" diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 7502819..02c0f2b 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Paul Lietar "] description="The metadata logic for librespot" license="MIT" +edition = "2018" [dependencies] byteorder = "1.3" diff --git a/playback/Cargo.toml b/playback/Cargo.toml index d7d1c5b..f414d5e 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" authors = ["Sasha Hilton "] description="The audio playback logic for librespot" license="MIT" +edition = "2018" [dependencies.librespot-audio] path = "../audio" diff --git a/playback/src/player.rs b/playback/src/player.rs index ba61471..4d1ae4f 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -15,15 +15,15 @@ use crate::config::{Bitrate, PlayerConfig}; use librespot_core::session::Session; use librespot_core::spotify_id::SpotifyId; -use audio::{AudioDecrypt, AudioFile, StreamLoaderController}; -use audio::{VorbisDecoder, VorbisPacket}; -use audio::{ +use crate::audio::{AudioDecrypt, AudioFile, StreamLoaderController}; +use crate::audio::{VorbisDecoder, VorbisPacket}; +use crate::audio::{ READ_AHEAD_BEFORE_PLAYBACK_ROUNDTRIPS, READ_AHEAD_BEFORE_PLAYBACK_SECONDS, READ_AHEAD_DURING_PLAYBACK_ROUNDTRIPS, READ_AHEAD_DURING_PLAYBACK_SECONDS, }; -use audio_backend::Sink; -use metadata::{AudioItem, FileFormat}; -use mixer::AudioFilter; +use crate::audio_backend::Sink; +use crate::metadata::{AudioItem, FileFormat}; +use crate::mixer::AudioFilter; pub struct Player { commands: Option>, diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index 4c9a332..792f5b5 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -5,6 +5,7 @@ authors = ["Paul Liétar "] build = "build.rs" description="The protobuf logic for communicating with Spotify servers" license="MIT" +edition = "2018" [dependencies] protobuf = "2.8.1" From 362106df622126f8ee2af3038a7eeb45b424229f Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Wed, 22 Jan 2020 16:58:21 +1100 Subject: [PATCH 09/29] Fix error handling for closed channel. fixes #417 --- core/src/channel.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/channel.rs b/core/src/channel.rs index a4785eb..d86b252 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -92,7 +92,8 @@ impl ChannelManager { impl Channel { fn recv_packet(&mut self) -> Poll { let (cmd, packet) = match self.receiver.poll() { - Ok(Async::Ready(t)) => t.expect("channel closed"), + Ok(Async::Ready(Some(t))) => t, + Ok(Async::Ready(None)) => return Err(ChannelError), // The channel has been closed. Ok(Async::NotReady) => return Ok(Async::NotReady), Err(()) => unreachable!(), }; From b6c676ad600b461d96ac999a87fc805f10985177 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Wed, 22 Jan 2020 21:55:45 +1100 Subject: [PATCH 10/29] Prevent librespot from panicking if server connection is lost. --- core/src/session.rs | 9 +++++++-- src/main.rs | 3 ++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/session.rs b/core/src/session.rs index e15c6b8..7695eb5 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -283,13 +283,18 @@ where loop { let (cmd, data) = match self.0.poll() { - Ok(Async::Ready(t)) => t, + Ok(Async::Ready(Some(t))) => t, + Ok(Async::Ready(None)) => { + warn!("Connection to server closed."); + session.shutdown(); + return Ok(Async::Ready(())); + }, Ok(Async::NotReady) => return Ok(Async::NotReady), Err(e) => { session.shutdown(); return Err(From::from(e)); } - }.expect("connection closed"); + }; session.dispatch(cmd, data); } diff --git a/src/main.rs b/src/main.rs index e193257..fcced12 100644 --- a/src/main.rs +++ b/src/main.rs @@ -500,7 +500,8 @@ impl Future for Main { if self.shutdown { return Ok(Async::Ready(())); } else { - panic!("Spirc shut down unexpectedly"); + warn!("Spirc shut down unexpectedly"); + self.spirc_task = None; } } } From 3fe384958833e983ac58da25094ffdc4e0b8d27c Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 01:14:43 +1100 Subject: [PATCH 11/29] Enable Mercury to be shut down and all pending requests being cancelled. --- core/src/mercury/mod.rs | 42 ++++++++++++++++++++++++++++------------- core/src/session.rs | 1 + 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 0b69d8e..2fb6b87 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -20,6 +20,7 @@ component! { sequence: SeqGenerator = SeqGenerator::new(0), pending: HashMap, MercuryPending> = HashMap::new(), subscriptions: Vec<(String, mpsc::UnboundedSender)> = Vec::new(), + is_shutdown: bool = false, } } @@ -61,7 +62,11 @@ impl MercuryManager { }; let seq = self.next_seq(); - self.lock(|inner| inner.pending.insert(seq.clone(), pending)); + self.lock(|inner| { + if !inner.is_shutdown { + inner.pending.insert(seq.clone(), pending); + } + }); let cmd = req.method.command(); let data = req.encode(&seq); @@ -109,21 +114,23 @@ impl MercuryManager { let (tx, rx) = mpsc::unbounded(); manager.lock(move |inner| { - debug!("subscribed uri={} count={}", uri, response.payload.len()); - if response.payload.len() > 0 { - // Old subscription protocol, watch the provided list of URIs - for sub in response.payload { - let mut sub: protocol::pubsub::Subscription = - protobuf::parse_from_bytes(&sub).unwrap(); - let sub_uri = sub.take_uri(); + if !inner.is_shutdown { + debug!("subscribed uri={} count={}", uri, response.payload.len()); + if response.payload.len() > 0 { + // Old subscription protocol, watch the provided list of URIs + for sub in response.payload { + let mut sub: protocol::pubsub::Subscription = + protobuf::parse_from_bytes(&sub).unwrap(); + let sub_uri = sub.take_uri(); - debug!("subscribed sub_uri={}", sub_uri); + debug!("subscribed sub_uri={}", sub_uri); - inner.subscriptions.push((sub_uri, tx.clone())); + inner.subscriptions.push((sub_uri, tx.clone())); + } + } else { + // New subscription protocol, watch the requested URI + inner.subscriptions.push((uri, tx)); } - } else { - // New subscription protocol, watch the requested URI - inner.subscriptions.push((uri, tx)); } }); @@ -222,4 +229,13 @@ impl MercuryManager { } } } + + pub(crate) fn shutdown(&self) { + self.lock(|inner| { + inner.is_shutdown = true; + // destroy the sending halves of the channels to signal everyone who is waiting for something. + inner.pending.clear(); + inner.subscriptions.clear(); + }); + } } diff --git a/core/src/session.rs b/core/src/session.rs index 7695eb5..e661e4f 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -237,6 +237,7 @@ impl Session { pub fn shutdown(&self) { debug!("Invalidating session[{}]", self.0.session_id); self.0.data.write().unwrap().invalid = true; + self.mercury().shutdown(); } pub fn is_invalid(&self) -> bool { From 04b52d7878d3ad52233007aafd19ce5dc86ff056 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 01:15:30 +1100 Subject: [PATCH 12/29] Have player handle Mercury errors while loading tracks. --- playback/src/player.rs | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/playback/src/player.rs b/playback/src/player.rs index a54a577..3bcdef4 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -623,9 +623,15 @@ impl PlayerInternal { spotify_id: SpotifyId, position: i64, ) -> Option<(Decoder, f32, StreamLoaderController, usize)> { - let audio = AudioItem::get_audio_item(&self.session, spotify_id) - .wait() - .unwrap(); + + let audio = match AudioItem::get_audio_item(&self.session, spotify_id).wait() { + Ok(audio) => audio, + Err(_) => { + error!("Unable to load audio item."); + return None + }, + }; + info!("Loading <{}> with Spotify URI <{}>", audio.name, audio.uri); let audio = match self.find_available_alternative(&audio) { @@ -673,7 +679,13 @@ impl PlayerInternal { let encrypted_file = AudioFile::open(&self.session, file_id, bytes_per_second, play_from_beginning); - let encrypted_file = encrypted_file.wait().unwrap(); + let encrypted_file = match encrypted_file.wait() { + Ok(encrypted_file) => encrypted_file, + Err(_) => { + error!("Unable to load encrypted file."); + return None; + } + }; let mut stream_loader_controller = encrypted_file.get_stream_loader_controller(); @@ -685,7 +697,14 @@ impl PlayerInternal { stream_loader_controller.set_random_access_mode(); } - let key = key.wait().unwrap(); + let key = match key.wait() { + Ok(key) => key, + Err(_) => { + error!("Unable to load decryption key"); + return None; + } + }; + let mut decrypted_file = AudioDecrypt::new(key, encrypted_file); let normalisation_factor = match NormalisationData::parse_from_file(&mut decrypted_file) { From ea1e0925dcf0717641794f3f4b5d999389723329 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 01:23:34 +1100 Subject: [PATCH 13/29] Enable proper shutdown of the channels. --- core/src/channel.rs | 13 ++++++++++++- core/src/session.rs | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/channel.rs b/core/src/channel.rs index d86b252..98ab62e 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -14,6 +14,7 @@ component! { download_rate_estimate: usize = 0, download_measurement_start: Option = None, download_measurement_bytes: usize = 0, + is_shutdown: bool = false, } } @@ -46,7 +47,9 @@ impl ChannelManager { let seq = self.lock(|inner| { let seq = inner.sequence.get(); - inner.channels.insert(seq, tx); + if !inner.is_shutdown { + inner.channels.insert(seq, tx); + } seq }); @@ -87,6 +90,14 @@ impl ChannelManager { pub fn get_download_rate_estimate(&self) -> usize { return self.lock(|inner| inner.download_rate_estimate); } + + pub(crate) fn shutdown(&self) { + self.lock(|inner| { + inner.is_shutdown = true; + // destroy the sending halves of the channels to signal everyone who is waiting for something. + inner.channels.clear(); + }); + } } impl Channel { diff --git a/core/src/session.rs b/core/src/session.rs index e661e4f..3f30e28 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -238,6 +238,7 @@ impl Session { debug!("Invalidating session[{}]", self.0.session_id); self.0.data.write().unwrap().invalid = true; self.mercury().shutdown(); + self.channel().shutdown(); } pub fn is_invalid(&self) -> bool { From 719943aec9f5e7f66563e288c970207cb154d919 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 01:24:59 +1100 Subject: [PATCH 14/29] Don't panic if spirc terminates prematurely. Instead attempt to reconnect and wait for new client connections. --- src/main.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main.rs b/src/main.rs index fcced12..b965904 100644 --- a/src/main.rs +++ b/src/main.rs @@ -381,6 +381,7 @@ struct Main { connect: Box>, shutdown: bool, + last_credentials: Option, player_event_channel: Option>, player_event_program: Option, @@ -404,6 +405,7 @@ impl Main { spirc: None, spirc_task: None, shutdown: false, + last_credentials: None, signal: Box::new(tokio_signal::ctrl_c().flatten_stream()), player_event_channel: None, @@ -425,6 +427,7 @@ impl Main { } fn credentials(&mut self, credentials: Credentials) { + self.last_credentials = Some(credentials.clone()); let config = self.session_config.clone(); let handle = self.handle.clone(); @@ -502,6 +505,10 @@ impl Future for Main { } else { warn!("Spirc shut down unexpectedly"); self.spirc_task = None; + if let Some(credentials) = self.last_credentials.clone() { + self.credentials(credentials); + progress = true; + } } } } From 113bb92c9d3c6ec18be15a14ae1faf26413adca5 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 19:05:10 +1100 Subject: [PATCH 15/29] Prevent spirc from crashing when the queue is empty. --- connect/src/spirc.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 9594082..3d52891 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -534,6 +534,8 @@ impl SpircTask { } else { info!("No more tracks left in queue"); self.state.set_status(PlayStatus::kPlayStatusStop); + self.player.stop(); + self.mixer.stop(); } self.notify(None); @@ -670,11 +672,12 @@ impl SpircTask { // Removes current track if it is queued // Returns the index of the next track let current_index = self.state.get_playing_track_index() as usize; - if self.state.get_track()[current_index].get_queued() { + if (current_index < self.state.get_track().len()) && self.state.get_track()[current_index].get_queued() { self.state.mut_track().remove(current_index); - return current_index; + current_index + } else { + current_index + 1 } - current_index + 1 } fn handle_next(&mut self) { @@ -706,12 +709,23 @@ impl SpircTask { new_index = 0; // Loop around back to start continue_playing = self.state.get_repeat(); } - self.state.set_playing_track_index(new_index); - self.state.set_position_ms(0); - let now = self.now_ms(); - self.state.set_position_measured_at(now as u64); - self.load_track(continue_playing); + if tracks_len > 0 { + self.state.set_playing_track_index(new_index); + self.state.set_position_ms(0); + let now = self.now_ms(); + self.state.set_position_measured_at(now as u64); + + self.load_track(continue_playing); + + } else { + info!("Not playing next track because there are no more tracks left in queue."); + self.state.set_playing_track_index(0); + self.state.set_status(PlayStatus::kPlayStatusStop); + self.player.stop(); + self.mixer.stop(); + } + } fn handle_prev(&mut self) { From 17821b26aaa1860fb0e714f7e792c7088e0f1a20 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 19:05:54 +1100 Subject: [PATCH 16/29] Rename variable to be in line with existing code. --- core/src/channel.rs | 6 +++--- core/src/mercury/mod.rs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/channel.rs b/core/src/channel.rs index 98ab62e..daf02c7 100644 --- a/core/src/channel.rs +++ b/core/src/channel.rs @@ -14,7 +14,7 @@ component! { download_rate_estimate: usize = 0, download_measurement_start: Option = None, download_measurement_bytes: usize = 0, - is_shutdown: bool = false, + invalid: bool = false, } } @@ -47,7 +47,7 @@ impl ChannelManager { let seq = self.lock(|inner| { let seq = inner.sequence.get(); - if !inner.is_shutdown { + if !inner.invalid { inner.channels.insert(seq, tx); } seq @@ -93,7 +93,7 @@ impl ChannelManager { pub(crate) fn shutdown(&self) { self.lock(|inner| { - inner.is_shutdown = true; + inner.invalid = true; // destroy the sending halves of the channels to signal everyone who is waiting for something. inner.channels.clear(); }); diff --git a/core/src/mercury/mod.rs b/core/src/mercury/mod.rs index 2fb6b87..1eb0e7e 100644 --- a/core/src/mercury/mod.rs +++ b/core/src/mercury/mod.rs @@ -20,7 +20,7 @@ component! { sequence: SeqGenerator = SeqGenerator::new(0), pending: HashMap, MercuryPending> = HashMap::new(), subscriptions: Vec<(String, mpsc::UnboundedSender)> = Vec::new(), - is_shutdown: bool = false, + invalid: bool = false, } } @@ -63,7 +63,7 @@ impl MercuryManager { let seq = self.next_seq(); self.lock(|inner| { - if !inner.is_shutdown { + if !inner.invalid { inner.pending.insert(seq.clone(), pending); } }); @@ -114,7 +114,7 @@ impl MercuryManager { let (tx, rx) = mpsc::unbounded(); manager.lock(move |inner| { - if !inner.is_shutdown { + if !inner.invalid { debug!("subscribed uri={} count={}", uri, response.payload.len()); if response.payload.len() > 0 { // Old subscription protocol, watch the provided list of URIs @@ -232,7 +232,7 @@ impl MercuryManager { pub(crate) fn shutdown(&self) { self.lock(|inner| { - inner.is_shutdown = true; + inner.invalid = true; // destroy the sending halves of the channels to signal everyone who is waiting for something. inner.pending.clear(); inner.subscriptions.clear(); From f26db0111063619ca2e3652273a0ab6da785c289 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 19:09:26 +1100 Subject: [PATCH 17/29] Rate-limit automatic re-connection attempts when spirc shuts down. --- src/main.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main.rs b/src/main.rs index b965904..988f62b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ use std::str::FromStr; use tokio_core::reactor::{Core, Handle}; use tokio_io::IoStream; use url::Url; +use std::time::Instant; use librespot::core::authentication::{get_credentials, Credentials}; use librespot::core::cache::Cache; @@ -382,6 +383,7 @@ struct Main { shutdown: bool, last_credentials: Option, + auto_connect_times: Vec, player_event_channel: Option>, player_event_program: Option, @@ -406,6 +408,7 @@ impl Main { spirc_task: None, shutdown: false, last_credentials: None, + auto_connect_times: Vec::new(), signal: Box::new(tokio_signal::ctrl_c().flatten_stream()), player_event_channel: None, @@ -454,6 +457,7 @@ impl Future for Main { if let Some(ref spirc) = self.spirc { spirc.shutdown(); } + self.auto_connect_times.clear(); self.credentials(creds); progress = true; @@ -505,7 +509,16 @@ impl Future for Main { } else { warn!("Spirc shut down unexpectedly"); self.spirc_task = None; + + while (!self.auto_connect_times.is_empty()) && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) { + let _ = self.auto_connect_times.remove(0); + } + if self.auto_connect_times.len() >= 5 { + error!("Spirc shut down too often. Exiting to avoid too many login attempts."); + return Ok(Async::Ready(())); + } if let Some(credentials) = self.last_credentials.clone() { + self.auto_connect_times.push(Instant::now()); self.credentials(credentials); progress = true; } From dadab486d2ec8a86b30f618d17599c1a563f2163 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 19:51:09 +1100 Subject: [PATCH 18/29] Don't exit if too many spirc failures. --- src/main.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 988f62b..a461857 100644 --- a/src/main.rs +++ b/src/main.rs @@ -513,16 +513,17 @@ impl Future for Main { while (!self.auto_connect_times.is_empty()) && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) { let _ = self.auto_connect_times.remove(0); } - if self.auto_connect_times.len() >= 5 { - error!("Spirc shut down too often. Exiting to avoid too many login attempts."); - return Ok(Async::Ready(())); - } + if let Some(credentials) = self.last_credentials.clone() { - self.auto_connect_times.push(Instant::now()); - self.credentials(credentials); - progress = true; + if self.auto_connect_times.len() >= 5 { + warn!("Spirc shut down too often. Not reconnecting automatically."); + } else { + self.auto_connect_times.push(Instant::now()); + self.credentials(credentials); + } } } + progress = true; } } From 0e22678a281fca2b746c4f2210e905398c9e9415 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Thu, 23 Jan 2020 21:10:55 +1100 Subject: [PATCH 19/29] Workaround for Rust 1.33 borrow checker. --- src/main.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index a461857..e2c5f51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -502,6 +502,7 @@ impl Future for Main { progress = true; } + let mut try_to_reconnect = false; if let Some(ref mut spirc_task) = self.spirc_task { if let Async::Ready(()) = spirc_task.poll().unwrap() { if self.shutdown { @@ -510,22 +511,26 @@ impl Future for Main { warn!("Spirc shut down unexpectedly"); self.spirc_task = None; - while (!self.auto_connect_times.is_empty()) && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) { - let _ = self.auto_connect_times.remove(0); - } - - if let Some(credentials) = self.last_credentials.clone() { - if self.auto_connect_times.len() >= 5 { - warn!("Spirc shut down too often. Not reconnecting automatically."); - } else { - self.auto_connect_times.push(Instant::now()); - self.credentials(credentials); - } - } + try_to_reconnect = true; } progress = true; } } + if try_to_reconnect { + while (!self.auto_connect_times.is_empty()) && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) { + let _ = self.auto_connect_times.remove(0); + } + + if let Some(credentials) = self.last_credentials.clone() { + if self.auto_connect_times.len() >= 5 { + warn!("Spirc shut down too often. Not reconnecting automatically."); + } else { + self.auto_connect_times.push(Instant::now()); + self.credentials(credentials); + } + } + } + if let Some(ref mut player_event_channel) = self.player_event_channel { if let Async::Ready(Some(event)) = player_event_channel.poll().unwrap() { From a52092e094e6e721c043d44260e2498d4848ffc9 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Fri, 24 Jan 2020 10:12:16 +1100 Subject: [PATCH 20/29] Convert another panic to error handling. Compatibility fix for Rust 1.33. --- connect/src/spirc.rs | 6 +++++- src/main.rs | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 9594082..132c9fc 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -333,7 +333,11 @@ impl Future for SpircTask { progress = true; self.handle_frame(frame); } - Async::Ready(None) => panic!("subscription terminated"), + Async::Ready(None) => { + error!("subscription terminated"); + self.shutdown = true; + self.commands.close(); + }, Async::NotReady => (), } diff --git a/src/main.rs b/src/main.rs index e2c5f51..84b2fdd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -502,21 +502,20 @@ impl Future for Main { progress = true; } - let mut try_to_reconnect = false; + let mut drop_spirc_and_try_to_reconnect = false; if let Some(ref mut spirc_task) = self.spirc_task { if let Async::Ready(()) = spirc_task.poll().unwrap() { if self.shutdown { return Ok(Async::Ready(())); } else { warn!("Spirc shut down unexpectedly"); - self.spirc_task = None; - - try_to_reconnect = true; + drop_spirc_and_try_to_reconnect = true; } progress = true; } } - if try_to_reconnect { + if drop_spirc_and_try_to_reconnect { + self.spirc_task = None; while (!self.auto_connect_times.is_empty()) && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) { let _ = self.auto_connect_times.remove(0); } From d599c5cc8514a8845cb2656d9567485b09cffb24 Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Fri, 24 Jan 2020 00:27:57 +0100 Subject: [PATCH 21/29] Further reduce cache bloat --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 182adfd..ee2bd7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,8 @@ before_cache: - rm -rfv target/debug/deps/liblibrespot-* - rm -rfv target/debug/deps/librespot-* - rm -rfv target/debug/{librespot,liblibrespot}.d - - cargo clean -p librespot librespot-core librespot-connect librespot-audio librespot-metadata librespot-playback + - rm -rfv target/debug/incremental/{build_script_build,librespot,librespot_core,librespot_connect,librespot_audio,librespot_metadata,librespot_playback,librespot_player,librespot_protocol}-* + - cargo clean -p librespot librespot-core librespot-connect librespot-audio librespot-metadata librespot-playback addons: apt: From 94bb4623d209aaf92221f5a6ef0497a93db1cbc5 Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Fri, 24 Jan 2020 00:59:28 +0100 Subject: [PATCH 22/29] Correct cargo clean package flags --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ee2bd7f..ffd2eb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_cache: - rm -rfv target/debug/deps/librespot-* - rm -rfv target/debug/{librespot,liblibrespot}.d - rm -rfv target/debug/incremental/{build_script_build,librespot,librespot_core,librespot_connect,librespot_audio,librespot_metadata,librespot_playback,librespot_player,librespot_protocol}-* - - cargo clean -p librespot librespot-core librespot-connect librespot-audio librespot-metadata librespot-playback + - cargo clean -p librespot -p librespot-core -p librespot-connect -p librespot-audio -p librespot-metadata -p librespot-playback addons: apt: From b8617f54a97f6660d8259332c3c51247953d4639 Mon Sep 17 00:00:00 2001 From: misuzu Date: Fri, 24 Jan 2020 02:35:24 +0200 Subject: [PATCH 23/29] Added subprocess audio backend --- Cargo.lock | 7 +++ playback/Cargo.toml | 1 + playback/src/audio_backend/mod.rs | 4 ++ playback/src/audio_backend/subprocess.rs | 59 ++++++++++++++++++++++++ playback/src/lib.rs | 1 + 5 files changed, 72 insertions(+) create mode 100644 playback/src/audio_backend/subprocess.rs diff --git a/Cargo.lock b/Cargo.lock index 594b80c..f5d6cfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -908,6 +908,7 @@ dependencies = [ "portaudio-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rodio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", + "shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1725,6 +1726,11 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "shell-words" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "signal-hook" version = "0.1.10" @@ -2540,6 +2546,7 @@ dependencies = [ "checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" "checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" "checksum shannon 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ea5b41c9427b56caa7b808cb548a04fb50bb5b9e98590b53f28064ff4174561" +"checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a" "checksum signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4f61c4d59f3aaa9f61bba6450a9b80ba48362fd7d651689e7a10c453b1f6dc68" "checksum signal-hook-registry 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "913661ac8848a61e39684a3c3e7a7a14a4deec7f54b4976d0641e70dda3939b1" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" diff --git a/playback/Cargo.toml b/playback/Cargo.toml index f414d5e..e3aa887 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -20,6 +20,7 @@ version = "0.1.0" futures = "0.1" log = "0.4" byteorder = "1.3" +shell-words = "0.1.0" alsa = { version = "0.2.1", optional = true } portaudio-rs = { version = "0.3.0", optional = true } diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index 441252c..d385e61 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -46,6 +46,9 @@ use self::sdl::SdlSink; mod pipe; use self::pipe::StdoutSink; +mod subprocess; +use self::subprocess::SubprocessSink; + pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box)] = &[ #[cfg(feature = "alsa-backend")] ("alsa", mk_sink::), @@ -60,6 +63,7 @@ pub const BACKENDS: &'static [(&'static str, fn(Option) -> Box #[cfg(feature = "sdl-backend")] ("sdl", mk_sink::), ("pipe", mk_sink::), + ("subprocess", mk_sink::), ]; pub fn find(name: Option) -> Option) -> Box> { diff --git a/playback/src/audio_backend/subprocess.rs b/playback/src/audio_backend/subprocess.rs new file mode 100644 index 0000000..2af8836 --- /dev/null +++ b/playback/src/audio_backend/subprocess.rs @@ -0,0 +1,59 @@ +use super::{Open, Sink}; +use shell_words::split; +use std::io::{self, Write}; +use std::mem; +use std::process::{Child, Command, Stdio}; +use std::slice; + +pub struct SubprocessSink { + shell_command: String, + child: Option, +} + +impl Open for SubprocessSink { + fn open(shell_command: Option) -> SubprocessSink { + if let Some(shell_command) = shell_command { + SubprocessSink { + shell_command: shell_command, + child: None, + } + } else { + panic!("subprocess sink requires specifying a shell command"); + } + } +} + +impl Sink for SubprocessSink { + fn start(&mut self) -> io::Result<()> { + let args = split(&self.shell_command).unwrap(); + self.child = Some( + Command::new(&args[0]) + .args(&args[1..]) + .stdin(Stdio::piped()) + .spawn()?, + ); + Ok(()) + } + + fn stop(&mut self) -> io::Result<()> { + if let Some(child) = &mut self.child.take() { + child.kill()?; + child.wait()?; + } + Ok(()) + } + + fn write(&mut self, data: &[i16]) -> io::Result<()> { + let data: &[u8] = unsafe { + slice::from_raw_parts( + data.as_ptr() as *const u8, + data.len() * mem::size_of::(), + ) + }; + if let Some(child) = &mut self.child { + let child_stdin = child.stdin.as_mut().unwrap(); + child_stdin.write_all(data)?; + } + Ok(()) + } +} diff --git a/playback/src/lib.rs b/playback/src/lib.rs index aeb0407..fe00aaa 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -3,6 +3,7 @@ extern crate log; extern crate byteorder; extern crate futures; +extern crate shell_words; #[cfg(feature = "alsa-backend")] extern crate alsa; From e9c3357e4134d6fc873d54389bcd32de8ec469f1 Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Fri, 24 Jan 2020 02:26:16 +0100 Subject: [PATCH 24/29] Run cargo fmt --- connect/src/spirc.rs | 2 +- core/src/session.rs | 2 +- playback/src/player.rs | 5 ++--- src/main.rs | 7 ++++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index 1cbcc2b..f1b4180 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -337,7 +337,7 @@ impl Future for SpircTask { error!("subscription terminated"); self.shutdown = true; self.commands.close(); - }, + } Async::NotReady => (), } diff --git a/core/src/session.rs b/core/src/session.rs index 53e7a2d..4d86a02 100644 --- a/core/src/session.rs +++ b/core/src/session.rs @@ -296,7 +296,7 @@ where warn!("Connection to server closed."); session.shutdown(); return Ok(Async::Ready(())); - }, + } Ok(Async::NotReady) => return Ok(Async::NotReady), Err(e) => { session.shutdown(); diff --git a/playback/src/player.rs b/playback/src/player.rs index 3f7152b..38ee00c 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -636,13 +636,12 @@ impl PlayerInternal { spotify_id: SpotifyId, position: i64, ) -> Option<(Decoder, f32, StreamLoaderController, usize)> { - let audio = match AudioItem::get_audio_item(&self.session, spotify_id).wait() { Ok(audio) => audio, Err(_) => { error!("Unable to load audio item."); - return None - }, + return None; + } }; info!("Loading <{}> with Spotify URI <{}>", audio.name, audio.uri); diff --git a/src/main.rs b/src/main.rs index 36a3ff4..9f41f2c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,10 @@ use std::mem; use std::path::PathBuf; use std::process::exit; use std::str::FromStr; +use std::time::Instant; use tokio_core::reactor::{Core, Handle}; use tokio_io::IoStream; use url::Url; -use std::time::Instant; use librespot::core::authentication::{get_credentials, Credentials}; use librespot::core::cache::Cache; @@ -513,7 +513,9 @@ impl Future for Main { } if drop_spirc_and_try_to_reconnect { self.spirc_task = None; - while (!self.auto_connect_times.is_empty()) && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) { + while (!self.auto_connect_times.is_empty()) + && ((Instant::now() - self.auto_connect_times[0]).as_secs() > 600) + { let _ = self.auto_connect_times.remove(0); } @@ -527,7 +529,6 @@ impl Future for Main { } } - if let Some(ref mut player_event_channel) = self.player_event_channel { if let Async::Ready(Some(event)) = player_event_channel.poll().unwrap() { if let Some(ref program) = self.player_event_program { From c9f1914fc8573af8fa63ba6a5d343d11ee804e7d Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Tue, 28 Jan 2020 19:19:18 +0100 Subject: [PATCH 25/29] Run cargo fmt --- connect/src/spirc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/connect/src/spirc.rs b/connect/src/spirc.rs index c1d6683..546d818 100644 --- a/connect/src/spirc.rs +++ b/connect/src/spirc.rs @@ -673,7 +673,9 @@ impl SpircTask { // Removes current track if it is queued // Returns the index of the next track let current_index = self.state.get_playing_track_index() as usize; - if (current_index < self.state.get_track().len()) && self.state.get_track()[current_index].get_queued() { + if (current_index < self.state.get_track().len()) + && self.state.get_track()[current_index].get_queued() + { self.state.mut_track().remove(current_index); current_index } else { @@ -719,15 +721,13 @@ impl SpircTask { self.state.set_position_measured_at(now as u64); self.load_track(continue_playing); - - } else { + } else { info!("Not playing next track because there are no more tracks left in queue."); self.state.set_playing_track_index(0); self.state.set_status(PlayStatus::kPlayStatusStop); self.player.stop(); self.mixer.stop(); } - } fn handle_prev(&mut self) { From 65d1c1bf8ed9073b7d409d222c0648912ee80f97 Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Wed, 29 Jan 2020 09:45:06 +1100 Subject: [PATCH 26/29] Proper error handling when connecting to the server. --- core/src/connection/mod.rs | 22 ++++++++++++++++++-- src/main.rs | 42 ++++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 20 deletions(-) diff --git a/core/src/connection/mod.rs b/core/src/connection/mod.rs index 5f9b3dc..7249779 100644 --- a/core/src/connection/mod.rs +++ b/core/src/connection/mod.rs @@ -28,9 +28,27 @@ pub fn connect( let (addr, connect_url) = match *proxy { Some(ref url) => { info!("Using proxy \"{}\"", url); - (url.to_socket_addrs().unwrap().next().unwrap(), Some(addr)) + match url.to_socket_addrs().and_then(|mut iter| { + iter.next().ok_or(io::Error::new( + io::ErrorKind::NotFound, + "Can't resolve proxy server address", + )) + }) { + Ok(socket_addr) => (socket_addr, Some(addr)), + Err(error) => return Box::new(futures::future::err(error)), + } + } + None => { + match addr.to_socket_addrs().and_then(|mut iter| { + iter.next().ok_or(io::Error::new( + io::ErrorKind::NotFound, + "Can't resolve server address", + )) + }) { + Ok(socket_addr) => (socket_addr, None), + Err(error) => return Box::new(futures::future::err(error)), + } } - None => (addr.to_socket_addrs().unwrap().next().unwrap(), None), }; let socket = TcpStream::connect(&addr, handle); diff --git a/src/main.rs b/src/main.rs index 9f41f2c..4a74d22 100644 --- a/src/main.rs +++ b/src/main.rs @@ -460,27 +460,33 @@ impl Future for Main { progress = true; } - if let Async::Ready(session) = self.connect.poll().unwrap() { - self.connect = Box::new(futures::future::empty()); - let mixer_config = self.mixer_config.clone(); - let mixer = (self.mixer)(Some(mixer_config)); - let player_config = self.player_config.clone(); - let connect_config = self.connect_config.clone(); + match self.connect.poll() { + Ok(Async::Ready(session)) => { + self.connect = Box::new(futures::future::empty()); + let mixer_config = self.mixer_config.clone(); + let mixer = (self.mixer)(Some(mixer_config)); + 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 device = self.device.clone(); - let (player, event_channel) = - Player::new(player_config, session.clone(), audio_filter, move || { - (backend)(device) - }); + let audio_filter = mixer.get_audio_filter(); + let backend = self.backend; + let device = self.device.clone(); + let (player, event_channel) = + Player::new(player_config, session.clone(), audio_filter, move || { + (backend)(device) + }); - let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer); - self.spirc = Some(spirc); - self.spirc_task = Some(spirc_task); - self.player_event_channel = Some(event_channel); + let (spirc, spirc_task) = Spirc::new(connect_config, session, player, mixer); + self.spirc = Some(spirc); + self.spirc_task = Some(spirc_task); + self.player_event_channel = Some(event_channel); - progress = true; + progress = true; + } + Ok(Async::NotReady) => (), + Err(_) => { + self.connect = Box::new(futures::future::empty()); + } } if let Async::Ready(Some(())) = self.signal.poll().unwrap() { From 37f6e3eb9ca52cc695db2d8285bc8e87a115d66a Mon Sep 17 00:00:00 2001 From: Konstantin Seiler Date: Wed, 29 Jan 2020 09:51:26 +1100 Subject: [PATCH 27/29] Print error message on connection failure. --- src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 4a74d22..8ee3b0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -484,7 +484,8 @@ impl Future for Main { progress = true; } Ok(Async::NotReady) => (), - Err(_) => { + Err(error) => { + error!("Could not connect to server: {}", error); self.connect = Box::new(futures::future::empty()); } } From 6daa2acd0af7dfe70837592b33bfe9480783f852 Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Thu, 30 Jan 2020 02:39:41 +0100 Subject: [PATCH 28/29] Update version numbers to 0.1.1 --- Cargo.toml | 14 +++++++------- audio/Cargo.toml | 4 ++-- connect/Cargo.toml | 8 ++++---- core/Cargo.toml | 4 ++-- metadata/Cargo.toml | 6 +++--- playback/Cargo.toml | 8 ++++---- protocol/Cargo.toml | 2 +- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6605b8a..9507feb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot" -version = "0.1.0" +version = "0.1.1" authors = ["Librespot Org"] license = "MIT" description = "An open source client library for Spotify, with support for Spotify Connect" @@ -22,22 +22,22 @@ doc = false [dependencies.librespot-audio] path = "audio" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-connect] path = "connect" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-core] path = "core" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-metadata] path = "metadata" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-playback] path = "playback" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-protocol] path = "protocol" -version = "0.1.0" +version = "0.1.1" [dependencies] base64 = "0.10" diff --git a/audio/Cargo.toml b/audio/Cargo.toml index a0f9531..c912156 100644 --- a/audio/Cargo.toml +++ b/audio/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-audio" -version = "0.1.0" +version = "0.1.1" authors = ["Paul Lietar "] description="The audio fetching and processing logic for librespot" license="MIT" @@ -8,7 +8,7 @@ edition = "2018" [dependencies.librespot-core] path = "../core" -version = "0.1.0" +version = "0.1.1" [dependencies] bit-set = "0.5" diff --git a/connect/Cargo.toml b/connect/Cargo.toml index c468d2d..25b4963 100644 --- a/connect/Cargo.toml +++ b/connect/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-connect" -version = "0.1.0" +version = "0.1.1" authors = ["Paul Lietar "] description="The discovery and Spotify Connect logic for librespot" license="MIT" @@ -8,13 +8,13 @@ edition = "2018" [dependencies.librespot-core] path = "../core" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-playback] path = "../playback" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-protocol] path = "../protocol" -version = "0.1.0" +version = "0.1.1" [dependencies] base64 = "0.10" diff --git a/core/Cargo.toml b/core/Cargo.toml index 9e9ccce..e01c986 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-core" -version = "0.1.0" +version = "0.1.1" authors = ["Paul Lietar "] build = "build.rs" description="The core functionality provided by librespot" @@ -9,7 +9,7 @@ edition = "2018" [dependencies.librespot-protocol] path = "../protocol" -version = "0.1.0" +version = "0.1.1" [dependencies] base64 = "0.10" diff --git a/metadata/Cargo.toml b/metadata/Cargo.toml index 02c0f2b..328ba06 100644 --- a/metadata/Cargo.toml +++ b/metadata/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-metadata" -version = "0.1.0" +version = "0.1.1" authors = ["Paul Lietar "] description="The metadata logic for librespot" license="MIT" @@ -15,7 +15,7 @@ log = "0.4" [dependencies.librespot-core] path = "../core" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-protocol] path = "../protocol" -version = "0.1.0" +version = "0.1.1" diff --git a/playback/Cargo.toml b/playback/Cargo.toml index e3aa887..c0986c4 100644 --- a/playback/Cargo.toml +++ b/playback/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-playback" -version = "0.1.0" +version = "0.1.1" authors = ["Sasha Hilton "] description="The audio playback logic for librespot" license="MIT" @@ -8,13 +8,13 @@ edition = "2018" [dependencies.librespot-audio] path = "../audio" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-core] path = "../core" -version = "0.1.0" +version = "0.1.1" [dependencies.librespot-metadata] path = "../metadata" -version = "0.1.0" +version = "0.1.1" [dependencies] futures = "0.1" diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml index 792f5b5..0960cfb 100644 --- a/protocol/Cargo.toml +++ b/protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librespot-protocol" -version = "0.1.0" +version = "0.1.1" authors = ["Paul Liétar "] build = "build.rs" description="The protobuf logic for communicating with Spotify servers" From 3672214e312d7a5634ca71837589555f9b0554e5 Mon Sep 17 00:00:00 2001 From: Sasha Hilton Date: Thu, 30 Jan 2020 02:41:47 +0100 Subject: [PATCH 29/29] Update Cargo.lock --- Cargo.lock | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5d6cfe..0ed0911 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -768,7 +768,7 @@ dependencies = [ [[package]] name = "librespot" -version = "0.1.0" +version = "0.1.1" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -776,12 +776,12 @@ dependencies = [ "getopts 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.27 (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-metadata 0.1.0", - "librespot-playback 0.1.0", - "librespot-protocol 0.1.0", + "librespot-audio 0.1.1", + "librespot-connect 0.1.1", + "librespot-core 0.1.1", + "librespot-metadata 0.1.1", + "librespot-playback 0.1.1", + "librespot-protocol 0.1.1", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -797,7 +797,7 @@ dependencies = [ [[package]] name = "librespot-audio" -version = "0.1.0" +version = "0.1.1" dependencies = [ "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "bit-set 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -805,7 +805,7 @@ dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "lewton 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.0", + "librespot-core 0.1.1", "librespot-tremor 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -816,7 +816,7 @@ dependencies = [ [[package]] name = "librespot-connect" -version = "0.1.0" +version = "0.1.1" dependencies = [ "aes-ctr 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -826,9 +826,9 @@ dependencies = [ "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", "libmdns 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.0", - "librespot-playback 0.1.0", - "librespot-protocol 0.1.0", + "librespot-core 0.1.1", + "librespot-playback 0.1.1", + "librespot-protocol 0.1.1", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -843,7 +843,7 @@ dependencies = [ [[package]] name = "librespot-core" -version = "0.1.0" +version = "0.1.1" dependencies = [ "aes 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -856,7 +856,7 @@ dependencies = [ "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-proxy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-protocol 0.1.0", + "librespot-protocol 0.1.1", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", @@ -879,12 +879,12 @@ dependencies = [ [[package]] name = "librespot-metadata" -version = "0.1.0" +version = "0.1.1" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-core 0.1.0", - "librespot-protocol 0.1.0", + "librespot-core 0.1.1", + "librespot-protocol 0.1.1", "linear-map 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -892,7 +892,7 @@ dependencies = [ [[package]] name = "librespot-playback" -version = "0.1.0" +version = "0.1.1" dependencies = [ "alsa 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -901,9 +901,9 @@ dependencies = [ "jack 0.5.7 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", "libpulse-sys 0.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "librespot-audio 0.1.0", - "librespot-core 0.1.0", - "librespot-metadata 0.1.0", + "librespot-audio 0.1.1", + "librespot-core 0.1.1", + "librespot-metadata 0.1.1", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "portaudio-rs 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "rodio 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -913,7 +913,7 @@ dependencies = [ [[package]] name = "librespot-protocol" -version = "0.1.0" +version = "0.1.1" dependencies = [ "protobuf 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf-codegen 2.8.1 (registry+https://github.com/rust-lang/crates.io-index)",