Skip to content

Commit

Permalink
Merge branch 'master' into rspotify_id_fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
eladyn authored Mar 6, 2023
2 parents cb15b38 + fbcbeca commit 69eecea
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 20 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ tags

*.rustfmt

.idea
.idea
.vscode
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

> An open source Spotify client running as a UNIX daemon.
[Project Website](https://spotifyd.rs)

Spotifyd streams music just like the official client, but is more lightweight and supports more platforms. Spotifyd also supports the Spotify Connect protocol, which makes it show up as a device that can be controlled from the official clients.

> __Note:__ Spotifyd requires a Spotify Premium account.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/installation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ It is also available as a [snap package](https://snapcraft.io/spotifyd).

You can also compile `Spotifyd` yourself, allowing you to make use of feature flags. `Spotifyd` is written in Rust. You can download the toolchain (compiler and package manager) over at [rustup.rs](https://rustup.rs). Follow their instructions to get started.

> __Note:__ Please make sure that you compile the package using the most recent `stable` verison of Rust available throug `rustup`. Some distro versions are quite outdated and might result in compilation errors.
> __Note:__ Please make sure that you compile the package using the most recent `stable` version of Rust available through `rustup`. Some distro versions are quite outdated and might result in compilation errors.
`Spotifyd` might require additional libraries during build and runtime, depending on your platform and the way to compile it (static or dynamic). The following table shows the libraries needed for each OS respectively.

Expand Down
2 changes: 1 addition & 1 deletion docs/src/installation/Raspberry-Pi.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This guide will help you to install `spotifyd` on a Raspberry Pi and have it alw

## Download

1. Download the latest ARMv6 from https://github.com/Spotifyd/spotifyd/releases (use `wget`)
1. Download the latest ARMv6 from <https://github.com/Spotifyd/spotifyd/releases> (use `wget`)
2. Unzip the file: `tar xzf spotifyd-linux-arm6*`
You will now see a file called `spotifyd`. You can run it with `./spotifyd --no-daemon`

Expand Down
2 changes: 1 addition & 1 deletion docs/src/other/User-supplied-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ This script will show a dunst notification when you play/change/stop Spotify (an
echo "Cannot get token."
fi
else
echo "Unkown event."
echo "Unknown event."
fi
```

Expand Down
21 changes: 20 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ use url::Url;

const CONFIG_FILE_NAME: &str = "spotifyd.conf";

#[cfg(not(any(
feature = "pulseaudio_backend",
feature = "portaudio_backend",
feature = "alsa_backend",
feature = "rodio_backend"
)))]
compile_error!("At least one of the backend features is required!");
static BACKEND_VALUES: &[&str] = &[
#[cfg(feature = "alsa_backend")]
"alsa",
Expand All @@ -39,6 +46,10 @@ pub enum Backend {
Rodio,
}

fn default_backend() -> Backend {
return Backend::from_str(BACKEND_VALUES.first().unwrap()).unwrap();
}

impl FromStr for Backend {
type Err = ParseError;

Expand Down Expand Up @@ -649,7 +660,7 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
let backend = config
.shared_config
.backend
.unwrap_or(Backend::Alsa)
.unwrap_or_else(default_backend)
.to_string();

let volume_controller = config
Expand Down Expand Up @@ -812,4 +823,12 @@ mod tests {
spotifyd_section.username = Some("testUserName".to_string());
assert_eq!(merged_config, spotifyd_section);
}
#[test]
fn test_default_backend() {
let spotifyd_config = get_internal_config(CliConfig::default());
assert_eq!(
spotifyd_config.backend.unwrap(),
default_backend().to_string()
);
}
}
85 changes: 71 additions & 14 deletions src/dbus_mpris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use librespot_core::{
spotify_id::SpotifyAudioType,
};
use librespot_playback::player::PlayerEvent;
use log::{error, info};
use log::{error, info, warn};
use rspotify::{
model::{
offset::Offset, parse_uri, AlbumId, ArtistId, EpisodeId, IdError, PlayableItem, PlaylistId,
Expand Down Expand Up @@ -234,12 +234,14 @@ async fn create_dbus_server(
b.method("VolumeUp", (), (), move |_, _, (): ()| {
local_spirc.volume_up();
Ok(())
});
})
.deprecated();
let local_spirc = spirc.clone();
b.method("VolumeDown", (), (), move |_, _, (): ()| {
local_spirc.volume_down();
Ok(())
});
})
.deprecated();
let local_spirc = spirc.clone();
b.method("Next", (), (), move |_, _, (): ()| {
local_spirc.next();
Expand Down Expand Up @@ -367,30 +369,22 @@ async fn create_dbus_server(

let id = uri_to_id(&uri).map_err(|e| MethodErr::invalid_arg(&e))?;

let device_id = sp_client.device().ok().and_then(|devices| {
devices.into_iter().find_map(|d| {
if d.is_active && d.name == mv_device_name {
Some(d.id)
} else {
None
}
})
});
let device_id = get_device_id(&sp_client, &mv_device_name, true);

if let Some(device_id) = device_id {
match id {
AnyId::Playable(id) => {
let _ = sp_client.start_uris_playback(
Some(id),
device_id.as_deref(),
Some(&device_id),
Some(Offset::Position(0)),
None,
);
}
AnyId::Context(id) => {
let _ = sp_client.start_context_playback(
id,
device_id.as_deref(),
Some(&device_id),
Some(Offset::Position(0)),
None,
);
Expand Down Expand Up @@ -516,12 +510,53 @@ async fn create_dbus_server(
}
});

let spotifyd_ctrls_interface: IfaceToken<()> =
cr.register("io.github.spotifyd.Controls", |b| {
let local_spirc = spirc.clone();
b.method("VolumeUp", (), (), move |_, _, (): ()| {
local_spirc.volume_up();
Ok(())
});
let local_spirc = spirc.clone();
b.method("VolumeDown", (), (), move |_, _, (): ()| {
local_spirc.volume_down();
Ok(())
});

let mv_device_name = device_name.clone();
let sp_client = Arc::clone(&spotify_api_client);
b.method("TransferPlayback", (), (), move |_, _, (): ()| {
let device_id = get_device_id(&sp_client, &mv_device_name, false);
if let Some(device_id) = device_id {
info!("Transferring playback to device {}", device_id);
match sp_client.transfer_playback(&device_id, Some(true)) {
Ok(_) => Ok(()),
Err(err) => {
let e = format!("TransferPlayback failed: {}", err);
error!("{}", e);
Err(MethodErr::failed(&e))
}
}
} else {
let msg = format!("Could not find device with name {}", mv_device_name);
warn!("TransferPlayback: {}", msg);
Err(MethodErr::failed(&msg))
}
});
});

cr.insert(
"/org/mpris/MediaPlayer2",
&[media_player2_interface, player_interface],
(),
);

cr.insert(
"/io/github/spotifyd/Controls",
&[spotifyd_ctrls_interface],
(),
);

conn.start_receive(
MatchRule::new_method_call(),
Box::new(move |msg, conn| {
Expand Down Expand Up @@ -651,6 +686,28 @@ async fn create_dbus_server(
}
}

fn get_device_id(
sp_client: &AuthCodeSpotify,
device_name: &str,
only_active: bool,
) -> Option<String> {
let device_result = sp_client.device();
match device_result {
Ok(devices) => devices.into_iter().find_map(|d| {
if d.name == device_name && (d.is_active || !only_active) {
info!("Found device: {}, active: {}", d.name, d.is_active);
d.id
} else {
None
}
}),
Err(err) => {
error!("Get devices error: {}", err);
None
}
}
}

fn uri_to_object_path(uri: String) -> dbus::Path<'static> {
let mut path = String::with_capacity(uri.len() + 1);
for element in uri.split(':') {
Expand Down
2 changes: 1 addition & 1 deletion src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fn get_shell_ffi() -> Option<String> {

let username = whoami::username();
let output = Command::new("dscl")
.args(&[".", "-read", &format!("/Users/{}", username), "UserShell"])
.args([".", "-read", &format!("/Users/{}", username), "UserShell"])
.output()
.ok()?;

Expand Down

0 comments on commit 69eecea

Please # to comment.