From 1d3c387fed809240c002f04cfe11175c26f8d83f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Krause?= Date: Thu, 8 Sep 2016 20:49:17 +0200 Subject: [PATCH] Add --on{start,stop} command line option The --onstart and --onstop command line options can be used to run a program when the audio playback is about to begin or has ended. Note, that librespot needs executions rights to run the program. Furthermore, the full path needs to be specified, e.g. `/usr/bin/logger`. Executable scripts must begin with a shebang, e.g. `#!/bin/sh`. --- src/main.rs | 1 + src/main_helper.rs | 10 ++++++++++ src/player.rs | 20 ++++++++++++++++++++ src/session.rs | 2 ++ src/util/mod.rs | 11 +++++++++++ 5 files changed, 44 insertions(+) diff --git a/src/main.rs b/src/main.rs index 47857b93..16f51684 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,6 +26,7 @@ fn main() { main_helper::add_session_arguments(&mut opts); main_helper::add_authentication_arguments(&mut opts); main_helper::add_player_arguments(&mut opts); + main_helper::add_program_arguments(&mut opts); let args: Vec = std::env::args().collect(); diff --git a/src/main_helper.rs b/src/main_helper.rs index 99119891..628a6ac6 100644 --- a/src/main_helper.rs +++ b/src/main_helper.rs @@ -54,6 +54,11 @@ pub fn add_player_arguments(opts: &mut getopts::Options) { opts.optopt("", "device", "Audio device to use. Use '?' to list options", "DEVICE"); } +pub fn add_program_arguments(opts: &mut getopts::Options) { + opts.optopt("", "onstart", "Run PROGRAM when playback is about to begin.", "PROGRAM"); + opts.optopt("", "onstop", "Run PROGRAM when playback has ended.", "PROGRAM"); +} + pub fn create_session(matches: &getopts::Matches) -> Session { info!("librespot {} ({}). Built on {}.", version::short_sha(), @@ -77,10 +82,15 @@ pub fn create_session(matches: &getopts::Matches) -> Session { Box::new(DefaultCache::new(PathBuf::from(cache_location)).unwrap()) as Box }).unwrap_or_else(|| Box::new(NoCache) as Box); + let onstart = matches.opt_str("onstart"); + let onstop = matches.opt_str("onstop"); + let config = Config { user_agent: version::version_string(), device_name: name, bitrate: bitrate, + onstart: onstart, + onstop: onstop, }; Session::new(config, cache) diff --git a/src/player.rs b/src/player.rs index 383e1b57..7648fd7c 100644 --- a/src/player.rs +++ b/src/player.rs @@ -212,6 +212,20 @@ fn load_track(session: &Session, track_id: SpotifyId) -> Option util::run_program(program), + None => {}, + }; +} + +fn run_onstop(session: &Session) { + match session.config().onstop { + Some(ref program) => util::run_program(program), + None => {}, + }; +} + impl PlayerInternal { fn run(self, mut sink: Box) { let mut decoder = None; @@ -229,6 +243,7 @@ impl PlayerInternal { self.update(|state| { if state.status == PlayStatus::kPlayStatusPlay { sink.stop().unwrap(); + run_onstop(&self.session); } state.end_of_track = false; state.status = PlayStatus::kPlayStatusPause; @@ -245,6 +260,7 @@ impl PlayerInternal { self.update(|state| { state.status = if play { + run_onstart(&self.session); sink.start().unwrap(); PlayStatus::kPlayStatusPlay } else { @@ -301,6 +317,7 @@ impl PlayerInternal { true }); + run_onstart(&self.session); sink.start().unwrap(); } Some(PlayerCommand::Pause) => { @@ -313,6 +330,7 @@ impl PlayerInternal { }); sink.stop().unwrap(); + run_onstop(&self.session); } Some(PlayerCommand::Volume(vol)) => { self.update(|state| { @@ -331,6 +349,7 @@ impl PlayerInternal { }); sink.stop().unwrap(); + run_onstop(&self.session); decoder = None; } None => (), @@ -362,6 +381,7 @@ impl PlayerInternal { }); sink.stop().unwrap(); + run_onstop(&self.session); decoder = None; } } diff --git a/src/session.rs b/src/session.rs index ac46cd00..097df7e8 100644 --- a/src/session.rs +++ b/src/session.rs @@ -39,6 +39,8 @@ pub struct Config { pub user_agent: String, pub device_name: String, pub bitrate: Bitrate, + pub onstart: Option, + pub onstop: Option, } pub struct SessionData { diff --git a/src/util/mod.rs b/src/util/mod.rs index 933e481e..0683f6a0 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -4,6 +4,7 @@ use std::io; use std::ops::{Mul, Rem, Shr}; use std::fs; use std::path::Path; +use std::process::Command; use std::time::{UNIX_EPOCH, SystemTime}; mod int128; @@ -51,6 +52,16 @@ pub fn mkdir_existing(path: &Path) -> io::Result<()> { }) } +pub fn run_program(program: &String) { + info!("Running {}", program); + let mut v: Vec<&str> = program.split_whitespace().collect(); + let status = Command::new(&v.remove(0)) + .args(&v) + .status() + .expect("program failed to start"); + info!("Exit status: {}", status); +} + pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { let mut base = base.clone(); let mut exp = exp.clone();