Files
psysonic/src-tauri/crates/psysonic-audio/src/power_notify_linux.rs
T
Psychotoxical 41e75663f1 refactor: extract psysonic-audio crate (M3/7)
Moves all audio playback code (Symphonia decode, rodio output, HTTP
streaming, gapless, previews, and the seven stream/ source-type
submodules from the prior split) out of the top crate into a new
psysonic-audio crate.

  crates/psysonic-audio/                  new lib crate, depends on
                                          psysonic-core + psysonic-analysis
    src/{engine,helpers,decode,…}.rs      flattened layout (no more
                                          extra audio/ namespace level)
    src/stream/                           seven submodules from M0
    src/lib.rs                            re-exports macros from
                                          psysonic-core and the public
                                          API surface

The audio↔analysis edges identified in the dep survey are now real
crate deps (audio depends on analysis directly: AnalysisCache reads,
recommended_gain_for_target, submit_analysis_cpu_seed). Only the
analysis→audio back-edge goes through the PlaybackQueryHandle port
registered in M2.

Cross-crate ref migrations applied via batch sed:
  crate::audio::*               → crate::*       (intra-crate)
  crate::analysis_cache::*      → psysonic_analysis::analysis_cache::*
  crate::submit_analysis_cpu_seed → psysonic_analysis::analysis_runtime::*
  crate::subsonic_wire_user_agent → psysonic_core::user_agent::*

Top crate keeps `crate::audio::*` paths working via
`pub use psysonic_audio as audio;` — lib_commands/cli callers untouched.
`stop_audio_engine` (mac process-exit cleanup) moved into the audio
crate as `pub fn stop_audio_engine` since it reaches AudioEngine
internals; tray.rs now re-exports the moved fn.

Two small visibility promotions in engine.rs:
  pub(crate) fn analysis_track_id_is_current_playback   → pub
  pub(crate) fn ranged_loudness_backfill_should_defer    → pub

Behaviour preserving. Cargo check + clippy --workspace clean.
2026-05-09 13:33:44 +02:00

91 lines
2.5 KiB
Rust

//! Linux: subscribe to logind `PrepareForSleep` on the system bus — `start == false` means resume
//! completed (systemd says the boolean is true when going to sleep, false when waking).
use tauri::AppHandle;
use super::power_resume::{debounce_allow_resume_reopen, reopen_audio_after_system_resume};
pub fn register(app: AppHandle) {
let res = std::thread::Builder::new()
.name("psysonic-logind-sleep".into())
.spawn(move || run_listener(app));
if let Err(e) = res {
crate::app_eprintln!("[psysonic] could not spawn logind listener: {e}");
}
}
fn run_listener(app: AppHandle) {
use zbus::blocking::{Connection, MessageIterator};
use zbus::message::Type;
use zbus::MatchRule;
let conn = match Connection::system() {
Ok(c) => c,
Err(e) => {
crate::app_eprintln!(
"[psysonic] D-Bus system bus unavailable — post-sleep audio recovery disabled: {e}"
);
return;
}
};
let rule: zbus::MatchRule = match (|| -> zbus::Result<_> {
Ok(MatchRule::builder()
.msg_type(Type::Signal)
.path("/org/freedesktop/login1")?
.interface("org.freedesktop.login1.Manager")?
.member("PrepareForSleep")?
.build())
})() {
Ok(r) => r,
Err(e) => {
crate::app_eprintln!(
"[psysonic] MatchRule for logind PrepareForSleep failed: {e}"
);
return;
}
};
let mut iter = match MessageIterator::for_match_rule(rule, &conn, Some(32)) {
Ok(i) => i,
Err(e) => {
crate::app_eprintln!("[psysonic] logind signal subscription failed: {e}");
return;
}
};
crate::app_eprintln!("[psysonic] logind PrepareForSleep listener registered (post-sleep audio recovery)");
loop {
let Some(result) = iter.next() else {
break;
};
let msg = match result {
Ok(m) => m,
Err(e) => {
crate::app_eprintln!("[psysonic] logind message stream error: {e}");
break;
}
};
let start: bool = match msg.body().deserialize() {
Ok(b) => b,
Err(_) => continue,
};
if start {
continue;
}
if !debounce_allow_resume_reopen() {
continue;
}
let app = app.clone();
tauri::async_runtime::spawn(async move {
reopen_audio_after_system_resume(&app).await;
});
}
}