@apocaliss92/scrypted-reolink-native 0.4.8 → 0.4.9

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/dist/plugin.zip CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apocaliss92/scrypted-reolink-native",
3
- "version": "0.4.8",
3
+ "version": "0.4.9",
4
4
  "description": "Use any reolink camera with Scrypted, even older/unsupported models without HTTP protocol support",
5
5
  "author": "@apocaliss92",
6
6
  "license": "Apache",
@@ -199,6 +199,15 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
199
199
  */
200
200
  protected abstract getConnectionCallbacks(): BaichuanConnectionCallbacks;
201
201
 
202
+ /**
203
+ * Check if this is an NVR/Hub device (multiple channels).
204
+ * Override in subclasses to return true for NVR devices.
205
+ * This is used for socket pooling to allocate separate sockets per channel.
206
+ */
207
+ protected isNvrDevice(): boolean {
208
+ return false; // Default: standalone camera
209
+ }
210
+
202
211
  /**
203
212
  * Check if debug logging is enabled
204
213
  */
@@ -236,8 +245,16 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
236
245
  * Ensure Baichuan client is connected and ready
237
246
  */
238
247
  async ensureBaichuanClient(): Promise<ReolinkBaichuanApi> {
248
+ const logger = this.getBaichuanLogger();
249
+ const caller = new Error().stack?.split("\n")[2]?.trim() ?? "unknown";
250
+
239
251
  // Prevent concurrent login storms - check promise first
240
- if (this.ensureClientPromise) return await this.ensureClientPromise;
252
+ if (this.ensureClientPromise) {
253
+ logger.debug(
254
+ `ensureBaichuanClient: waiting on existing promise (caller: ${caller})`,
255
+ );
256
+ return await this.ensureClientPromise;
257
+ }
241
258
 
242
259
  // Reuse existing client if socket is still connected and logged in
243
260
  // Check this AFTER checking the promise to avoid race conditions
@@ -247,18 +264,22 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
247
264
 
248
265
  // Only reuse if both conditions are true
249
266
  if (isConnected && isLoggedIn) {
267
+ logger.debug(
268
+ `ensureBaichuanClient: reusing existing client (caller: ${caller})`,
269
+ );
250
270
  return this.baichuanApi;
251
271
  }
252
272
 
253
273
  // If socket is not connected or not logged in, cleanup the stale client
254
274
  // This prevents leaking connections when the socket appears connected but isn't
255
- const logger = this.getBaichuanLogger();
256
275
  logger.log(
257
- `Stale client detected: connected=${isConnected}, loggedIn=${isLoggedIn}, cleaning up`,
276
+ `Stale client detected: connected=${isConnected}, loggedIn=${isLoggedIn}, cleaning up (caller: ${caller})`,
258
277
  );
259
278
  await this.cleanupBaichuanApi();
260
279
  }
261
280
 
281
+ logger.log(`ensureBaichuanClient: creating NEW client (caller: ${caller})`);
282
+
262
283
  // IMPORTANT: Assign the promise BEFORE the backoff to prevent parallel reconnections
263
284
  this.ensureClientPromise = (async () => {
264
285
  // Apply backoff to avoid aggressive reconnection after disconnection
@@ -299,6 +320,10 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
299
320
 
300
321
  await api.login();
301
322
 
323
+ // Set NVR flag BEFORE any streaming to ensure correct socket pooling
324
+ // NVR devices need separate sockets per channel
325
+ api.setIsNvr(this.isNvrDevice());
326
+
302
327
  // Verify socket is connected before returning
303
328
  if (!api.client.isSocketConnected()) {
304
329
  throw new Error("Socket not connected after login");
@@ -657,8 +682,15 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
657
682
  `No events received in the last ${Math.round(timeSinceLastEvent / 60_000)} minutes, restarting event listener`,
658
683
  );
659
684
  // Restart event subscription
685
+ logger.debug(
686
+ "Restarting event listener: calling unsubscribeFromEvents...",
687
+ );
660
688
  await this.unsubscribeFromEvents(true);
689
+ logger.debug(
690
+ "Restarting event listener: calling subscribeToEvents...",
691
+ );
661
692
  await this.subscribeToEvents(true);
693
+ logger.debug("Restarting event listener: done");
662
694
  } else if (this.lastEventTime === 0) {
663
695
  // If lastEventTime is 0, it means we just subscribed but haven't received any events yet
664
696
  // Wait a bit longer before considering it a problem
@@ -667,8 +699,17 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
667
699
  logger.log(
668
700
  `No events received since subscription (${Math.round(timeSinceSubscription / 60_000)} minutes ago), restarting event listener`,
669
701
  );
702
+ logger.debug(
703
+ "Restarting event listener (no events since sub): calling unsubscribeFromEvents...",
704
+ );
670
705
  await this.unsubscribeFromEvents(true);
706
+ logger.debug(
707
+ "Restarting event listener (no events since sub): calling subscribeToEvents...",
708
+ );
671
709
  await this.subscribeToEvents(true);
710
+ logger.debug(
711
+ "Restarting event listener (no events since sub): done",
712
+ );
672
713
  }
673
714
  }
674
715
  } catch (e) {
@@ -694,6 +735,13 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
694
735
  async subscribeToEvents(silent: boolean = false): Promise<void> {
695
736
  const logger = this.getBaichuanLogger();
696
737
  const callbacks = this.getConnectionCallbacks();
738
+ const existingClientInfo = this.baichuanApi
739
+ ? `connected=${this.baichuanApi.client.isSocketConnected()}, loggedIn=${this.baichuanApi.client.loggedIn}`
740
+ : "no client";
741
+
742
+ logger.debug(
743
+ `subscribeToEvents() called: silent=${silent}, existingClient=[${existingClientInfo}], eventSubscriptionActive=${this.eventSubscriptionActive}`,
744
+ );
697
745
 
698
746
  if (!callbacks.onSimpleEvent) {
699
747
  return;
@@ -716,7 +764,11 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
716
764
  await this.unsubscribeFromEvents(silent);
717
765
 
718
766
  // Get Baichuan client connection
767
+ logger.debug("subscribeToEvents: calling ensureBaichuanClient...");
719
768
  const api = await this.ensureBaichuanClient();
769
+ logger.debug(
770
+ `subscribeToEvents: ensureBaichuanClient returned, reused=${api === this.baichuanApi}`,
771
+ );
720
772
 
721
773
  // Verify connection is ready
722
774
  if (!api.client.isSocketConnected() || !api.client.loggedIn) {