Skip to content

Commit

Permalink
add config option for bus type for MPRIS
Browse files Browse the repository at this point in the history
Add a configuration option that determines if the MPRIS interface
should use the session or the system bus. The default is the
session bus.
  • Loading branch information
eladyn committed Dec 12, 2021
1 parent 603bcc4 commit bfceb56
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 12 deletions.
17 changes: 12 additions & 5 deletions docs/src/config/File.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ password_cmd = "command_that_writes_password_to_stdout"
# can't be used simultaneously.
use_keyring = true

#
# If set to true, `spotifyd` tries to bind to the session dbus
# and expose MPRIS controls. When running headless, without a dbus session,
# then set this to false to avoid binding errors
#
# If set to true, `spotifyd` tries to bind to dbus (default is the session bus)
# and expose MPRIS controls. When running headless, without the session bus,
# you should set this to false, to avoid errors. If you still want to use MPRIS,
# have a look at the `dbus_type` option.
use_mpris = true

# The bus to bind to with the MPRIS interface.
# Possible values: "session", "system"
# The system bus can be used if no graphical session is available
# (e.g. on headless systems) but you still want to be able to use MPRIS.
# NOTE: You might need to add appropriate policies to allow spotifyd to
# own the name.
dbus_type = "session"

# The audio backend used to play the your music. To get
# a list of possible backends, run `spotifyd --help`.
backend = "alsa" # use portaudio for macOS [homebrew]
Expand Down
55 changes: 54 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,37 @@ impl From<Bitrate> for LSBitrate {
}
}

#[cfg(feature = "dbus_mpris")]
static DBUSTYPE_VALUES: &[&str] = &["session", "system"];

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, StructOpt)]
#[serde(rename_all = "snake_case")]
pub enum DBusType {
Session,
System
}

impl FromStr for DBusType {
type Err = ParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"session" => Ok(DBusType::Session),
"system" => Ok(DBusType::System),
_ => unreachable!(),
}
}
}

impl ToString for DBusType {
fn to_string(&self) -> String {
match self {
DBusType::Session => "session".to_string(),
DBusType::System => "system".to_string(),
}
}
}

#[derive(Debug, Default, StructOpt)]
#[structopt(
about = "A Spotify daemon",
Expand Down Expand Up @@ -280,6 +311,7 @@ pub struct SharedConfigValues {
#[cfg_attr(not(feature = "dbus_keyring"), structopt(skip), serde(skip))]
use_keyring: bool,

/// Enables the MPRIS interface
#[cfg_attr(
feature = "dbus_mpris",
structopt(long),
Expand All @@ -288,6 +320,18 @@ pub struct SharedConfigValues {
#[cfg_attr(not(feature = "dbus_mpris"), structopt(skip), serde(skip))]
use_mpris: Option<bool>,

/// The Bus-type to use for the MPRIS interface
#[cfg_attr(
feature = "dbus_mpris",
structopt(
long,
possible_values = &DBUSTYPE_VALUES,
value_name = "string",
)
)]
#[cfg_attr(not(feature = "dbus_mpris"), structopt(skip))]
dbus_type: Option<DBusType>,

