diff --git a/audio/src/decrypt.rs b/audio/src/decrypt.rs index b745c4e0..85ba725a 100644 --- a/audio/src/decrypt.rs +++ b/audio/src/decrypt.rs @@ -7,8 +7,9 @@ use std::ops::Add; use core::audio_key::AudioKey; -const AUDIO_AESIV: &'static [u8] = &[0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, - 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93]; +const AUDIO_AESIV: &'static [u8] = &[ + 0x72, 0xe0, 0x67, 0xfb, 0xdd, 0xcb, 0xcf, 0x77, 0xeb, 0xe8, 0xbc, 0x64, 0x3f, 0x63, 0x0d, 0x93 +]; pub struct AudioDecrypt { cipher: Box, @@ -44,8 +45,8 @@ impl io::Seek for AudioDecrypt { let skip = newpos % 16; let iv = BigUint::from_bytes_be(AUDIO_AESIV) - .add(BigUint::from_u64(newpos / 16).unwrap()) - .to_bytes_be(); + .add(BigUint::from_u64(newpos / 16).unwrap()) + .to_bytes_be(); self.cipher = aes::ctr(aes::KeySize::KeySize128, &self.key.0, &iv); let buf = vec![0u8; skip as usize]; diff --git a/audio/src/fetch.rs b/audio/src/fetch.rs index 1455f21b..a54e48de 100644 --- a/audio/src/fetch.rs +++ b/audio/src/fetch.rs @@ -1,11 +1,11 @@ use bit_set::BitSet; -use byteorder::{ByteOrder, BigEndian, WriteBytesExt}; +use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use futures::Stream; -use futures::sync::{oneshot, mpsc}; -use futures::{Poll, Async, Future}; +use futures::sync::{mpsc, oneshot}; +use futures::{Async, Future, Poll}; use std::cmp::min; use std::fs; -use std::io::{self, Read, Write, Seek, SeekFrom}; +use std::io::{self, Read, Seek, SeekFrom, Write}; use std::sync::{Arc, Condvar, Mutex}; use tempfile::NamedTempFile; @@ -71,7 +71,12 @@ impl AudioFileOpenStreaming { let (seek_tx, seek_rx) = mpsc::unbounded(); let fetcher = AudioFileFetch::new( - self.session.clone(), shared.clone(), data_rx, write_file, seek_rx, complete_tx + self.session.clone(), + shared.clone(), + data_rx, + write_file, + seek_rx, + complete_tx, ); self.session.spawn(move |_| fetcher); @@ -148,14 +153,16 @@ impl AudioFile { let session_ = session.clone(); session.spawn(move |_| { - complete_rx.map(move |mut file| { - if let Some(cache) = session_.cache() { - cache.save_file(file_id, &mut file); - debug!("File {} complete, saving to cache", file_id); - } else { - debug!("File {} complete", file_id); - } - }).or_else(|oneshot::Canceled| Ok(())) + complete_rx + .map(move |mut file| { + if let Some(cache) = session_.cache() { + cache.save_file(file_id, &mut file); + debug!("File {} complete, saving to cache", file_id); + } else { + debug!("File {} complete", file_id); + } + }) + .or_else(|oneshot::Canceled| Ok(())) }); AudioFileOpen::Streaming(open) @@ -200,11 +207,14 @@ struct AudioFileFetch { } impl AudioFileFetch { - fn new(session: Session, shared: Arc, - data_rx: ChannelData, output: NamedTempFile, - seek_rx: mpsc::UnboundedReceiver, - complete_tx: oneshot::Sender) -> AudioFileFetch - { + fn new( + session: Session, + shared: Arc, + data_rx: ChannelData, + output: NamedTempFile, + seek_rx: mpsc::UnboundedReceiver, + complete_tx: oneshot::Sender, + ) -> AudioFileFetch { AudioFileFetch { session: session, shared: shared, @@ -233,8 +243,11 @@ impl AudioFileFetch { let offset = self.index * CHUNK_SIZE; - self.output.as_mut().unwrap() - .seek(SeekFrom::Start(offset as u64)).unwrap(); + self.output + .as_mut() + .unwrap() + .seek(SeekFrom::Start(offset as u64)) + .unwrap(); let (_headers, data) = request_chunk(&self.session, self.shared.file_id, self.index).split(); self.data_rx = data; @@ -275,13 +288,20 @@ impl Future for AudioFileFetch { Ok(Async::Ready(Some(data))) => { progress = true; - self.output.as_mut().unwrap() - .write_all(data.as_ref()).unwrap(); + self.output + .as_mut() + .unwrap() + .write_all(data.as_ref()) + .unwrap(); } - Ok(Async::Ready(None)) => { + Ok(Async::Ready(None)) => { progress = true; - trace!("chunk {} / {} complete", self.index, self.shared.chunk_count); + trace!( + "chunk {} / {} complete", + self.index, + self.shared.chunk_count + ); let full = { let mut bitmap = self.shared.bitmap.lock().unwrap(); @@ -303,7 +323,7 @@ impl Future for AudioFileFetch { Err(ChannelError) => { warn!("error from channel"); return Ok(Async::Ready(())); - }, + } } if !progress { diff --git a/audio/src/lewton_decoder.rs b/audio/src/lewton_decoder.rs index 5bedda9d..3ea418fe 100644 --- a/audio/src/lewton_decoder.rs +++ b/audio/src/lewton_decoder.rs @@ -10,8 +10,9 @@ pub struct VorbisDecoder(OggStreamReader); pub struct VorbisPacket(Vec); pub struct VorbisError(lewton::VorbisError); -impl VorbisDecoder - where R: Read + Seek +impl VorbisDecoder +where + R: Read + Seek, { pub fn new(input: R) -> Result, VorbisError> { Ok(VorbisDecoder(OggStreamReader::new(input)?)) diff --git a/audio/src/lib.rs b/audio/src/lib.rs index 37b62774..b9531c7b 100644 --- a/audio/src/lib.rs +++ b/audio/src/lib.rs @@ -1,11 +1,13 @@ -#[macro_use] extern crate log; -#[macro_use] extern crate futures; +#[macro_use] +extern crate futures; +#[macro_use] +extern crate log; extern crate bit_set; extern crate byteorder; extern crate crypto; -extern crate num_traits; extern crate num_bigint; +extern crate num_traits; extern crate tempfile; extern crate librespot_core as core; @@ -22,6 +24,6 @@ pub use fetch::{AudioFile, AudioFileOpen}; pub use decrypt::AudioDecrypt; #[cfg(not(any(feature = "with-tremor", feature = "with-vorbis")))] -pub use lewton_decoder::{VorbisDecoder, VorbisPacket, VorbisError}; +pub use lewton_decoder::{VorbisDecoder, VorbisError, VorbisPacket}; #[cfg(any(feature = "with-tremor", feature = "with-vorbis"))] -pub use libvorbis_decoder::{VorbisDecoder, VorbisPacket, VorbisError}; +pub use libvorbis_decoder::{VorbisDecoder, VorbisError, VorbisPacket}; diff --git a/audio/src/libvorbis_decoder.rs b/audio/src/libvorbis_decoder.rs index c88fc44e..d7ad91c3 100644 --- a/audio/src/libvorbis_decoder.rs +++ b/audio/src/libvorbis_decoder.rs @@ -1,5 +1,7 @@ -#[cfg(not(feature = "with-tremor"))] extern crate vorbis; -#[cfg(feature = "with-tremor")] extern crate tremor as vorbis; +#[cfg(feature = "with-tremor")] +extern crate tremor as vorbis; +#[cfg(not(feature = "with-tremor"))] +extern crate vorbis; use std::io::{Read, Seek}; use std::fmt; @@ -9,8 +11,9 @@ pub struct VorbisDecoder(vorbis::Decoder); pub struct VorbisPacket(vorbis::Packet); pub struct VorbisError(vorbis::VorbisError); -impl VorbisDecoder - where R: Read + Seek +impl VorbisDecoder +where + R: Read + Seek, { pub fn new(input: R) -> Result, VorbisError> { Ok(VorbisDecoder(vorbis::Decoder::new(input)?)) diff --git a/examples/play.rs b/examples/play.rs index 4dd8601c..289c37e0 100644 --- a/examples/play.rs +++ b/examples/play.rs @@ -20,7 +20,7 @@ fn main() { let session_config = SessionConfig::default(); let player_config = PlayerConfig::default(); - let args : Vec<_> = env::args().collect(); + let args: Vec<_> = env::args().collect(); if args.len() != 4 { println!("Usage: {} USERNAME PASSWORD TRACK", args[0]); } @@ -33,9 +33,12 @@ fn main() { let backend = audio_backend::find(None).unwrap(); println!("Connecting .."); - let session = core.run(Session::connect(session_config, credentials, None, handle)).unwrap(); + let session = core.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/playback/src/audio_backend/alsa.rs b/playback/src/audio_backend/alsa.rs index 517c8f0a..01e0cd94 100644 --- a/playback/src/audio_backend/alsa.rs +++ b/playback/src/audio_backend/alsa.rs @@ -1,11 +1,11 @@ use super::{Open, Sink}; use std::io; -use alsa::{PCM, Stream, Mode, Format, Access}; +use alsa::{Access, Format, Mode, Stream, PCM}; pub struct AlsaSink(Option, String); impl Open for AlsaSink { - fn open(device: Option) -> AlsaSink { + fn open(device: Option) -> AlsaSink { info!("Using alsa sink"); let name = device.unwrap_or("default".to_string()); @@ -17,14 +17,22 @@ impl Open for AlsaSink { impl Sink for AlsaSink { fn start(&mut self) -> io::Result<()> { if self.0.is_none() { - match PCM::open(&*self.1, - Stream::Playback, Mode::Blocking, - Format::Signed16, Access::Interleaved, - 2, 44100) { + match PCM::open( + &*self.1, + Stream::Playback, + Mode::Blocking, + Format::Signed16, + Access::Interleaved, + 2, + 44100, + ) { Ok(f) => self.0 = Some(f), Err(e) => { - error!("Alsa error PCM open {}", e); - return Err(io::Error::new(io::ErrorKind::Other, "Alsa error: PCM open failed")); + error!("Alsa error PCM open {}", e); + return Err(io::Error::new( + io::ErrorKind::Other, + "Alsa error: PCM open failed", + )); } } } diff --git a/playback/src/audio_backend/jackaudio.rs b/playback/src/audio_backend/jackaudio.rs index 9b389ea6..79b6ca21 100644 --- a/playback/src/audio_backend/jackaudio.rs +++ b/playback/src/audio_backend/jackaudio.rs @@ -1,11 +1,12 @@ use std::io; use super::{Open, Sink}; -use jack::prelude::{AudioOutPort, AudioOutSpec, Client, JackControl, ProcessScope, AsyncClient, client_options, ProcessHandler, Port }; -use std::sync::mpsc::{sync_channel, SyncSender, Receiver}; +use jack::prelude::{client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port, + ProcessHandler, ProcessScope}; +use std::sync::mpsc::{sync_channel, Receiver, SyncSender}; pub struct JackSink { send: SyncSender, - active_client: AsyncClient<(),JackData>, + active_client: AsyncClient<(), JackData>, } pub struct JackData { @@ -43,15 +44,26 @@ impl Open for JackSink { 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 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 { rec: rx, port_l: ch_l, port_r: ch_r }; + let (tx, rx) = sync_channel(2 * 1024 * 4); + let jack_data = JackData { + rec: rx, + port_l: ch_l, + port_r: ch_r, + }; let active_client = AsyncClient::new(client, (), jack_data).unwrap(); - JackSink { send: tx, active_client: active_client } - } + JackSink { + send: tx, + active_client: active_client, + } + } } impl Sink for JackSink { diff --git a/playback/src/audio_backend/mod.rs b/playback/src/audio_backend/mod.rs index be73bf6c..895b0100 100644 --- a/playback/src/audio_backend/mod.rs +++ b/playback/src/audio_backend/mod.rs @@ -37,9 +37,7 @@ use self::jackaudio::JackSink; 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")] @@ -53,8 +51,16 @@ pub const BACKENDS : &'static [ pub fn find(name: Option) -> Option) -> Box> { if let Some(name) = name { - BACKENDS.iter().find(|backend| name == backend.0).map(|backend| backend.1) + BACKENDS + .iter() + .find(|backend| name == backend.0) + .map(|backend| backend.1) } else { - Some(BACKENDS.first().expect("No backends were enabled at build time").1) + Some( + BACKENDS + .first() + .expect("No backends were enabled at build time") + .1, + ) } } diff --git a/playback/src/audio_backend/pipe.rs b/playback/src/audio_backend/pipe.rs index 10461cfb..c15727e6 100644 --- a/playback/src/audio_backend/pipe.rs +++ b/playback/src/audio_backend/pipe.rs @@ -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)?; @@ -37,4 +40,3 @@ impl Sink for StdoutSink { Ok(()) } } - diff --git a/playback/src/audio_backend/portaudio.rs b/playback/src/audio_backend/portaudio.rs index 7b69e1ab..fd3b0b26 100644 --- a/playback/src/audio_backend/portaudio.rs +++ b/playback/src/audio_backend/portaudio.rs @@ -4,18 +4,18 @@ use std::process::exit; use std::time::Duration; use portaudio_rs; use portaudio_rs::stream::*; -use portaudio_rs::device::{DeviceIndex, DeviceInfo, get_default_output_index}; +use portaudio_rs::device::{get_default_output_index, DeviceIndex, DeviceInfo}; -pub struct PortAudioSink<'a>(Option>, StreamParameters); +pub struct PortAudioSink<'a>( + Option>, + 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)) - }).filter(|&(_, ref info)| { - info.max_output_channels > 0 - }); + .filter_map(|idx| portaudio_rs::device::get_info(idx).map(|info| (idx, info))) + .filter(|&(_, ref info)| info.max_output_channels > 0); Box::new(devices) } @@ -38,9 +38,8 @@ fn find_output(device: &str) -> Option { .map(|(idx, _)| idx) } -impl <'a> Open for PortAudioSink<'a> { +impl<'a> Open for PortAudioSink<'a> { fn open(device: Option) -> PortAudioSink<'a> { - debug!("Using PortAudio sink"); portaudio_rs::initialize().unwrap(); @@ -71,16 +70,19 @@ impl <'a> Open for PortAudioSink<'a> { } } -impl <'a> Sink for PortAudioSink<'a> { +impl<'a> Sink for PortAudioSink<'a> { fn start(&mut self) -> io::Result<()> { if self.0.is_none() { - self.0 = Some(Stream::open( - None, Some(self.1), - 44100.0, - FRAMES_PER_BUFFER_UNSPECIFIED, - StreamFlags::empty(), - None - ).unwrap());; + self.0 = Some( + Stream::open( + None, + Some(self.1), + 44100.0, + FRAMES_PER_BUFFER_UNSPECIFIED, + StreamFlags::empty(), + None, + ).unwrap(), + );; } self.0.as_mut().unwrap().start().unwrap(); @@ -94,8 +96,7 @@ impl <'a> Sink for PortAudioSink<'a> { fn write(&mut self, data: &[i16]) -> io::Result<()> { match self.0.as_mut().unwrap().write(data) { Ok(_) => (), - Err(portaudio_rs::PaError::OutputUnderflowed) => - error!("PortAudio write underflow"), + Err(portaudio_rs::PaError::OutputUnderflowed) => error!("PortAudio write underflow"), Err(e) => panic!("PA Error {}", e), }; @@ -103,7 +104,7 @@ impl <'a> Sink for PortAudioSink<'a> { } } -impl <'a> Drop for PortAudioSink<'a> { +impl<'a> Drop for PortAudioSink<'a> { fn drop(&mut self) { portaudio_rs::terminate().unwrap(); } diff --git a/playback/src/audio_backend/pulseaudio.rs b/playback/src/audio_backend/pulseaudio.rs index e9f0039b..51bf19de 100644 --- a/playback/src/audio_backend/pulseaudio.rs +++ b/playback/src/audio_backend/pulseaudio.rs @@ -8,13 +8,14 @@ use std::mem; use libc; pub struct PulseAudioSink { - s : *mut pa_simple, - ss : pa_sample_spec, - name : CString, - desc : CString + s: *mut pa_simple, + ss: pa_sample_spec, + name: CString, + desc: CString, } -fn call_pulseaudio(f: F, fail_check: FailCheck, kind: io::ErrorKind) -> io::Result where +fn call_pulseaudio(f: F, fail_check: FailCheck, kind: io::ErrorKind) -> io::Result +where T: Copy, F: Fn(*mut libc::c_int) -> T, FailCheck: Fn(T) -> bool, @@ -23,7 +24,7 @@ fn call_pulseaudio(f: F, fail_check: FailCheck, kind: io::Error let ret = f(&mut error); if fail_check(ret) { let err_cstr = unsafe { CStr::from_ptr(pa_strerror(error)) }; - let errstr = err_cstr.to_string_lossy().into_owned(); + let errstr = err_cstr.to_string_lossy().into_owned(); Err(io::Error::new(kind, errstr)) } else { Ok(ret) @@ -48,7 +49,7 @@ impl Drop for PulseAudioSink { } impl Open for PulseAudioSink { - fn open(device: Option) -> PulseAudioSink { + fn open(device: Option) -> PulseAudioSink { debug!("Using PulseAudio sink"); if device.is_some() { @@ -58,9 +59,9 @@ impl Open for PulseAudioSink { let ss = pa_sample_spec { format: PA_SAMPLE_S16LE, channels: 2, // stereo - rate: 44100 + rate: 44100, }; - + let name = CString::new("librespot").unwrap(); let description = CString::new("Spotify endpoint").unwrap(); @@ -68,7 +69,7 @@ impl Open for PulseAudioSink { s: null_mut(), ss: ss, name: name, - desc: description + desc: description, } } } @@ -78,18 +79,21 @@ impl Sink for PulseAudioSink { if self.s == null_mut() { self.s = call_pulseaudio( |err| unsafe { - pa_simple_new(null(), // Use the default server. - self.name.as_ptr(), // Our application's name. - PA_STREAM_PLAYBACK, - null(), // Use the default device. - self.desc.as_ptr(), // desc of our stream. - &self.ss, // Our sample format. - null(), // Use default channel map - null(), // Use default buffering attributes. - err) + pa_simple_new( + null(), // Use the default server. + self.name.as_ptr(), // Our application's name. + PA_STREAM_PLAYBACK, + null(), // Use the default device. + self.desc.as_ptr(), // desc of our stream. + &self.ss, // Our sample format. + null(), // Use default channel map + null(), // Use default buffering attributes. + err, + ) }, |ptr| ptr == null_mut(), - io::ErrorKind::ConnectionRefused)?; + io::ErrorKind::ConnectionRefused, + )?; } Ok(()) } @@ -101,17 +105,18 @@ impl Sink for PulseAudioSink { fn write(&mut self, data: &[i16]) -> io::Result<()> { if self.s == null_mut() { - Err(io::Error::new(io::ErrorKind::NotConnected, "Not connected to pulseaudio")) - } - else { + Err(io::Error::new( + io::ErrorKind::NotConnected, + "Not connected to pulseaudio", + )) + } else { let ptr = data.as_ptr() as *const libc::c_void; let len = data.len() as usize * mem::size_of::(); call_pulseaudio( - |err| unsafe { - pa_simple_write(self.s, ptr, len, err) - }, + |err| unsafe { pa_simple_write(self.s, ptr, len, err) }, |ret| ret < 0, - io::ErrorKind::BrokenPipe)?; + io::ErrorKind::BrokenPipe, + )?; Ok(()) } } diff --git a/playback/src/lib.rs b/playback/src/lib.rs index 3effd43a..afce662c 100644 --- a/playback/src/lib.rs +++ b/playback/src/lib.rs @@ -1,7 +1,8 @@ -#[macro_use] extern crate log; +#[macro_use] +extern crate log; -extern crate futures; extern crate byteorder; +extern crate futures; #[cfg(feature = "alsa-backend")] extern crate alsa; diff --git a/playback/src/mixer/mod.rs b/playback/src/mixer/mod.rs index a33f5e54..a6ba34aa 100644 --- a/playback/src/mixer/mod.rs +++ b/playback/src/mixer/mod.rs @@ -1,5 +1,7 @@ -pub trait Mixer : Send { - fn open() -> Self where Self: Sized; +pub trait Mixer: Send { + fn open() -> Self + where + Self: Sized; fn start(&self); fn stop(&self); fn set_volume(&self, volume: u16); diff --git a/playback/src/mixer/softmixer.rs b/playback/src/mixer/softmixer.rs index 1637537b..d7d99437 100644 --- a/playback/src/mixer/softmixer.rs +++ b/playback/src/mixer/softmixer.rs @@ -6,19 +6,17 @@ use super::AudioFilter; #[derive(Clone)] pub struct SoftMixer { - volume: Arc + volume: Arc, } impl Mixer for SoftMixer { fn open() -> SoftMixer { SoftMixer { - volume: Arc::new(AtomicUsize::new(0xFFFF)) + volume: Arc::new(AtomicUsize::new(0xFFFF)), } } - fn start(&self) { - } - fn stop(&self) { - } + fn start(&self) {} + fn stop(&self) {} fn volume(&self) -> u16 { self.volume.load(Ordering::Relaxed) as u16 } @@ -26,12 +24,14 @@ impl Mixer for SoftMixer { self.volume.store(volume as usize, Ordering::Relaxed); } fn get_audio_filter(&self) -> Option> { - Some(Box::new(SoftVolumeApplier { volume: self.volume.clone() })) + Some(Box::new(SoftVolumeApplier { + volume: self.volume.clone(), + })) } } struct SoftVolumeApplier { - volume: Arc + volume: Arc, } impl AudioFilter for SoftVolumeApplier { diff --git a/playback/src/player.rs b/playback/src/player.rs index 4c379014..3271c514 100644 --- a/playback/src/player.rs +++ b/playback/src/player.rs @@ -3,10 +3,10 @@ use futures::sync::oneshot; use futures::{future, Future}; use std; use std::borrow::Cow; -use std::io::{Read, Seek, SeekFrom, Result}; +use std::io::{Read, Result, Seek, SeekFrom}; use std::mem; use std::process::Command; -use std::sync::mpsc::{RecvError, TryRecvError, RecvTimeoutError}; +use std::sync::mpsc::{RecvError, RecvTimeoutError, TryRecvError}; use std::thread; use std::time::Duration; @@ -15,9 +15,9 @@ use core::session::Session; use core::spotify_id::SpotifyId; use audio_backend::Sink; -use audio::{AudioFile, AudioDecrypt}; +use audio::{AudioDecrypt, AudioFile}; use audio::{VorbisDecoder, VorbisPacket}; -use metadata::{FileFormat, Track, Metadata}; +use metadata::{FileFormat, Metadata, Track}; use mixer::AudioFilter; pub struct Player { @@ -53,10 +53,14 @@ struct NormalisationConfig { } impl Player { - pub fn new(config: PlayerConfig, session: Session, - audio_filter: Option>, - sink_builder: F) -> Player - where F: FnOnce() -> Box + Send + 'static + pub fn new( + config: PlayerConfig, + session: Session, + audio_filter: Option>, + sink_builder: F, + ) -> Player + where + F: FnOnce() -> Box + Send + 'static, { let (cmd_tx, cmd_rx) = std::sync::mpsc::channel(); @@ -87,9 +91,12 @@ impl Player { self.commands.as_ref().unwrap().send(cmd).unwrap(); } - pub fn load(&self, track: SpotifyId, start_playing: bool, position_ms: u32) - -> oneshot::Receiver<()> - { + pub fn load( + &self, + track: SpotifyId, + start_playing: bool, + position_ms: u32, + ) -> oneshot::Receiver<()> { let (tx, rx) = oneshot::channel(); self.command(PlayerCommand::Load(track, start_playing, position_ms, tx)); @@ -120,7 +127,7 @@ impl Drop for Player { if let Some(handle) = self.thread_handle.take() { match handle.join() { Ok(_) => (), - Err(_) => error!("Player thread panicked!") + Err(_) => error!("Player thread panicked!"), } } } @@ -157,8 +164,12 @@ impl PlayerState { use self::PlayerState::*; match *self { Stopped => 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"), } } @@ -166,8 +177,7 @@ impl PlayerState { fn signal_end_of_track(self) { use self::PlayerState::*; match self { - Paused { end_of_track, .. } | - Playing { end_of_track, .. } => { + Paused { end_of_track, .. } | Playing { end_of_track, .. } => { let _ = end_of_track.send(()); } @@ -179,7 +189,11 @@ impl PlayerState { fn paused_to_playing(&mut self) { use self::PlayerState::*; match ::std::mem::replace(self, Invalid) { - Paused { decoder, end_of_track, normalisation_factor } => { + Paused { + decoder, + end_of_track, + normalisation_factor, + } => { *self = Playing { decoder: decoder, end_of_track: end_of_track, @@ -193,7 +207,11 @@ impl PlayerState { fn playing_to_paused(&mut self) { use self::PlayerState::*; match ::std::mem::replace(self, Invalid) { - Playing { decoder, end_of_track, normalisation_factor } => { + Playing { + decoder, + end_of_track, + normalisation_factor, + } => { *self = Paused { decoder: decoder, end_of_track: end_of_track, @@ -209,16 +227,13 @@ impl PlayerInternal { fn run(mut self) { loop { let cmd = if self.state.is_playing() { - if self.sink_running - { + if self.sink_running { match self.commands.try_recv() { Ok(cmd) => Some(cmd), Err(TryRecvError::Empty) => None, Err(TryRecvError::Disconnected) => return, } - } - else - { + } else { match self.commands.recv_timeout(Duration::from_secs(5)) { Ok(cmd) => Some(cmd), Err(RecvTimeoutError::Timeout) => None, @@ -236,14 +251,19 @@ impl PlayerInternal { self.handle_command(cmd); } - if self.state.is_playing() && ! self.sink_running { + if self.state.is_playing() && !self.sink_running { self.start_sink(); } if self.sink_running { let mut current_normalisation_factor: f32 = 1.0; - let packet = if let PlayerState::Playing { ref mut decoder, normalisation_factor, .. } = self.state { + let packet = if let PlayerState::Playing { + ref mut decoder, + normalisation_factor, + .. + } = self.state + { current_normalisation_factor = normalisation_factor; Some(decoder.next_packet().expect("Vorbis error")) } else { @@ -380,22 +400,20 @@ impl PlayerInternal { } } - PlayerCommand::Stop => { - match self.state { - PlayerState::Playing { .. } => { - self.stop_sink_if_running(); - self.run_onstop(); - self.state = PlayerState::Stopped; - } - PlayerState::Paused { .. } => { - self.state = PlayerState::Stopped; - }, - PlayerState::Stopped => { - warn!("Player::stop called from invalid state"); - } - PlayerState::Invalid => panic!("invalid state"), + PlayerCommand::Stop => match self.state { + PlayerState::Playing { .. } => { + self.stop_sink_if_running(); + self.run_onstop(); + self.state = PlayerState::Stopped; } - } + PlayerState::Paused { .. } => { + self.state = PlayerState::Stopped; + } + PlayerState::Stopped => { + warn!("Player::stop called from invalid state"); + } + PlayerState::Invalid => panic!("invalid state"), + }, } } @@ -415,14 +433,16 @@ impl PlayerInternal { if track.available { Some(Cow::Borrowed(track)) } else { - let alternatives = track.alternatives + let alternatives = track + .alternatives .iter() - .map(|alt_id| { - Track::get(&self.session, *alt_id) - }); + .map(|alt_id| Track::get(&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) } } @@ -478,12 +498,19 @@ impl PlayerInternal { let file_id = match track.files.get(&format) { Some(&file_id) => file_id, None => { - warn!("Track \"{}\" is not available in format {:?}", track.name, format); + warn!( + "Track \"{}\" is not available in format {:?}", + track.name, format + ); return None; } }; - let key = self.session.audio_key().request(track.id, file_id).wait().unwrap(); + let key = self.session + .audio_key() + .request(track.id, file_id) + .wait() + .unwrap(); let encrypted_file = AudioFile::open(&self.session, file_id).wait().unwrap(); @@ -493,7 +520,10 @@ impl PlayerInternal { if self.config.normalisation { let normalisation_config = self.parse_normalisation(&mut decrypted_file); - normalisation_factor = f32::powf(10.0, (normalisation_config.track_gain_db + self.config.normalisation_pregain) / 20.0); + normalisation_factor = f32::powf( + 10.0, + (normalisation_config.track_gain_db + self.config.normalisation_pregain) / 20.0, + ); if normalisation_factor * normalisation_config.track_peak > 1.0 { debug!("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid."); @@ -527,27 +557,15 @@ impl Drop for PlayerInternal { impl ::std::fmt::Debug for PlayerCommand { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { - PlayerCommand::Load(track, play, position, _) => { - f.debug_tuple("Load") - .field(&track) - .field(&play) - .field(&position) - .finish() - } - PlayerCommand::Play => { - f.debug_tuple("Play").finish() - } - PlayerCommand::Pause => { - f.debug_tuple("Pause").finish() - } - PlayerCommand::Stop => { - f.debug_tuple("Stop").finish() - } - PlayerCommand::Seek(position) => { - f.debug_tuple("Seek") - .field(&position) - .finish() - } + PlayerCommand::Load(track, play, position, _) => f.debug_tuple("Load") + .field(&track) + .field(&play) + .field(&position) + .finish(), + PlayerCommand::Play => f.debug_tuple("Play").finish(), + PlayerCommand::Pause => f.debug_tuple("Pause").finish(), + PlayerCommand::Stop => f.debug_tuple("Stop").finish(), + PlayerCommand::Seek(position) => f.debug_tuple("Seek").field(&position).finish(), } } } @@ -593,8 +611,8 @@ fn run_program(program: &str) { info!("Running {}", program); let mut v: Vec<&str> = program.split_whitespace().collect(); let status = Command::new(&v.remove(0)) - .args(&v) - .status() - .expect("program failed to start"); + .args(&v) + .status() + .expect("program failed to start"); info!("Exit status: {}", status); } diff --git a/protocol/build.rs b/protocol/build.rs index f8ad050f..4e93ea47 100644 --- a/protocol/build.rs +++ b/protocol/build.rs @@ -7,7 +7,10 @@ fn main() { for &(path, expected_checksum) in files::FILES { let actual = cksum_file(path).unwrap(); if expected_checksum != actual { - panic!("Checksum for {:?} does not match. Try running build.sh", path); + panic!( + "Checksum for {:?} does not match. Try running build.sh", + path + ); } } } @@ -23,83 +26,51 @@ fn cksum_file>(path: T) -> std::io::Result { fn cksum>(data: T) -> u32 { let data = data.as_ref(); - let mut value = 0u32; - for x in data { - value = (value << 8) ^ CRC_LOOKUP_ARRAY[(*x as u32 ^ (value >> 24)) as usize]; - } + let mut value = 0u32; + for x in data { + value = (value << 8) ^ CRC_LOOKUP_ARRAY[(*x as u32 ^ (value >> 24)) as usize]; + } let mut n = data.len(); while n != 0 { - value = (value << 8) ^ CRC_LOOKUP_ARRAY[((n & 0xFF) as u32 ^ (value >> 24)) as usize]; + value = (value << 8) ^ CRC_LOOKUP_ARRAY[((n & 0xFF) as u32 ^ (value >> 24)) as usize]; n >>= 8; } !value } -static CRC_LOOKUP_ARRAY : &'static[u32] = &[ - 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, - 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, - 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, - 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, - 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, - 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, - 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, - 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, - 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, - 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, - 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, - 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, - 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, - 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, - 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, - 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, - 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, - 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, - 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, - 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, - 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, - 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, - 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, - 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, - 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, - 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, - 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, - 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, - 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, - 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, - 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, - 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, - 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, - 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, - 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, - 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, - 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, - 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, - 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, - 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, - 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, - 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, - 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, - 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, - 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, - 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, - 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, - 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, - 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, - 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, - 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, - 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, - 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, - 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, - 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, - 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, - 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, - 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, - 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, - 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, - 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, - 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, - 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, - 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +static CRC_LOOKUP_ARRAY: &'static [u32] = &[ + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4, ]; diff --git a/protocol/files.rs b/protocol/files.rs index ad208d76..ed1b9723 100644 --- a/protocol/files.rs +++ b/protocol/files.rs @@ -1,6 +1,6 @@ // Autogenerated by build.sh -pub const FILES : &'static [(&'static str, u32)] = &[ +pub const FILES: &'static [(&'static str, u32)] = &[ ("proto/authentication.proto", 2098196376), ("proto/keyexchange.proto", 451735664), ("proto/mercury.proto", 709993906), diff --git a/src/lib.rs b/src/lib.rs index 2ba4dac0..2b5e2026 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ #![crate_name = "librespot"] - #![cfg_attr(feature = "cargo-clippy", allow(unused_io_amount))] extern crate base64; @@ -15,6 +14,6 @@ extern crate url; pub extern crate librespot_audio as audio; pub extern crate librespot_connect as connect; pub extern crate librespot_core as core; +pub extern crate librespot_metadata as metadata; pub extern crate librespot_playback as playback; pub extern crate librespot_protocol as protocol; -pub extern crate librespot_metadata as metadata; diff --git a/src/main.rs b/src/main.rs index fa40bd04..cf079fcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,27 @@ -#[macro_use] extern crate log; extern crate env_logger; extern crate futures; extern crate getopts; extern crate librespot; +#[macro_use] +extern crate log; extern crate tokio_core; extern crate tokio_io; extern crate tokio_signal; use env_logger::LogBuilder; -use futures::{Future, Async, Poll, Stream}; +use futures::{Async, Future, Poll, Stream}; use std::env; use std::io::{self, stderr, Write}; use std::path::PathBuf; use std::process::exit; use std::str::FromStr; -use tokio_core::reactor::{Handle, Core}; +use tokio_core::reactor::{Core, Handle}; use tokio_io::IoStream; use std::mem; use librespot::core::authentication::{get_credentials, Credentials}; use librespot::core::cache::Cache; -use librespot::core::config::{DeviceType, SessionConfig, ConnectConfig}; +use librespot::core::config::{ConnectConfig, DeviceType, SessionConfig}; use librespot::core::session::Session; use librespot::core::version; @@ -87,29 +88,86 @@ struct Setup { fn setup(args: &[String]) -> Setup { let mut opts = getopts::Options::new(); - opts.optopt("c", "cache", "Path to a directory where files will be cached.", "CACHE") - .optflag("", "disable-audio-cache", "Disable caching of the audio data.") + opts.optopt( + "c", + "cache", + "Path to a directory where files will be cached.", + "CACHE", + ).optflag( + "", + "disable-audio-cache", + "Disable caching of the audio data.", + ) .reqopt("n", "name", "Device name", "NAME") .optopt("", "device-type", "Displayed device type", "DEVICE_TYPE") - .optopt("b", "bitrate", "Bitrate (96, 160 or 320). Defaults to 160", "BITRATE") - .optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM") - .optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM") + .optopt( + "b", + "bitrate", + "Bitrate (96, 160 or 320). Defaults to 160", + "BITRATE", + ) + .optopt( + "", + "onstart", + "Run PROGRAM when playback is about to begin.", + "PROGRAM", + ) + .optopt( + "", + "onstop", + "Run PROGRAM when playback has ended.", + "PROGRAM", + ) .optflag("v", "verbose", "Enable verbose output") .optopt("u", "username", "Username to sign in with", "USERNAME") .optopt("p", "password", "Password", "PASSWORD") .optflag("", "disable-discovery", "Disable discovery mode") - .optopt("", "backend", "Audio backend to use. Use '?' to list options", "BACKEND") - .optopt("", "device", "Audio device to use. Use '?' to list options if using portaudio", "DEVICE") + .optopt( + "", + "backend", + "Audio backend to use. Use '?' to list options", + "BACKEND", + ) + .optopt( + "", + "device", + "Audio device to use. Use '?' to list options if using portaudio", + "DEVICE", + ) .optopt("", "mixer", "Mixer to use", "MIXER") - .optopt("", "initial-volume", "Initial volume in %, once connected (must be from 0 to 100)", "VOLUME") - .optopt("", "zeroconf-port", "The port the internal server advertised over zeroconf uses.", "ZEROCONF_PORT") - .optflag("", "enable-volume-normalisation", "Play all tracks at the same volume") - .optopt("", "normalisation-pregain", "Pregain (dB) applied by volume normalisation", "PREGAIN"); + .optopt( + "", + "initial-volume", + "Initial volume in %, once connected (must be from 0 to 100)", + "VOLUME", + ) + .optopt( + "", + "zeroconf-port", + "The port the internal server advertised over zeroconf uses.", + "ZEROCONF_PORT", + ) + .optflag( + "", + "enable-volume-normalisation", + "Play all tracks at the same volume", + ) + .optopt( + "", + "normalisation-pregain", + "Pregain (dB) applied by volume normalisation", + "PREGAIN", + ); 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); } }; @@ -117,11 +175,13 @@ fn setup(args: &[String]) -> Setup { let verbose = matches.opt_present("verbose"); setup_logging(verbose); - info!("librespot {} ({}). Built on {}. Build ID: {}", - version::short_sha(), - version::commit_date(), - version::short_now(), - version::build_id()); + info!( + "librespot {} ({}). Built on {}. Build ID: {}", + version::short_sha(), + version::commit_date(), + version::short_now(), + version::build_id() + ); let backend_name = matches.opt_str("backend"); if backend_name == Some("?".into()) { @@ -129,14 +189,12 @@ fn setup(args: &[String]) -> Setup { exit(0); } - let backend = audio_backend::find(backend_name) - .expect("Invalid backend"); + let backend = audio_backend::find(backend_name).expect("Invalid backend"); let device = matches.opt_str("device"); let mixer_name = matches.opt_str("mixer"); - let mixer = mixer::find(mixer_name.as_ref()) - .expect("Invalid mixer"); + let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer"); let initial_volume = matches .opt_str("initial-volume") @@ -149,17 +207,17 @@ fn setup(args: &[String]) -> Setup { }) .unwrap_or(0x8000); - let zeroconf_port = - matches.opt_str("zeroconf-port") - .map(|port| port.parse::().unwrap()) - .unwrap_or(0); + let zeroconf_port = matches + .opt_str("zeroconf-port") + .map(|port| port.parse::().unwrap()) + .unwrap_or(0); let name = matches.opt_str("name").unwrap(); let use_audio_cache = !matches.opt_present("disable-audio-cache"); - let cache = matches.opt_str("c").map(|cache_location| { - Cache::new(PathBuf::from(cache_location), use_audio_cache) - }); + let cache = matches + .opt_str("c") + .map(|cache_location| Cache::new(PathBuf::from(cache_location), use_audio_cache)); let credentials = { let cached_credentials = cache.as_ref().and_then(Cache::credentials); @@ -167,7 +225,7 @@ fn setup(args: &[String]) -> Setup { get_credentials( matches.opt_str("username"), matches.opt_str("password"), - cached_credentials + cached_credentials, ) }; @@ -181,7 +239,9 @@ fn setup(args: &[String]) -> Setup { }; let player_config = { - let bitrate = matches.opt_str("b").as_ref() + let bitrate = matches + .opt_str("b") + .as_ref() .map(|bitrate| Bitrate::from_str(bitrate).expect("Invalid bitrate")) .unwrap_or(Bitrate::default()); @@ -190,14 +250,17 @@ fn setup(args: &[String]) -> Setup { onstart: matches.opt_str("onstart"), onstop: matches.opt_str("onstop"), normalisation: matches.opt_present("enable-volume-normalisation"), - normalisation_pregain: matches.opt_str("normalisation-pregain") + normalisation_pregain: matches + .opt_str("normalisation-pregain") .map(|pregain| pregain.parse::().expect("Invalid pregain float value")) .unwrap_or(PlayerConfig::default().normalisation_pregain), } }; let connect_config = { - let device_type = matches.opt_str("device-type").as_ref() + let device_type = matches + .opt_str("device-type") + .as_ref() .map(|device_type| DeviceType::from_str(device_type).expect("Invalid device type")) .unwrap_or(DeviceType::default()); @@ -239,7 +302,7 @@ struct Main { spirc: Option, spirc_task: Option, - connect: Box>, + connect: Box>, shutdown: bool, }