merge ColmOnline changes

This commit is contained in:
fossedihelm 2017-12-07 10:04:43 +01:00
commit 94e4cd853a
3 changed files with 102 additions and 40 deletions

View file

@ -1,16 +1,22 @@
[![Build Status](https://travis-ci.org/ComlOnline/librespot.svg?branch=master)](https://travis-ci.org/ComlOnline/librespot)
# librespot # librespot
*librespot* is an open source client library for Spotify. It enables *librespot* is an open source client library for Spotify. It enables
applications to use Spotify's service, without using the official but applications to use Spotify's service, without using the official but
closed-source libspotify. Additionally, it will provide extra features closed-source libspotify. Additionally, it will provide extra features
which are not available in the official library. which are not available in the official library.
Note: librespot only works with Spotify Premium. Note: librespot needs to be logged in and only works with Spotify Premium
# Unmaintained # THIS FORK
Unfortunately I am unable to maintain librespot anymore. It should still work, As the origin is no longer maintained I wanted to have a place for a version of librespot with other peoples forks and features merged.
but issues and Pull requests will be ignored. Feel free to fork it and continue
development there. If a fork gains traction I will happily point to it from the
README. # THANKS
I've done noting more than make this pretty so big thanks to:
[plietar](https://github.com/plietar/) for making the thing in the first place.
[kingosticks](https://github.com/kingosticks/) For the Suffling and Repeat.
[ipha](https://github.com/ipha/) For the start stop audio sink.
## Building ## Building
Rust 1.17.0 or later is required to build librespot. Rust 1.17.0 or later is required to build librespot.
@ -46,9 +52,27 @@ Once you've built *librespot*, run it using :
target/release/librespot --username USERNAME --cache CACHEDIR --name DEVICENAME [--initial-volume 20] target/release/librespot --username USERNAME --cache CACHEDIR --name DEVICENAME [--initial-volume 20]
``` ```
## Discovery mode ### All options
*librespot* can be run in discovery mode, in which case no password is required at startup.
For that, simply omit the `--username` argument. | Type | Short | Long | Description | Hint |
|----------|-------|---------------------|-------------------------------------------------|-------------|
| Option | c | cache | Path to a directory where files will be cached. | CACHE |
| Flag | | disable-audio-cache | Disable caching of the audio data. | |
| Required | n | name | Device name | NAME |
| Option | | device-type | Displayed device type | DEVICE_TYPE |
| Option | b | bitrate | Bitrate (96, 160 or 320). Defaults to 160 | BITRATE |
| Option | | onstart | Run PROGRAM when playback is about to begin. | |
| Option | | onstop | Run PROGRAM when playback has ended. | PROGRAM |
| Flag | v | verbose | Enable verbose output | PROGRAM |
| Option | u | username | Username to sign in with | USERNAME |
| Option | p | password | Password | PASSWORD |
| Flag | | disable-discovery | Disable discovery mode | |
| Option | | backend | Audio backend to use. Use '?' to list options | BACKEND |
| Option | | device | Audio device to use. Use '?' to list options | DEVICE |
| Option | | mixer | Mixer to use | MIXER |
Taken from here:
https://github.com/ComlOnline/librespot/blob/master/src/main.rs#L88
## Audio Backends ## Audio Backends
*librespot* supports various audio backends. Multiple backends can be enabled at compile time by enabling the *librespot* supports various audio backends. Multiple backends can be enabled at compile time by enabling the

View file

@ -5,7 +5,12 @@ use std::ptr::{null, null_mut};
use std::mem::{transmute}; use std::mem::{transmute};
use std::ffi::CString; use std::ffi::CString;
pub struct PulseAudioSink(*mut pa_simple); pub struct PulseAudioSink {
s : *mut pa_simple,
ss : pa_sample_spec,
name : CString,
desc : CString
}
impl Open for PulseAudioSink { impl Open for PulseAudioSink {
fn open(device: Option<String>) -> PulseAudioSink { fn open(device: Option<String>) -> PulseAudioSink {
@ -24,30 +29,40 @@ impl Open for PulseAudioSink {
let name = CString::new("librespot").unwrap(); let name = CString::new("librespot").unwrap();
let description = CString::new("A spoty client library").unwrap(); let description = CString::new("A spoty client library").unwrap();
let s = unsafe { PulseAudioSink {
pa_simple_new(null(), // Use the default server. s: null_mut(),
name.as_ptr(), // Our application's name. ss: ss,
PA_STREAM_PLAYBACK, name: name,
null(), // Use the default device. desc: description
description.as_ptr(), // Description of our stream. }
&ss, // Our sample format.
null(), // Use default channel map
null(), // Use default buffering attributes.
null_mut(), // Ignore error code.
)
};
assert!(s != null_mut());
PulseAudioSink(s)
} }
} }
impl Sink for PulseAudioSink { impl Sink for PulseAudioSink {
fn start(&mut self) -> io::Result<()> { fn start(&mut self) -> io::Result<()> {
if self.s == null_mut() {
self.s = 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.
null_mut(), // Ignore error code.
)
};
assert!(self.s != null_mut());
}
Ok(()) Ok(())
} }
fn stop(&mut self) -> io::Result<()> { fn stop(&mut self) -> io::Result<()> {
unsafe {
pa_simple_free(self.s);
}
self.s = null_mut();
Ok(()) Ok(())
} }
@ -55,13 +70,9 @@ impl Sink for PulseAudioSink {
unsafe { unsafe {
let ptr = transmute(data.as_ptr()); let ptr = transmute(data.as_ptr());
let bytes = data.len() as usize * 2; let bytes = data.len() as usize * 2;
pa_simple_write(self.0, ptr, bytes, null_mut()); pa_simple_write(self.s, ptr, bytes, null_mut());
}; };
Ok(()) Ok(())
} }
} }

View file

@ -17,6 +17,9 @@ use protocol::spirc::{PlayStatus, State, MessageType, Frame, DeviceState};
use mixer::Mixer; use mixer::Mixer;
use player::Player; use player::Player;
use rand;
use rand::Rng;
pub struct SpircTask { pub struct SpircTask {
player: Player, player: Player,
mixer: Box<Mixer>, mixer: Box<Mixer>,
@ -396,6 +399,31 @@ impl SpircTask {
self.notify(None); self.notify(None);
} }
MessageType::kMessageTypeRepeat => {
self.state.set_repeat(frame.get_state().get_repeat());
self.notify(None);
}
MessageType::kMessageTypeShuffle => {
self.state.set_shuffle(frame.get_state().get_shuffle());
if self.state.get_shuffle()
{
let current_index = self.state.get_playing_track_index();
{
let tracks = self.state.mut_track();
tracks.swap(0, current_index as usize);
if let Some((_, rest)) = tracks.split_first_mut() {
rand::thread_rng().shuffle(rest);
}
}
self.state.set_playing_track_index(0);
} else {
let context = self.state.get_context_uri();
debug!("{:?}", context);
}
self.notify(None);
}
MessageType::kMessageTypeSeek => { MessageType::kMessageTypeSeek => {
let position = frame.get_position(); let position = frame.get_position();
@ -467,13 +495,19 @@ impl SpircTask {
fn handle_next(&mut self) { fn handle_next(&mut self) {
let current_index = self.state.get_playing_track_index(); let current_index = self.state.get_playing_track_index();
let new_index = (current_index + 1) % (self.state.get_track().len() as u32); let num_tracks = self.state.get_track().len() as u32;
let new_index = (current_index + 1) % num_tracks;
let mut was_last_track = (current_index + 1) >= num_tracks;
if self.state.get_repeat() {
was_last_track = false;
}
self.state.set_playing_track_index(new_index); self.state.set_playing_track_index(new_index);
self.state.set_position_ms(0); self.state.set_position_ms(0);
self.state.set_position_measured_at(now_ms() as u64); self.state.set_position_measured_at(now_ms() as u64);
self.load_track(true); self.load_track(!was_last_track);
} }
fn handle_prev(&mut self) { fn handle_prev(&mut self) {
@ -520,14 +554,7 @@ impl SpircTask {
} }
fn handle_end_of_track(&mut self) { fn handle_end_of_track(&mut self) {
let current_index = self.state.get_playing_track_index(); self.handle_next();
let new_index = (current_index + 1) % (self.state.get_track().len() as u32);
self.state.set_playing_track_index(new_index);
self.state.set_position_ms(0);
self.state.set_position_measured_at(now_ms() as u64);
self.load_track(true);
self.notify(None); self.notify(None);
} }