@lox-audioserver/node-librespot 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/src/lib.rs
CHANGED
|
@@ -1865,12 +1865,21 @@ fn start_connect_device_inner(
|
|
|
1865
1865
|
let player = Player::new(player_config, session.clone(), volume_getter, sink_builder);
|
|
1866
1866
|
// Clone log_tsfn before it is moved into the player-event spawn below.
|
|
1867
1867
|
let log_tsfn_for_disc = log_tsfn.clone();
|
|
1868
|
+
// [issue #252] Bumped by the player-event observer on signals that
|
|
1869
|
+
// prove audio is flowing (`playing` / `position_correction`). Read by
|
|
1870
|
+
// the log observer further down to suppress transient `audio_key_error`
|
|
1871
|
+
// events that fire while spirc is already recovering from an
|
|
1872
|
+
// account-side dealer-disconnect burst. A permanent audio-key fault
|
|
1873
|
+
// never produces a healthy event, so the error still surfaces after
|
|
1874
|
+
// the grace window.
|
|
1875
|
+
let healthy_seq = Arc::new(AtomicU64::new(0));
|
|
1868
1876
|
// Forward player events to JS if requested.
|
|
1869
1877
|
if let Some(tsfn_ev) = event_tsfn.clone() {
|
|
1870
1878
|
let mut ev_rx = player.get_player_event_channel();
|
|
1871
1879
|
let session_for_events = session.clone();
|
|
1872
1880
|
let device_id_for_events = device_id.clone();
|
|
1873
1881
|
let session_id_for_events = session_id.clone();
|
|
1882
|
+
let healthy_seq_for_events = healthy_seq.clone();
|
|
1874
1883
|
let last_pcm_for_health = last_pcm_at.clone();
|
|
1875
1884
|
let stop_flag_for_health = stop_flag_for_block.clone();
|
|
1876
1885
|
let device_id_for_health = device_id.clone();
|
|
@@ -2250,6 +2259,13 @@ fn start_connect_device_inner(
|
|
|
2250
2259
|
_ => {}
|
|
2251
2260
|
}
|
|
2252
2261
|
|
|
2262
|
+
// [issue #252] Signal "audio is actually flowing" to the
|
|
2263
|
+
// log observer. Excludes `paused` because a paused stream
|
|
2264
|
+
// can't prove the dealer/session is healthy.
|
|
2265
|
+
if matches!(payload.r#type.as_str(), "playing" | "position_correction") {
|
|
2266
|
+
healthy_seq_for_events.fetch_add(1, Ordering::AcqRel);
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2253
2269
|
let _ = tsfn_ev.call(payload, ThreadsafeFunctionCallMode::NonBlocking);
|
|
2254
2270
|
}
|
|
2255
2271
|
});
|
|
@@ -2265,6 +2281,16 @@ fn start_connect_device_inner(
|
|
|
2265
2281
|
let stop_flag_for_logs = stop_flag_for_block.clone();
|
|
2266
2282
|
let device_id_for_logs = device_id.clone();
|
|
2267
2283
|
let session_id_for_logs = session_id.clone();
|
|
2284
|
+
let healthy_seq_for_logs = healthy_seq.clone();
|
|
2285
|
+
// [issue #252] Grace window before surfacing audio_key_error to JS.
|
|
2286
|
+
// Account-side Spotify dealer-disconnect bursts cause every
|
|
2287
|
+
// offload=false zone to emit a single audio-key-timeout log line,
|
|
2288
|
+
// while spirc reconnects internally within seconds. Without this
|
|
2289
|
+
// delay every burst trips the per-zone cooldown in
|
|
2290
|
+
// SpotifyConnectInstance and produces ~5 minutes of unplayable
|
|
2291
|
+
// state per zone. 5s is well above typical spirc recovery time
|
|
2292
|
+
// and well below the cooldown window.
|
|
2293
|
+
const AUDIO_KEY_GRACE_MS: u64 = 5000;
|
|
2268
2294
|
runtime().spawn(async move {
|
|
2269
2295
|
while let Some(event) = log_rx.recv().await {
|
|
2270
2296
|
if stop_flag_for_logs.load(Ordering::Acquire) {
|
|
@@ -2322,7 +2348,28 @@ fn start_connect_device_inner(
|
|
|
2322
2348
|
metric_message: None,
|
|
2323
2349
|
credentials_json: None,
|
|
2324
2350
|
};
|
|
2325
|
-
|
|
2351
|
+
// [issue #252] Defer the emit by AUDIO_KEY_GRACE_MS and
|
|
2352
|
+
// suppress it if a healthy event arrived in the meantime
|
|
2353
|
+
// (spirc finished its internal reconnect). Re-arm
|
|
2354
|
+
// `error_sent` on suppression so a *later* fault in the
|
|
2355
|
+
// same session can still surface.
|
|
2356
|
+
let baseline = healthy_seq_for_logs.load(Ordering::Acquire);
|
|
2357
|
+
let healthy_seq_for_check = healthy_seq_for_logs.clone();
|
|
2358
|
+
let stop_flag_for_grace = stop_flag_for_logs.clone();
|
|
2359
|
+
let error_sent_for_reset = error_sent.clone();
|
|
2360
|
+
let tsfn_ev_for_grace = tsfn_ev.clone();
|
|
2361
|
+
runtime().spawn(async move {
|
|
2362
|
+
tokio::time::sleep(Duration::from_millis(AUDIO_KEY_GRACE_MS)).await;
|
|
2363
|
+
if stop_flag_for_grace.load(Ordering::Acquire) {
|
|
2364
|
+
return;
|
|
2365
|
+
}
|
|
2366
|
+
if healthy_seq_for_check.load(Ordering::Acquire) != baseline {
|
|
2367
|
+
error_sent_for_reset.store(false, Ordering::Release);
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
let _ = tsfn_ev_for_grace
|
|
2371
|
+
.call(payload, ThreadsafeFunctionCallMode::NonBlocking);
|
|
2372
|
+
});
|
|
2326
2373
|
}
|
|
2327
2374
|
});
|
|
2328
2375
|
}
|