Skip to content

Commit

Permalink
Added volume subcommand.
Browse files Browse the repository at this point in the history
  • Loading branch information
ray-kast committed Feb 28, 2022
1 parent 4677e99 commit bafc9d4
Show file tree
Hide file tree
Showing 8 changed files with 367 additions and 234 deletions.
318 changes: 157 additions & 161 deletions Cargo.lock

Large diffs are not rendered by default.

23 changes: 12 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ keywords = ["mpris", "music", "play", "pause", "skip"]
categories = ["command-line-utilities", "multimedia", "os"]

[dependencies]
anyhow = "1.0.38"
anyhow = "1.0.55"
atty = "0.2.14"
dbus = "0.9.2"
dbus-crossroads = "0.3.0"
dbus-tokio = "0.7.3"
env_logger = "0.8.3"
futures = "0.3.13"
clap = { version = "3.1.2", features = ["derive"] }
dbus = "0.9.5"
dbus-crossroads = "0.5.0"
dbus-tokio = "0.7.5"
env_logger = "0.9.0"
futures = "0.3.21"
lazy_static = "1.4.0"
log = "0.4.14"
regex = "1.4.4"
serde = { version = "1.0.130", features = ["derive"] }
serde_json = "1.0.70"
structopt = "0.3.21"
tokio = { version = "1.2.0", features = ["macros", "rt", "signal", "sync"] }
regex = "1.5.4"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
strum = { version = "0.24.0", features = ["derive"] }
tokio = { version = "1.17.0", features = ["macros", "rt", "signal", "sync"] }
14 changes: 13 additions & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,19 @@ pub(super) async fn run(cmd: ClientCommand) -> Result {
player: PlayerOpts {},
to,
} => {
try_send(&proxy, id, (to.pos(),)).await?;
try_send(&proxy, id, (to.offset(),)).await?;
},
ClientCommand::Volume {
player: PlayerOpts {},
vol,
} => {
let (vol,): (f64,) = try_send(&proxy, id, (vol.offset(),)).await?;

print!("{}", vol);

if atty::is(atty::Stream::Stdout) {
println!();
}
},
ClientCommand::SwitchCurrent { to, no_play } => {
let switch_playing = !no_play;
Expand Down
124 changes: 74 additions & 50 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ use std::{
};

use anyhow::{anyhow, Context, Error};
use clap::Parser;
use lazy_static::lazy_static;
use log::{error, LevelFilter};
use structopt::StructOpt;
use tokio::runtime::Builder as RtBuilder;

mod client;
Expand All @@ -45,7 +45,7 @@ lazy_static! {
static ref SERVER_PATH: String = format!("{}/Daemon", *PATH_PREFIX);
}

#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, strum::Display)]
enum MethodId {
/// List the players currently tracked by the daemon
ListPlayers,
Expand All @@ -67,43 +67,29 @@ enum MethodId {
SeekRelative,
/// Seek to an absolute position on a player
SeekAbsolute,
/// Set a player's volume relative to its current volume
VolRelative,
/// Set a player's volume to an absolute value
VolAbsolute,
/// Move a player to the top of the priority list, optionally pausing all
/// other players and playing the selected one
SwitchCurrent,
}

impl Display for MethodId {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str(match self {
Self::ListPlayers => "ListPlayers",
Self::Next => "Next",
Self::NowPlaying => "NowPlaying",
Self::Previous => "Previous",
Self::Pause => "Pause",
Self::PlayPause => "PlayPause",
Self::Stop => "Stop",
Self::Play => "Play",
Self::SeekRelative => "SeekRelative",
Self::SeekAbsolute => "SeekAbsolute",
Self::SwitchCurrent => "SwitchCurrent",
})
}
}

#[derive(StructOpt)]
#[derive(Parser)]
enum Opts {
/// Launch a D-Bus service abstracting MPRIS players
Server {
/// Disable info logs (enabled by default if stderr is not a TTY)
#[structopt(short, long)]
#[clap(short, long)]
quiet: bool,

/// Enable info logs, even if stderr is not a TTY
#[structopt(long, conflicts_with("quiet"))]
#[clap(long, conflicts_with("quiet"))]
no_quiet: bool,

/// Output extra information to the console
#[structopt(
#[clap(
short,
long,
parse(from_occurrences),
Expand All @@ -112,11 +98,11 @@ enum Opts {
)]
verbose: usize,
},
#[structopt(flatten)]
#[clap(flatten)]
Client(ClientCommand),
}

