@apocaliss92/scrypted-reolink-native 0.1.32 → 0.1.33
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/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +3 -2
- package/src/baichuan-base.ts +35 -7
- package/src/camera-battery.ts +0 -6
- package/src/camera.ts +1 -36
- package/src/common.ts +514 -17
- package/src/debug-options.ts +4 -0
- package/src/main.ts +158 -4
- package/src/multiFocal.ts +1 -29
- package/src/nvr.ts +1 -3
- package/src/stream-utils.ts +24 -7
- package/src/utils.ts +471 -2
- package/logs/composite-stream.txt +0 -16390
- package/logs/lense.txt +0 -44
- package/logs/multifocal.txt +0 -136
- package/logs/multifocal2.txt +0 -3585
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.1.
|
|
3
|
+
"version": "0.1.33",
|
|
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",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"ScryptedDeviceCreator",
|
|
34
34
|
"DeviceProvider",
|
|
35
35
|
"DeviceCreator",
|
|
36
|
-
"Settings"
|
|
36
|
+
"Settings",
|
|
37
|
+
"HttpRequestHandler"
|
|
37
38
|
]
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
package/src/baichuan-base.ts
CHANGED
|
@@ -208,14 +208,27 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
208
208
|
* Ensure Baichuan client is connected and ready
|
|
209
209
|
*/
|
|
210
210
|
async ensureBaichuanClient(): Promise<ReolinkBaichuanApi> {
|
|
211
|
+
// Prevent concurrent login storms - check promise first
|
|
212
|
+
if (this.ensureClientPromise) return await this.ensureClientPromise;
|
|
213
|
+
|
|
211
214
|
// Reuse existing client if socket is still connected and logged in
|
|
212
|
-
|
|
213
|
-
|
|
215
|
+
// Check this AFTER checking the promise to avoid race conditions
|
|
216
|
+
if (this.baichuanApi) {
|
|
217
|
+
const isConnected = this.baichuanApi.client.isSocketConnected();
|
|
218
|
+
const isLoggedIn = this.baichuanApi.client.loggedIn;
|
|
219
|
+
|
|
220
|
+
// Only reuse if both conditions are true
|
|
221
|
+
if (isConnected && isLoggedIn) {
|
|
222
|
+
return this.baichuanApi;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// If socket is not connected or not logged in, cleanup the stale client
|
|
226
|
+
// This prevents leaking connections when the socket appears connected but isn't
|
|
227
|
+
const logger = this.getBaichuanLogger();
|
|
228
|
+
logger.log(`Stale client detected: connected=${isConnected}, loggedIn=${isLoggedIn}, cleaning up`);
|
|
229
|
+
await this.cleanupBaichuanApi();
|
|
214
230
|
}
|
|
215
231
|
|
|
216
|
-
// Prevent concurrent login storms
|
|
217
|
-
if (this.ensureClientPromise) return await this.ensureClientPromise;
|
|
218
|
-
|
|
219
232
|
// Apply backoff to avoid aggressive reconnection after disconnection
|
|
220
233
|
if (this.lastDisconnectTime > 0) {
|
|
221
234
|
const timeSinceDisconnect = Date.now() - this.lastDisconnectTime;
|
|
@@ -309,6 +322,13 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
309
322
|
|
|
310
323
|
// Close listener
|
|
311
324
|
this.closeListener = async () => {
|
|
325
|
+
// Prevent multiple concurrent cleanup operations
|
|
326
|
+
if (!this.baichuanApi || this.baichuanApi !== api) {
|
|
327
|
+
// This close event is for a different/old client, ignore it
|
|
328
|
+
logger.debug('Close event for stale client, ignoring');
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
312
332
|
try {
|
|
313
333
|
const wasConnected = api.client.isSocketConnected();
|
|
314
334
|
const wasLoggedIn = api.client.loggedIn;
|
|
@@ -330,8 +350,16 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
330
350
|
|
|
331
351
|
logger.log(`Socket closed, resetting client state for reconnection (last disconnect ${timeSinceLastDisconnect}ms ago)`);
|
|
332
352
|
|
|
333
|
-
//
|
|
334
|
-
|
|
353
|
+
// Mark as disconnected immediately to prevent reuse
|
|
354
|
+
// This prevents race conditions where ensureBaichuanClient might check
|
|
355
|
+
// isSocketConnected() before cleanup completes
|
|
356
|
+
const currentApi = this.baichuanApi;
|
|
357
|
+
if (currentApi === api) {
|
|
358
|
+
// Only cleanup if this is still the current API instance
|
|
359
|
+
// This prevents cleanup of a new connection that was created
|
|
360
|
+
// while the old one was closing
|
|
361
|
+
await this.cleanupBaichuanApi();
|
|
362
|
+
}
|
|
335
363
|
|
|
336
364
|
// Call custom close handler if provided
|
|
337
365
|
if (callbacks.onClose) {
|
package/src/camera-battery.ts
CHANGED
|
@@ -318,10 +318,4 @@ export class ReolinkNativeBatteryCamera extends CommonCameraMixin {
|
|
|
318
318
|
this.getBaichuanLogger().warn(`Baichuan client reset requested: ${message}`);
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
|
-
|
|
322
|
-
protected async withBaichuanClient<T>(fn: (api: ReolinkBaichuanApi) => Promise<T>): Promise<T> {
|
|
323
|
-
const client = await this.ensureClient();
|
|
324
|
-
return fn(client);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
321
|
}
|
package/src/camera.ts
CHANGED
|
@@ -103,24 +103,15 @@ export class ReolinkNativeCamera extends CommonCameraMixin {
|
|
|
103
103
|
await this.alignAuxDevicesState();
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
async getDetectionInput(detectionId: string, eventId?: any): Promise<MediaObject> {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
106
|
async processEvents(events: { motion?: boolean; objects?: string[] }) {
|
|
111
107
|
const logger = this.getBaichuanLogger();
|
|
112
108
|
|
|
113
109
|
if (!this.isEventDispatchEnabled()) return;
|
|
114
110
|
|
|
115
|
-
if (this.
|
|
111
|
+
if (this.isDebugEnabled()) {
|
|
116
112
|
logger.debug(`Events received: ${JSON.stringify(events)}`);
|
|
117
113
|
}
|
|
118
114
|
|
|
119
|
-
// const debugEvents = this.storageSettings.values.debugEvents;
|
|
120
|
-
// if (debugEvents) {
|
|
121
|
-
// logger.debug(`Events received: ${JSON.stringify(events)}`);
|
|
122
|
-
// }
|
|
123
|
-
|
|
124
115
|
if (this.shouldDispatchMotion() && events.motion !== this.motionDetected) {
|
|
125
116
|
if (events.motion) {
|
|
126
117
|
this.motionDetected = true;
|
|
@@ -147,30 +138,4 @@ export class ReolinkNativeCamera extends CommonCameraMixin {
|
|
|
147
138
|
sdk.deviceManager.onDeviceEvent(this.nativeId, ScryptedInterface.ObjectDetector, od);
|
|
148
139
|
}
|
|
149
140
|
}
|
|
150
|
-
|
|
151
|
-
protected async withBaichuanClient<T>(fn: (api: ReolinkBaichuanApi) => Promise<T>): Promise<T> {
|
|
152
|
-
const client = await this.ensureClient();
|
|
153
|
-
return fn(client);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
async getPictureOptions(): Promise<ResponsePictureOptions[]> {
|
|
157
|
-
return [];
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async getOtherSettings(): Promise<Setting[]> {
|
|
161
|
-
return await this.getSettings();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
showRtspUrlOverride() {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
async startIntercom(media: MediaObject): Promise<void> {
|
|
170
|
-
await this.intercom.start(media);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
stopIntercom(): Promise<void> {
|
|
174
|
-
return this.intercom.stop();
|
|
175
|
-
}
|
|
176
141
|
}
|