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
|
||||||
*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
|
||||||
|
|
|
@ -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(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
47
src/spirc.rs
47
src/spirc.rs
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue