Skip to content

Commit

Permalink
New attempt at integrating Connect mode: use librespot's event hand…
Browse files Browse the repository at this point in the history
…ler to signal all kinds of events. Let's filter on the LMS side.
  • Loading branch information
michaelherger committed Sep 24, 2024
1 parent 231f6e3 commit bace211
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 199 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,13 @@ path = "oauth"
version = "0.5.0-dev"

[dependencies]
base64 = "0.22"
bytes = "1.7.2"
data-encoding = "2.5"
env_logger = { version = "0.11.2", default-features = false, features = ["color", "humantime", "auto-color"] }
futures-util = { version = "0.3", default-features = false }
getopts = "0.2"
http-body-util = "0.1.2"
hyper = { version = "1.3", features = [] }
hyper-util = { version = "0.1", features = ["client"] }
log = "0.4"
Expand Down Expand Up @@ -108,4 +111,4 @@ assets = [
[profile.release]
lto = true
panic = "abort"
strip = "symbols"
strip = "symbols"
32 changes: 16 additions & 16 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ use librespot::{

#[cfg(feature = "spotty")]
mod spotty;
// #[cfg(feature = "spotty")]
// use spotty::LMS;

const VERSION: &str = concat!(env!("CARGO_PKG_NAME"), " v", env!("CARGO_PKG_VERSION"));

Expand All @@ -52,8 +50,9 @@ const NULLDEVICE: &str = "/dev/null";
#[cfg(feature = "alsa-backend")]
use librespot::playback::mixer::alsamixer::AlsaMixer;

#[cfg(not(feature = "spotty"))]
mod player_event_handler;
#[cfg(feature = "spotty")]
use player_event_handler::EventHandler;
#[cfg(not(feature = "spotty"))]
use player_event_handler::{run_program_on_sink_events, EventHandler};

Expand Down Expand Up @@ -235,7 +234,6 @@ struct Setup {
oauth_port: Option<u16>,
enable_discovery: bool,
zeroconf_port: u16,
#[cfg(not(feature = "spotty"))]
player_event_program: Option<String>,
#[cfg(not(feature = "spotty"))]
emit_sink_events: bool,
Expand All @@ -248,7 +246,6 @@ struct Setup {
scopes: Option<String>,
get_token: bool,
save_token: Option<String>,
// lms: LMS,
}

fn get_setup() -> Setup {
Expand Down Expand Up @@ -1865,12 +1862,25 @@ fn get_setup() -> Setup {
normalisation_release_cf,
normalisation_knee_db,
ditherer,
#[cfg(feature = "spotty")]
lms_connect_mode: !opt_present(SINGLE_TRACK),
}
};

let authenticate = opt_present(AUTHENTICATE);

#[cfg(feature = "spotty")]
let player_event_program = if opt_present(LYRION_MUSIC_SERVER) && opt_present(PLAYER_MAC) {
Some(format!(
"{}|{}|{}",
opt_str(LYRION_MUSIC_SERVER).unwrap(),
opt_str(PLAYER_MAC).unwrap_or_default(),
opt_str(LMS_AUTH).unwrap_or_default(),
))
} else {
None
};

#[cfg(not(feature = "spotty"))]
let player_event_program = opt_str(ONEVENT);
#[cfg(not(feature = "spotty"))]
Expand All @@ -1885,13 +1895,6 @@ fn get_setup() -> Setup {
let save_token = opt_str(SAVE_TOKEN).unwrap_or_else(|| "".to_string());
let client_id = opt_str(CLIENT_ID).unwrap_or_else(|| include_str!("client_id.txt").to_string());

// #[cfg(feature = "spotty")]
// let lms = LMS::new(
// opt_str(LYRION_MUSIC_SERVER),
// opt_str(PLAYER_MAC),
// opt_str(LMS_AUTH),
// );

Setup {
format,
backend,
Expand All @@ -1907,7 +1910,6 @@ fn get_setup() -> Setup {
oauth_port,
enable_discovery,
zeroconf_port,
#[cfg(not(feature = "spotty"))]
player_event_program,
#[cfg(not(feature = "spotty"))]
emit_sink_events,
Expand All @@ -1929,7 +1931,6 @@ fn get_setup() -> Setup {
Some(client_id)
},
scopes: opt_str(SCOPE),
// lms,
}
}

Expand All @@ -1952,7 +1953,6 @@ async fn main() {
let mut auto_connect_times: Vec<Instant> = vec![];
let mut discovery = None;
let mut connecting = false;
#[cfg(not(feature = "spotty"))]
let mut _event_handler: Option<EventHandler> = None;

let mut session = Session::new(setup.session_config.clone(), setup.cache.clone());
Expand Down Expand Up @@ -2061,13 +2061,13 @@ async fn main() {
(backend)(device, format)
});

#[cfg(not(feature = "spotty"))]
if let Some(player_event_program) = setup.player_event_program.clone() {
_event_handler = Some(EventHandler::new(
player.get_player_event_channel(),
&player_event_program,
));

#[cfg(not(feature = "spotty"))]
if setup.emit_sink_events {
player.set_sink_event_callback(Some(Box::new(move |sink_status| {
run_program_on_sink_events(sink_status, &player_event_program)
Expand Down
62 changes: 62 additions & 0 deletions src/player_event_handler.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use log::{debug, error, warn};

#[allow(unused)]
use std::{collections::HashMap, process::Command, thread};

#[allow(unused)]
use librespot::{
metadata::audio::UniqueFields,
playback::player::{PlayerEvent, PlayerEventChannel, SinkStatus},
};

#[cfg(feature = "spotty")]
const VERSION: &str = concat!(env!("CARGO_PKG_NAME"), " v", env!("CARGO_PKG_VERSION"));

pub struct EventHandler {
thread_handle: Option<thread::JoinHandle<()>>,
}
Expand Down Expand Up @@ -245,6 +250,10 @@ impl EventHandler {
}

if !env_vars.is_empty() {
#[cfg(feature = "spotty")]
call_lms(env_vars, &on_event);

#[cfg(not(feature = "spotty"))]
run_program(env_vars, &on_event);
}
}
Expand All @@ -266,6 +275,7 @@ impl Drop for EventHandler {
}
}

#[cfg(not(feature = "spotty"))]
pub fn run_program_on_sink_events(sink_status: SinkStatus, onevent: &str) {
let mut env_vars = HashMap::new();

Expand All @@ -282,6 +292,7 @@ pub fn run_program_on_sink_events(sink_status: SinkStatus, onevent: &str) {
run_program(env_vars, onevent);
}

#[cfg(not(feature = "spotty"))]
fn run_program(env_vars: HashMap<&str, String>, onevent: &str) {
let mut v: Vec<&str> = onevent.split_whitespace().collect();

Expand Down Expand Up @@ -309,3 +320,54 @@ fn run_program(env_vars: HashMap<&str, String>, onevent: &str) {
},
}
}

#[cfg(feature = "spotty")]
fn call_lms(env_vars: HashMap<&str, String>, onevent: &str) {
let mut params = onevent.split("|");

let host_port = params.next();
let player_mac = params.next();
let auth = params.next();

if host_port.is_some() && player_mac.is_some() {
debug!("Calling {:?} with variables:\n{:#?}", onevent, env_vars);

let base_url = format!("http://{}/jsonrpc.js", host_port.unwrap());

let json = format!(
r#"{{"id": 1,"method":"slim.request","params":["{}",["spottyconnect","event","{}"]]}}"#,
player_mac.unwrap(),
BASE64_STANDARD.encode(format!("{:?}", env_vars).as_bytes())
);

use bytes::Bytes;
use http_body_util::Full;

use base64::prelude::*;
use hyper::Request;
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
use tokio::runtime::Runtime;

let req: Request<Full<Bytes>> = Request::builder()
.method("POST")
.uri(base_url.to_string())
.header("user-agent", VERSION.to_string())
.header("content-type", "application/json")
.header(
"authorization",
format!("Basic {}", format!("Basic {}", auth.unwrap_or(""))),
)
.header("x-scanner", "1")
.body(Full::from(json))
.expect("Signal Spotty Event");

let client: Client<_, Full<Bytes>> = Client::builder(TokioExecutor::new()).build_http();

let rt = Runtime::new().unwrap();
rt.block_on(async move {
let _ = client.request(req).await;
});
} else {
eprint!("missing LMS connection params");
}
}
Loading

0 comments on commit bace211

Please sign in to comment.