mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
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`.
This commit is contained in:
parent
f79df63734
commit
1d3c387fed
5 changed files with 44 additions and 0 deletions
|
@ -26,6 +26,7 @@ fn main() {
|
||||||
main_helper::add_session_arguments(&mut opts);
|
main_helper::add_session_arguments(&mut opts);
|
||||||
main_helper::add_authentication_arguments(&mut opts);
|
main_helper::add_authentication_arguments(&mut opts);
|
||||||
main_helper::add_player_arguments(&mut opts);
|
main_helper::add_player_arguments(&mut opts);
|
||||||
|
main_helper::add_program_arguments(&mut opts);
|
||||||
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
|
||||||
|
|
|
@ -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");
|
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 {
|
pub fn create_session(matches: &getopts::Matches) -> Session {
|
||||||
info!("librespot {} ({}). Built on {}.",
|
info!("librespot {} ({}). Built on {}.",
|
||||||
version::short_sha(),
|
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<Cache + Send + Sync>
|
Box::new(DefaultCache::new(PathBuf::from(cache_location)).unwrap()) as Box<Cache + Send + Sync>
|
||||||
}).unwrap_or_else(|| Box::new(NoCache) as Box<Cache + Send + Sync>);
|
}).unwrap_or_else(|| Box::new(NoCache) as Box<Cache + Send + Sync>);
|
||||||
|
|
||||||
|
let onstart = matches.opt_str("onstart");
|
||||||
|
let onstop = matches.opt_str("onstop");
|
||||||
|
|
||||||
let config = Config {
|
let config = Config {
|
||||||
user_agent: version::version_string(),
|
user_agent: version::version_string(),
|
||||||
device_name: name,
|
device_name: name,
|
||||||
bitrate: bitrate,
|
bitrate: bitrate,
|
||||||
|
onstart: onstart,
|
||||||
|
onstop: onstop,
|
||||||
};
|
};
|
||||||
|
|
||||||
Session::new(config, cache)
|
Session::new(config, cache)
|
||||||
|
|
|
@ -212,6 +212,20 @@ fn load_track(session: &Session, track_id: SpotifyId) -> Option<vorbis::Decoder<
|
||||||
Some(decoder)
|
Some(decoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_onstart(session: &Session) {
|
||||||
|
match session.config().onstart {
|
||||||
|
Some(ref program) => util::run_program(program),
|
||||||
|
None => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_onstop(session: &Session) {
|
||||||
|
match session.config().onstop {
|
||||||
|
Some(ref program) => util::run_program(program),
|
||||||
|
None => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
impl PlayerInternal {
|
impl PlayerInternal {
|
||||||
fn run(self, mut sink: Box<Sink>) {
|
fn run(self, mut sink: Box<Sink>) {
|
||||||
let mut decoder = None;
|
let mut decoder = None;
|
||||||
|
@ -229,6 +243,7 @@ impl PlayerInternal {
|
||||||
self.update(|state| {
|
self.update(|state| {
|
||||||
if state.status == PlayStatus::kPlayStatusPlay {
|
if state.status == PlayStatus::kPlayStatusPlay {
|
||||||
sink.stop().unwrap();
|
sink.stop().unwrap();
|
||||||
|
run_onstop(&self.session);
|
||||||
}
|
}
|
||||||
state.end_of_track = false;
|
state.end_of_track = false;
|
||||||
state.status = PlayStatus::kPlayStatusPause;
|
state.status = PlayStatus::kPlayStatusPause;
|
||||||
|
@ -245,6 +260,7 @@ impl PlayerInternal {
|
||||||
|
|
||||||
self.update(|state| {
|
self.update(|state| {
|
||||||
state.status = if play {
|
state.status = if play {
|
||||||
|
run_onstart(&self.session);
|
||||||
sink.start().unwrap();
|
sink.start().unwrap();
|
||||||
PlayStatus::kPlayStatusPlay
|
PlayStatus::kPlayStatusPlay
|
||||||
} else {
|
} else {
|
||||||
|
@ -301,6 +317,7 @@ impl PlayerInternal {
|
||||||
true
|
true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
run_onstart(&self.session);
|
||||||
sink.start().unwrap();
|
sink.start().unwrap();
|
||||||
}
|
}
|
||||||
Some(PlayerCommand::Pause) => {
|
Some(PlayerCommand::Pause) => {
|
||||||
|
@ -313,6 +330,7 @@ impl PlayerInternal {
|
||||||
});
|
});
|
||||||
|
|
||||||
sink.stop().unwrap();
|
sink.stop().unwrap();
|
||||||
|
run_onstop(&self.session);
|
||||||
}
|
}
|
||||||
Some(PlayerCommand::Volume(vol)) => {
|
Some(PlayerCommand::Volume(vol)) => {
|
||||||
self.update(|state| {
|
self.update(|state| {
|
||||||
|
@ -331,6 +349,7 @@ impl PlayerInternal {
|
||||||
});
|
});
|
||||||
|
|
||||||
sink.stop().unwrap();
|
sink.stop().unwrap();
|
||||||
|
run_onstop(&self.session);
|
||||||
decoder = None;
|
decoder = None;
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
|
@ -362,6 +381,7 @@ impl PlayerInternal {
|
||||||
});
|
});
|
||||||
|
|
||||||
sink.stop().unwrap();
|
sink.stop().unwrap();
|
||||||
|
run_onstop(&self.session);
|
||||||
decoder = None;
|
decoder = None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,8 @@ pub struct Config {
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
pub device_name: String,
|
pub device_name: String,
|
||||||
pub bitrate: Bitrate,
|
pub bitrate: Bitrate,
|
||||||
|
pub onstart: Option<String>,
|
||||||
|
pub onstop: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SessionData {
|
pub struct SessionData {
|
||||||
|
|
|
@ -4,6 +4,7 @@ use std::io;
|
||||||
use std::ops::{Mul, Rem, Shr};
|
use std::ops::{Mul, Rem, Shr};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::process::Command;
|
||||||
use std::time::{UNIX_EPOCH, SystemTime};
|
use std::time::{UNIX_EPOCH, SystemTime};
|
||||||
|
|
||||||
mod int128;
|
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 {
|
pub fn powm(base: &BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint {
|
||||||
let mut base = base.clone();
|
let mut base = base.clone();
|
||||||
let mut exp = exp.clone();
|
let mut exp = exp.clone();
|
||||||
|
|
Loading…
Reference in a new issue