#[derive(Debug, Clone, StructOpt)]
#[derive(Debug, Clone, Parser)]
enum ClientCommand {
/// List the players currently tracked by the daemon
ListPlayers,
Expand All @@ -136,21 +122,37 @@ enum ClientCommand {
Play(PlayerOpts),
/// Seek to a position on a player
Seek {
#[structopt(flatten)]
#[clap(flatten)]
player: PlayerOpts,

/// The position to seek to, either absolute (e.g. 5) or relative (e.g.
/// +5 or -5)
to: SeekPosition,
/// 5+ or 5-)
to: Offset,
},
/// Get or set the volume on a player
Volume {
#[clap(flatten)]
player: PlayerOpts,

/// The volume as a number between 0.0 and 1.0, either absolute (e.g.
/// 0.5) or relative (e.g. 0.1+ or 0.1-). If no value is given
/// the current volume is simply printed instead.
#[clap(default_value_t = Offset::Relative(0.0))]
vol: Offset,
},
/// Bump the priority of a specific player
///
/// Note that if no-play is true, any players with a status of Playing will
/// still hold priority over the selected player.
/// Note that if --no-play is passed, any players with a status of Playing
/// will still hold priority over the selected player.
SwitchCurrent {
/// The player ID to switch to. For a list of valid players see the
/// list-players subcommand.
to: String,

#[structopt(short, long)]
/// By default switch-current will pause any currently running players
/// and play the selected player. Pass this flag to disable
/// this behavior.
#[clap(short, long)]
no_play: bool,
},
}
Expand All @@ -167,55 +169,75 @@ impl ClientCommand {
Self::Stop(..) => MethodId::Stop,
Self::Play(..) => MethodId::Play,
Self::Seek {
to: SeekPosition::Relative(..),
to: Offset::Relative(..),
..
} => MethodId::SeekRelative,
Self::Seek {
to: SeekPosition::Absolute(..),
to: Offset::Absolute(..),
..
} => MethodId::SeekAbsolute,
Self::Volume {
vol: Offset::Relative(..),
..
} => MethodId::VolRelative,
Self::Volume {
vol: Offset::Absolute(..),
..
} => MethodId::VolAbsolute,
Self::SwitchCurrent { .. } => MethodId::SwitchCurrent,
}
}
}

#[derive(Debug, Clone, StructOpt)]
#[derive(Debug, Clone, Parser)]
struct PlayerOpts {} // WARNING: DO NOT TOUCH WITHOUT INCREMENTING MAJOR VERSION

