@lox-audioserver/node-librespot 0.3.5 → 0.3.6

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lox-audioserver/node-librespot",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Node.js bindings for librespot (Spotify Connect) via N-API with prebuild support.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/lib.rs CHANGED
@@ -2207,6 +2207,78 @@ fn start_connect_device_inner(
2207
2207
  });
2208
2208
  }
2209
2209
 
2210
+ // Log observer: detect audio_key_error and decoder errors from librespot log output.
2211
+ // This mirrors the same pattern used in stream_track to ensure the JS layer receives
2212
+ // error events and can trigger recovery (e.g. re-authentication, skip to next track).
2213
+ if let Some(tsfn_ev) = event_tsfn.clone() {
2214
+ let mut log_rx = subscribe_log_events();
2215
+ let error_sent = Arc::new(AtomicBool::new(false));
2216
+ let decoder_metric_sent = Arc::new(AtomicBool::new(false));
2217
+ let stop_flag_for_logs = stop_flag_for_block.clone();
2218
+ let device_id_for_logs = device_id.clone();
2219
+ let session_id_for_logs = session_id.clone();
2220
+ runtime().spawn(async move {
2221
+ while let Some(event) = log_rx.recv().await {
2222
+ if stop_flag_for_logs.load(Ordering::Acquire) {
2223
+ break;
2224
+ }
2225
+ if is_decoder_error(&event) {
2226
+ if !decoder_metric_sent.swap(true, Ordering::AcqRel) {
2227
+ let payload = ConnectEvent {
2228
+ r#type: "metric".into(),
2229
+ device_id: Some(device_id_for_logs.clone()),
2230
+ session_id: Some(session_id_for_logs.clone()),
2231
+ track_id: None,
2232
+ uri: None,
2233
+ title: None,
2234
+ artist: None,
2235
+ album: None,
2236
+ duration_ms: None,
2237
+ position_ms: None,
2238
+ volume: None,
2239
+ error_code: None,
2240
+ error_message: None,
2241
+ metric_name: Some("decode_error".into()),
2242
+ metric_value_ms: None,
2243
+ metric_message: Some(event.message.clone()),
2244
+ credentials_json: None,
2245
+ };
2246
+ let _ = tsfn_ev.call(payload, ThreadsafeFunctionCallMode::NonBlocking);
2247
+ }
2248
+ }
2249
+ if error_sent.load(Ordering::Acquire) {
2250
+ continue;
2251
+ }
2252
+ if !is_audio_key_error(&event) {
2253
+ continue;
2254
+ }
2255
+ if error_sent.swap(true, Ordering::AcqRel) {
2256
+ continue;
2257
+ }
2258
+ let payload = ConnectEvent {
2259
+ r#type: "error".into(),
2260
+ device_id: Some(device_id_for_logs.clone()),
2261
+ session_id: Some(session_id_for_logs.clone()),
2262
+ track_id: None,
2263
+ uri: None,
2264
+ title: None,
2265
+ artist: None,
2266
+ album: None,
2267
+ duration_ms: None,
2268
+ position_ms: None,
2269
+ volume: None,
2270
+ error_code: Some("audio_key_error".into()),
2271
+ error_message: Some(event.message.clone()),
2272
+ metric_name: None,
2273
+ metric_value_ms: None,
2274
+ metric_message: None,
2275
+ credentials_json: None,
2276
+ };
2277
+ let _ = tsfn_ev.call(payload, ThreadsafeFunctionCallMode::NonBlocking);
2278
+ }
2279
+ });
2280
+ }
2281
+
2210
2282
  let (spirc, spirc_task) = Spirc::new(
2211
2283
  connect_config,
2212
2284
  session,