mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
merge ColmOnline changes
This commit is contained in:
commit
94e4cd853a
3 changed files with 102 additions and 40 deletions
42
README.md
42
README.md
|
@ -1,16 +1,22 @@
|
|||
[![Build Status](https://travis-ci.org/ComlOnline/librespot.svg?branch=master)](https://travis-ci.org/ComlOnline/librespot)
|
||||
|
||||
# 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.
|
||||
|
||||
Note: librespot only works with Spotify Premium.
|
||||
Note: librespot needs to be logged in and only works with Spotify Premium
|
||||
|
||||
# Unmaintained
|
||||
Unfortunately I am unable to maintain librespot anymore. It should still work,
|
||||
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.
|
||||
# THIS FORK
|
||||
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.
|
||||
|
||||
|
||||
# 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
|
||||
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]
|
||||
```
|
||||
|
||||
## Discovery mode
|
||||
*librespot* can be run in discovery mode, in which case no password is required at startup.
|
||||
For that, simply omit the `--username` argument.
|
||||
### All options
|
||||
|
||||
| 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
|
||||
*librespot* supports various audio backends. Multiple backends can be enabled at compile time by enabling the
|
||||
|
|
|
@ -5,7 +5,12 @@ use std::ptr::{null, null_mut};
|
|||
use std::mem::{transmute};
|
||||
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 {
|
||||
fn open(device: Option<String>) -> PulseAudioSink {
|
||||
|
@ -24,30 +29,40 @@ impl Open for PulseAudioSink {
|
|||
let name = CString::new("librespot").unwrap();
|
||||
let description = CString::new("A spoty client library").unwrap();
|
||||
|
||||
let s = unsafe {
|
||||
pa_simple_new(null(), // Use the default server.
|
||||
name.as_ptr(), // Our application's name.
|
||||
PA_STREAM_PLAYBACK,
|
||||
null(), // Use the default device.
|
||||
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)
|
||||
PulseAudioSink {
|
||||
s: null_mut(),
|
||||
ss: ss,
|
||||
name: name,
|
||||
desc: description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sink for PulseAudioSink {
|
||||
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(())
|
||||
}
|
||||
|
||||
fn stop(&mut self) -> io::Result<()> {
|
||||
unsafe {
|
||||
pa_simple_free(self.s);
|
||||
}
|
||||
self.s = null_mut();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -55,13 +70,9 @@ impl Sink for PulseAudioSink {
|
|||
unsafe {
|
||||
let ptr = transmute(data.as_ptr());
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
47
src/spirc.rs
47
src/spirc.rs
|
@ -17,6 +17,9 @@ use protocol::spirc::{PlayStatus, State, MessageType, Frame, DeviceState};
|
|||
use mixer::Mixer;
|
||||
use player::Player;
|
||||
|
||||
use rand;
|
||||
use rand::Rng;
|
||||
|
||||
pub struct SpircTask {
|
||||
player: Player,
|
||||
mixer: Box<Mixer>,
|
||||
|
@ -396,6 +399,31 @@ impl SpircTask {
|
|||
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 => {
|
||||
let position = frame.get_position();
|
||||
|
||||
|
@ -467,13 +495,19 @@ impl SpircTask {
|
|||
|
||||
fn handle_next(&mut self) {
|
||||
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_position_ms(0);
|
||||
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) {
|
||||
|
@ -520,14 +554,7 @@ impl SpircTask {
|
|||
}
|
||||
|
||||
fn handle_end_of_track(&mut self) {
|
||||
let current_index = self.state.get_playing_track_index();
|
||||
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.handle_next();
|
||||
self.notify(None);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue