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 Sep 9, 2022
1 parent 7146f64 commit 7f249ff
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 7 deletions.
15 changes: 12 additions & 3 deletions docs/src/config/File.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +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 music. To get
# a list of possible backends, run `spotifyd --help`.
backend = "alsa" # use portaudio for macOS [homebrew]
Expand Down
47 changes: 46 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,14 @@ 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), serde(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 @@ -446,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 @@ -527,7 +568,8 @@ impl SharedConfigValues {
proxy,
device_type,
use_mpris,
max_cache_size
max_cache_size,
dbus_type
);

// Handles boolean merging.
Expand Down Expand Up @@ -562,6 +604,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 @@ -642,6 +685,7 @@ pub(crate) fn get_internal_config(config: CliConfig) -> SpotifydConfig {

let normalisation_pregain = config.shared_config.normalisation_pregain.unwrap_or(0.0f32);

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

let device_type = config
Expand Down Expand Up @@ -719,6 +763,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
14 changes: 11 additions & 3 deletions src/dbus_mpris.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::config::DBusType;
use chrono::{prelude::*, Duration};
use dbus::{
arg::{RefArg, Variant},
Expand Down Expand Up @@ -36,6 +37,7 @@ pub struct DbusServer {
session: Session,
spirc: Arc<Spirc>,
spotify_client: Arc<AuthCodeSpotify>,
dbus_type: DBusType,
#[allow(clippy::type_complexity)]
token_request: Option<Pin<Box<dyn Future<Output = Result<LibrespotToken, MercuryError>>>>>,
dbus_future: Option<Pin<Box<dyn Future<Output = ()>>>>,
Expand All @@ -54,11 +56,13 @@ impl DbusServer {
spirc: Arc<Spirc>,
device_name: String,
event_rx: UnboundedReceiver<PlayerEvent>,
dbus_type: DBusType,
) -> DbusServer {
DbusServer {
session,
spirc,
spotify_client: Default::default(),
dbus_type,
token_request: None,
dbus_future: None,
device_name,
Expand Down Expand Up @@ -112,6 +116,7 @@ impl Future for DbusServer {
self.spirc.clone(),
self.device_name.clone(),
rx,
self.dbus_type,
)));
} else {
*self.spotify_client.get_token().lock().unwrap() = Some(api_token);
Expand Down Expand Up @@ -155,10 +160,13 @@ async fn create_dbus_server(
spirc: Arc<Spirc>,
device_name: String,
mut event_rx: UnboundedReceiver<PlayerEvent>,
dbus_type: DBusType,
) {
// TODO: allow other DBus types through CLI and config entry.
let (resource, conn) =
connection::new_session_sync().expect("Failed to initialize DBus connection");
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
4 changes: 4 additions & 0 deletions src/main_loop.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::config::DBusType;
#[cfg(feature = "dbus_mpris")]
use crate::dbus_mpris::DbusServer;
use crate::process::spawn_program_on_event;
Expand Down Expand Up @@ -81,6 +82,8 @@ pub(crate) struct MainLoop {
pub(crate) device_type: DeviceType,
#[cfg_attr(not(feature = "dbus_mpris"), allow(unused))]
pub(crate) use_mpris: bool,
#[cfg_attr(not(feature = "dbus_mpris"), allow(unused))]
pub(crate) dbus_type: DBusType,
pub(crate) credentials_provider: CredentialsProvider,
}

Expand Down Expand Up @@ -156,6 +159,7 @@ impl MainLoop {
shared_spirc.clone(),
self.spotifyd_state.device_name.clone(),
rx,
self.dbus_type,
));
Some(tx)
} else {
Expand Down
1 change: 1 addition & 0 deletions src/setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,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 7f249ff

Please sign in to comment.