#[derive(Debug, Clone, Copy)]
enum SeekPosition {
enum Offset {
Relative(f64),
Absolute(f64),
}

impl SeekPosition {
fn pos(&self) -> f64 {
impl Offset {
fn offset(&self) -> f64 {
*match self {
Self::Relative(p) | Self::Absolute(p) => p,
Self::Relative(o) | Self::Absolute(o) => o,
}
}
}

impl ::std::str::FromStr for SeekPosition {
impl Display for Offset {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let offs = self.offset();
assert!(offs.is_sign_positive());
write!(f, "{}", offs)?;

if matches!(self, Self::Relative(..)) {
f.write_str(if offs.is_sign_negative() { "-" } else { "+" })?;
}

Ok(())
}
}

impl ::std::str::FromStr for Offset {
type Err = anyhow::Error;

fn from_str(s: &str) -> Result<Self, Self::Err> {
use regex::Regex;

lazy_static! {
static ref SEEK_PATTERN: Regex = Regex::new(r"(\+|-)?(\d+(?:\.\d+)?)").unwrap();
static ref SEEK_PATTERN: Regex = Regex::new(r"(\d*(?:\.\d+)?)(\+|-)?").unwrap();
}

if let Some(caps) = SEEK_PATTERN.captures(s) {
let time: f64 = caps[2]
.parse()
.context("invalid number for seek position")?;
let time: f64 = caps[1].parse().context("invalid number for offset")?;

Ok(match caps.get(1).map(|c| c.as_str()) {
Some("-") => SeekPosition::Relative(-time),
Some("+") => SeekPosition::Relative(time),
Ok(match caps.get(2).map(|c| c.as_str()) {
Some("-") => Offset::Relative(-time),
Some("+") => Offset::Relative(time),
Some(_) => unreachable!(),
None => SeekPosition::Absolute(time),
None => Offset::Absolute(time),
})
} else {
Err(anyhow!("invalid format for seek position"))
Expand All @@ -232,7 +254,9 @@ fn log_cfg(v: usize) -> env_logger::Builder {

let mut b = env_logger::builder();

b.filter_level(VERBOSITY[(DEFAULT_V + v).min(VERBOSITY.len() - 1)]);
b.filter_level(VERBOSITY[(DEFAULT_V + v).min(VERBOSITY.len() - 1)])
.parse_default_env();

b
}

Expand All @@ -253,7 +277,7 @@ fn run() -> Result {
.build()
.context("failed to start runtime")?;

let opts = Opts::from_args();
let opts = Opts::parse();

match opts {
Opts::Server {
Expand Down
24 changes: 24 additions & 0 deletions src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ fn handle<
}
}

#[allow(clippy::too_many_lines)]
fn register_interface(b: &mut IfaceBuilder<Arc<Server>>) {
b.method_with_cr_async(
MethodId::ListPlayers.to_string(),
Expand Down Expand Up @@ -150,6 +151,28 @@ fn register_interface(b: &mut IfaceBuilder<Arc<Server>>) {
},
);

b.method_with_cr_async(
MethodId::VolRelative.to_string(),
("vol",),
("vol",),
|ctx, cr, (to,)| {
handle(ctx, cr, move |serv| async move {
serv.vol_relative(PlayerOpts {}, to).await
})
},
);

b.method_with_cr_async(
MethodId::VolAbsolute.to_string(),
("vol",),
("vol",),
|ctx, cr, (to,)| {
handle(ctx, cr, move |serv| async move {
serv.vol_absolute(PlayerOpts {}, to).await
})
},
);

b.method_with_cr_async(
MethodId::SwitchCurrent.to_string(),
("to", "switch_playing"),
Expand Down Expand Up @@ -200,6 +223,7 @@ pub async fn run() -> Result {
Box::new(move |msg, conn| {
let msg_dbg = format!("{:?}", msg);

#[allow(clippy::single_match_else)] // Clippy bug
match cr.handle_message(msg, conn) {
Ok(()) => (),
Err(()) => warn!("Failed to handle message {}", msg_dbg),
Expand Down
3 changes: 3 additions & 0 deletions src/server/mpris.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::needless_borrow)] // Clippy bug

use dbus::{
strings::{BusName, Interface, Member},
Path,
Expand Down Expand Up @@ -25,6 +27,7 @@ pub mod player {
pub static ref SET_POSITION: Member<'static> = "SetPosition".into();
pub static ref PLAYBACK_STATUS: Member<'static> = "PlaybackStatus".into();
pub static ref METADATA: Member<'static> = "Metadata".into();
pub static ref VOLUME: Member<'static> = "Volume".into();
pub static ref POSITION: Member<'static> = "Position".into();
pub static ref CAN_GO_NEXT: Member<'static> = "CanGoNext".into();
pub static ref CAN_GO_PREVIOUS: Member<'static> = "CanGoPrevious".into();
Expand Down
Loading

0 comments on commit bafc9d4

Please sign in to comment.