/// A command that can be used to retrieve the Spotify account password
#[structopt(
conflicts_with = "password",
Expand Down Expand Up @@ -442,6 +486,7 @@ impl fmt::Debug for SharedConfigValues {
.field("password_cmd", &password_cmd_value)
.field("use_keyring", &self.use_keyring)
.field("use_mpris", &self.use_mpris)
.field("dbus_type", &self.dbus_type)
.field("on_song_change_hook", &self.on_song_change_hook)
.field("cache_path", &self.cache_path)
.field("no-audio-cache", &self.no_audio_cache)
Expand Down Expand Up @@ -520,7 +565,8 @@ impl SharedConfigValues {
zeroconf_port,
proxy,
device_type,
use_mpris
use_mpris,
dbus_type
);

// Handles boolean merging.
Expand Down Expand Up @@ -554,6 +600,7 @@ pub(crate) struct SpotifydConfig {
#[allow(unused)]
pub(crate) use_keyring: bool,
pub(crate) use_mpris: bool,
pub(crate) dbus_type: DBusType,
pub(crate) cache: Option<Cache>,
pub(crate) backend: Option<String>,
pub(crate) audio_device: Option<String>,
Expand Down Expand Up @@ -643,6 +690,11 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
.unwrap_or(DeviceType::Speaker)
.to_string();

let dbus_type: DBusType = config
.shared_config
.dbus_type
.unwrap_or(DBusType::Session);

let pid = config.pid.map(|f| {
f.into_os_string()
.into_string()
Expand Down Expand Up @@ -712,6 +764,7 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {
password,
use_keyring: config.shared_config.use_keyring,
use_mpris: config.shared_config.use_mpris.unwrap_or(true),
dbus_type,
cache,
backend: Some(backend),
audio_device: config.shared_config.device,
Expand Down
15 changes: 10 additions & 5 deletions src/dbus_mpris.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use librespot_core::{
mercury::MercuryError,
session::Session,
};
use crate::config::DBusType;
use log::info;
use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use rspotify::spotify::{
Expand All @@ -25,6 +26,7 @@ use std::{collections::HashMap, env};
pub struct DbusServer {
session: Session,
spirc: Arc<Spirc>,
dbus_type: DBusType,
api_token: RspotifyToken,
#[allow(clippy::type_complexity)]
token_request: Option<Pin<Box<dyn Future<Output = Result<LibrespotToken, MercuryError>>>>>,
Expand All @@ -41,10 +43,11 @@ const SCOPE: &str = "user-read-playback-state,user-read-private,\
user-read-recently-played";

impl DbusServer {
pub fn new(session: Session, spirc: Arc<Spirc>, device_name: String) -> DbusServer {
pub fn new(session: Session, spirc: Arc<Spirc>, device_name: String, dbus_type: DBusType) -> DbusServer {
DbusServer {
session,
spirc,
dbus_type,
api_token: RspotifyToken::default(),
token_request: None,
dbus_future: None,
Expand Down Expand Up @@ -77,6 +80,7 @@ impl Future for DbusServer {
self.api_token.clone(),
self.spirc.clone(),
self.device_name.clone(),
self.dbus_type,
)));
// TODO: for reasons I don't _entirely_ understand, the token request completing
// convinces callers that they don't need to re-check the status of this future
Expand Down Expand Up @@ -110,10 +114,11 @@ fn create_spotify_api(token: &RspotifyToken) -> Spotify {
Spotify::default().access_token(&token.access_token).build()
}

async fn create_dbus_server(api_token: RspotifyToken, spirc: Arc<Spirc>, device_name: String) {
// TODO: allow other DBus types through CLI and config entry.
let (resource, conn) =
connection::new_session_sync().expect("Failed to initialize DBus connection");
async fn create_dbus_server(api_token: RspotifyToken, spirc: Arc<Spirc>, device_name: String, dbus_type: DBusType) {
let (resource, conn) = match dbus_type {
DBusType::Session => connection::new_session_sync(),
DBusType::System => connection::new_system_sync(),
}.expect("Failed to initialize DBus connection");
tokio::spawn(async {
let err = resource.await;
panic!("Lost connection to D-Bus: {}", err);
Expand Down
7 changes: 6 additions & 1 deletion src/main_loop.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#[cfg(feature = "dbus_mpris")]
use crate::dbus_mpris::DbusServer;
use crate::process::{spawn_program_on_event, Child};
use crate::config::DBusType;
use futures::{self, Future, Stream, StreamExt};
use librespot_connect::{discovery::DiscoveryStream, spirc::Spirc};
use librespot_core::session::SessionError;
Expand Down Expand Up @@ -67,15 +68,17 @@ fn new_dbus_server(
session: Session,
spirc: Arc<Spirc>,
device_name: String,
dbus_type: DBusType,
) -> Option<Pin<Box<dyn Future<Output = ()>>>> {
Some(Box::pin(DbusServer::new(session, spirc, device_name)))
Some(Box::pin(DbusServer::new(session, spirc, device_name, dbus_type)))
}

#[cfg(not(feature = "dbus_mpris"))]
fn new_dbus_server(
_: Session,
_: Arc<Spirc>,
_: String,
_: DBusType,
) -> Option<Pin<Box<dyn Future<Output = ()>>>> {
None
}
Expand All @@ -93,6 +96,7 @@ pub(crate) struct MainLoopState {
pub(crate) shell: String,
pub(crate) device_type: DeviceType,
pub(crate) use_mpris: bool,
pub(crate) dbus_type: DBusType,
}

impl Future for MainLoopState {
Expand Down Expand Up @@ -184,6 +188,7 @@ impl Future for MainLoopState {
session,
shared_spirc,
self.spotifyd_state.device_name.clone(),
self.dbus_type,
);
}
} else if self
Expand Down
1 change: 1 addition & 0 deletions src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ pub(crate) fn initial_state(config: config::SpotifydConfig) -> main_loop::MainLo
device_type,
autoplay,
use_mpris: config.use_mpris,
dbus_type: config.dbus_type,
}
}

Expand Down

0 comments on commit bfceb56

Please sign in to comment.