@apocaliss92/scrypted-reolink-native 0.4.5 → 0.4.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/dist/main.nodejs.js +1 -1
- package/dist/plugin.zip +0 -0
- package/package.json +1 -1
- package/src/baichuan-base.ts +5 -130
- package/src/camera.ts +1 -142
- package/src/nvr.ts +16 -5
- package/src/stream-utils.ts +1 -1
package/dist/plugin.zip
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/src/baichuan-base.ts
CHANGED
|
@@ -467,11 +467,11 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
467
467
|
this.errorListener = undefined;
|
|
468
468
|
}
|
|
469
469
|
|
|
470
|
-
// Close connection
|
|
470
|
+
// Close connection best-effort.
|
|
471
|
+
// Don't rely on isSocketConnected(): if the local socket state is inconsistent,
|
|
472
|
+
// skipping close can leave a "ghost" session on the device.
|
|
471
473
|
try {
|
|
472
|
-
|
|
473
|
-
await api.close();
|
|
474
|
-
}
|
|
474
|
+
await api.close();
|
|
475
475
|
} catch {
|
|
476
476
|
// ignore
|
|
477
477
|
}
|
|
@@ -799,133 +799,8 @@ export abstract class BaseBaichuanClass extends ScryptedDeviceBase {
|
|
|
799
799
|
? await (this as any).ensureClient()
|
|
800
800
|
: await this.ensureBaichuanClient();
|
|
801
801
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
const sessions = await api.getOnlineUserList();
|
|
805
|
-
|
|
806
|
-
// Get current socket session ID if available
|
|
807
|
-
let socketSessionId: string | undefined;
|
|
808
|
-
try {
|
|
809
|
-
socketSessionId = api.client.getSocketSessionId();
|
|
810
|
-
} catch {
|
|
811
|
-
// getSocketSessionId might not be available on all clients
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// Format sessions as array of readable strings
|
|
815
|
-
const sessionStrings: string[] = [];
|
|
816
|
-
|
|
817
|
-
// Add header with timestamp and socket session ID
|
|
818
|
-
const timestamp = new Date().toLocaleString();
|
|
819
|
-
sessionStrings.push(`Last updated: ${timestamp}`);
|
|
820
|
-
if (socketSessionId) {
|
|
821
|
-
sessionStrings.push(`Current socket session ID: ${socketSessionId}`);
|
|
822
|
-
}
|
|
823
|
-
sessionStrings.push(""); // Empty line separator
|
|
824
|
-
|
|
825
|
-
// Parse sessions data (handle different response formats)
|
|
826
|
-
let sessionCount = 0;
|
|
827
|
-
const parseSessions = (data: any, prefix: string = ""): void => {
|
|
828
|
-
if (Array.isArray(data)) {
|
|
829
|
-
data.forEach((session: any, index: number) => {
|
|
830
|
-
sessionCount++;
|
|
831
|
-
const sessionInfo = formatSessionInfo(session, index + 1);
|
|
832
|
-
sessionStrings.push(sessionInfo);
|
|
833
|
-
});
|
|
834
|
-
} else if (data && typeof data === "object") {
|
|
835
|
-
// Handle nested objects
|
|
836
|
-
for (const [key, value] of Object.entries(data)) {
|
|
837
|
-
if (Array.isArray(value)) {
|
|
838
|
-
value.forEach((session: any, index: number) => {
|
|
839
|
-
sessionCount++;
|
|
840
|
-
const sessionInfo = formatSessionInfo(session, index + 1, key);
|
|
841
|
-
sessionStrings.push(sessionInfo);
|
|
842
|
-
});
|
|
843
|
-
} else if (value && typeof value === "object") {
|
|
844
|
-
parseSessions(value, prefix ? `${prefix}.${key}` : key);
|
|
845
|
-
} else {
|
|
846
|
-
// Single session object
|
|
847
|
-
sessionCount++;
|
|
848
|
-
const sessionInfo = formatSessionInfo(data, 1);
|
|
849
|
-
sessionStrings.push(sessionInfo);
|
|
850
|
-
return; // Only process once
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
|
|
856
|
-
// Helper function to format session info as readable string
|
|
857
|
-
const formatSessionInfo = (
|
|
858
|
-
session: any,
|
|
859
|
-
index: number,
|
|
860
|
-
group?: string,
|
|
861
|
-
): string => {
|
|
862
|
-
const parts: string[] = [];
|
|
863
|
-
|
|
864
|
-
if (group) {
|
|
865
|
-
parts.push(`[${group}]`);
|
|
866
|
-
}
|
|
867
|
-
parts.push(`Session ${index}:`);
|
|
868
|
-
|
|
869
|
-
// Extract common fields
|
|
870
|
-
if (session.userName !== undefined) {
|
|
871
|
-
parts.push(`User: ${session.userName}`);
|
|
872
|
-
}
|
|
873
|
-
if (session.user !== undefined) {
|
|
874
|
-
parts.push(`User: ${session.user}`);
|
|
875
|
-
}
|
|
876
|
-
if (session.ip !== undefined) {
|
|
877
|
-
parts.push(`IP: ${session.ip}`);
|
|
878
|
-
}
|
|
879
|
-
if (session.ipAddress !== undefined) {
|
|
880
|
-
parts.push(`IP: ${session.ipAddress}`);
|
|
881
|
-
}
|
|
882
|
-
if (session.port !== undefined) {
|
|
883
|
-
parts.push(`Port: ${session.port}`);
|
|
884
|
-
}
|
|
885
|
-
if (session.sessionId !== undefined) {
|
|
886
|
-
parts.push(`Session ID: ${session.sessionId}`);
|
|
887
|
-
}
|
|
888
|
-
if (session.id !== undefined) {
|
|
889
|
-
parts.push(`ID: ${session.id}`);
|
|
890
|
-
}
|
|
891
|
-
if (session.loginTime !== undefined) {
|
|
892
|
-
parts.push(`Login Time: ${session.loginTime}`);
|
|
893
|
-
}
|
|
894
|
-
if (session.time !== undefined) {
|
|
895
|
-
parts.push(`Time: ${session.time}`);
|
|
896
|
-
}
|
|
897
|
-
if (session.status !== undefined) {
|
|
898
|
-
parts.push(`Status: ${session.status}`);
|
|
899
|
-
}
|
|
900
|
-
|
|
901
|
-
// If no common fields found, show all fields
|
|
902
|
-
if (parts.length === (group ? 2 : 1)) {
|
|
903
|
-
const allFields = Object.entries(session)
|
|
904
|
-
.map(([key, value]) => `${key}: ${value}`)
|
|
905
|
-
.join(", ");
|
|
906
|
-
parts.push(allFields);
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
return parts.join(" | ");
|
|
910
|
-
};
|
|
911
|
-
|
|
912
|
-
parseSessions(sessions);
|
|
913
|
-
|
|
914
|
-
// If no sessions found, add a message
|
|
915
|
-
if (sessionCount === 0) {
|
|
916
|
-
sessionStrings.push("No active sessions found");
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
// Update the setting with the formatted session strings
|
|
920
|
-
// Note: storageSettings must be defined in subclasses
|
|
802
|
+
const sessionStrings = await api.getOnlineUserSessionsForUi();
|
|
921
803
|
(this as any).storageSettings.values.userSessions = sessionStrings;
|
|
922
|
-
|
|
923
|
-
logger.log(`[Sessions] Retrieved ${sessionCount} active session(s)`);
|
|
924
|
-
if (socketSessionId) {
|
|
925
|
-
logger.debug(
|
|
926
|
-
`[Sessions] Current socket session ID: ${socketSessionId}`,
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
804
|
} catch (e) {
|
|
930
805
|
const errorMsg = e?.message || String(e);
|
|
931
806
|
logger.error(`[Sessions] Failed to fetch user sessions: ${errorMsg}`);
|
package/src/camera.ts
CHANGED
|
@@ -1264,148 +1264,7 @@ export class ReolinkCamera
|
|
|
1264
1264
|
* Refresh the list of active user sessions from the device
|
|
1265
1265
|
*/
|
|
1266
1266
|
async refreshUserSessionsList(): Promise<void> {
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
try {
|
|
1270
|
-
const api = await this.ensureClient();
|
|
1271
|
-
|
|
1272
|
-
logger.log("[Sessions] Fetching active user sessions from device...");
|
|
1273
|
-
|
|
1274
|
-
const sessions = await api.getOnlineUserList();
|
|
1275
|
-
|
|
1276
|
-
// Get current socket session ID if available
|
|
1277
|
-
let socketSessionId: string | undefined;
|
|
1278
|
-
try {
|
|
1279
|
-
socketSessionId = api.client.getSocketSessionId();
|
|
1280
|
-
} catch {
|
|
1281
|
-
// getSocketSessionId might not be available on all clients
|
|
1282
|
-
}
|
|
1283
|
-
|
|
1284
|
-
// Format sessions as array of readable strings
|
|
1285
|
-
const sessionStrings: string[] = [];
|
|
1286
|
-
|
|
1287
|
-
// Add header with timestamp and socket session ID
|
|
1288
|
-
const timestamp = new Date().toLocaleString();
|
|
1289
|
-
sessionStrings.push(`Last updated: ${timestamp}`);
|
|
1290
|
-
if (socketSessionId) {
|
|
1291
|
-
sessionStrings.push(`Current socket session ID: ${socketSessionId}`);
|
|
1292
|
-
}
|
|
1293
|
-
sessionStrings.push(""); // Empty line separator
|
|
1294
|
-
|
|
1295
|
-
// Parse sessions data (handle different response formats)
|
|
1296
|
-
let sessionCount = 0;
|
|
1297
|
-
const parseSessions = (data: any, prefix: string = ""): void => {
|
|
1298
|
-
if (Array.isArray(data)) {
|
|
1299
|
-
data.forEach((session: any, index: number) => {
|
|
1300
|
-
sessionCount++;
|
|
1301
|
-
const sessionInfo = formatSessionInfo(session, index + 1);
|
|
1302
|
-
sessionStrings.push(sessionInfo);
|
|
1303
|
-
});
|
|
1304
|
-
} else if (data && typeof data === "object") {
|
|
1305
|
-
// Handle nested objects
|
|
1306
|
-
for (const [key, value] of Object.entries(data)) {
|
|
1307
|
-
if (Array.isArray(value)) {
|
|
1308
|
-
value.forEach((session: any, index: number) => {
|
|
1309
|
-
sessionCount++;
|
|
1310
|
-
const sessionInfo = formatSessionInfo(session, index + 1, key);
|
|
1311
|
-
sessionStrings.push(sessionInfo);
|
|
1312
|
-
});
|
|
1313
|
-
} else if (value && typeof value === "object") {
|
|
1314
|
-
parseSessions(value, prefix ? `${prefix}.${key}` : key);
|
|
1315
|
-
} else {
|
|
1316
|
-
// Single session object
|
|
1317
|
-
sessionCount++;
|
|
1318
|
-
const sessionInfo = formatSessionInfo(data, 1);
|
|
1319
|
-
sessionStrings.push(sessionInfo);
|
|
1320
|
-
return; // Only process once
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
}
|
|
1324
|
-
};
|
|
1325
|
-
|
|
1326
|
-
// Helper function to format session info as readable string
|
|
1327
|
-
const formatSessionInfo = (
|
|
1328
|
-
session: any,
|
|
1329
|
-
index: number,
|
|
1330
|
-
group?: string,
|
|
1331
|
-
): string => {
|
|
1332
|
-
const parts: string[] = [];
|
|
1333
|
-
|
|
1334
|
-
if (group) {
|
|
1335
|
-
parts.push(`[${group}]`);
|
|
1336
|
-
}
|
|
1337
|
-
parts.push(`Session ${index}:`);
|
|
1338
|
-
|
|
1339
|
-
// Extract common fields
|
|
1340
|
-
if (session.userName !== undefined) {
|
|
1341
|
-
parts.push(`User: ${session.userName}`);
|
|
1342
|
-
}
|
|
1343
|
-
if (session.user !== undefined) {
|
|
1344
|
-
parts.push(`User: ${session.user}`);
|
|
1345
|
-
}
|
|
1346
|
-
if (session.ip !== undefined) {
|
|
1347
|
-
parts.push(`IP: ${session.ip}`);
|
|
1348
|
-
}
|
|
1349
|
-
if (session.ipAddress !== undefined) {
|
|
1350
|
-
parts.push(`IP: ${session.ipAddress}`);
|
|
1351
|
-
}
|
|
1352
|
-
if (session.port !== undefined) {
|
|
1353
|
-
parts.push(`Port: ${session.port}`);
|
|
1354
|
-
}
|
|
1355
|
-
if (session.sessionId !== undefined) {
|
|
1356
|
-
parts.push(`Session ID: ${session.sessionId}`);
|
|
1357
|
-
}
|
|
1358
|
-
if (session.id !== undefined) {
|
|
1359
|
-
parts.push(`ID: ${session.id}`);
|
|
1360
|
-
}
|
|
1361
|
-
if (session.loginTime !== undefined) {
|
|
1362
|
-
parts.push(`Login Time: ${session.loginTime}`);
|
|
1363
|
-
}
|
|
1364
|
-
if (session.time !== undefined) {
|
|
1365
|
-
parts.push(`Time: ${session.time}`);
|
|
1366
|
-
}
|
|
1367
|
-
if (session.status !== undefined) {
|
|
1368
|
-
parts.push(`Status: ${session.status}`);
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
// If no common fields found, show all fields
|
|
1372
|
-
if (parts.length === (group ? 2 : 1)) {
|
|
1373
|
-
const allFields = Object.entries(session)
|
|
1374
|
-
.map(([key, value]) => `${key}: ${value}`)
|
|
1375
|
-
.join(", ");
|
|
1376
|
-
parts.push(allFields);
|
|
1377
|
-
}
|
|
1378
|
-
|
|
1379
|
-
return parts.join(" | ");
|
|
1380
|
-
};
|
|
1381
|
-
|
|
1382
|
-
parseSessions(sessions);
|
|
1383
|
-
|
|
1384
|
-
// If no sessions found, add a message
|
|
1385
|
-
if (sessionCount === 0) {
|
|
1386
|
-
sessionStrings.push("No active sessions found");
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
// Update the setting with the formatted session strings
|
|
1390
|
-
this.storageSettings.values.userSessions = sessionStrings;
|
|
1391
|
-
|
|
1392
|
-
logger.log(`[Sessions] Retrieved ${sessionCount} active session(s)`);
|
|
1393
|
-
if (socketSessionId) {
|
|
1394
|
-
logger.debug(
|
|
1395
|
-
`[Sessions] Current socket session ID: ${socketSessionId}`,
|
|
1396
|
-
);
|
|
1397
|
-
}
|
|
1398
|
-
} catch (e) {
|
|
1399
|
-
const errorMsg = e?.message || String(e);
|
|
1400
|
-
logger.error(`[Sessions] Failed to fetch user sessions: ${errorMsg}`);
|
|
1401
|
-
|
|
1402
|
-
// Update setting with error message
|
|
1403
|
-
this.storageSettings.values.userSessions = [
|
|
1404
|
-
`Error fetching sessions: ${errorMsg}`,
|
|
1405
|
-
`Timestamp: ${new Date().toLocaleString()}`,
|
|
1406
|
-
];
|
|
1407
|
-
throw e;
|
|
1408
|
-
}
|
|
1267
|
+
return super.refreshUserSessionsList();
|
|
1409
1268
|
}
|
|
1410
1269
|
|
|
1411
1270
|
/**
|
package/src/nvr.ts
CHANGED
|
@@ -120,8 +120,7 @@ export class ReolinkNativeNvrDevice
|
|
|
120
120
|
this.debugLogsResetTimeout = setTimeout(async () => {
|
|
121
121
|
this.debugLogsResetTimeout = undefined;
|
|
122
122
|
try {
|
|
123
|
-
this.
|
|
124
|
-
this.ensureClientPromise = undefined;
|
|
123
|
+
await this.cleanupBaichuanApi();
|
|
125
124
|
await this.ensureBaichuanClient();
|
|
126
125
|
} catch (e) {
|
|
127
126
|
logger.warn(
|
|
@@ -600,9 +599,21 @@ export class ReolinkNativeNvrDevice
|
|
|
600
599
|
channel: entry.rtspChannel,
|
|
601
600
|
uid,
|
|
602
601
|
});
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
602
|
+
let capabilities: any;
|
|
603
|
+
let objects: any;
|
|
604
|
+
let presets: any;
|
|
605
|
+
try {
|
|
606
|
+
await baichuanApi.login();
|
|
607
|
+
({ capabilities, objects, presets } =
|
|
608
|
+
await baichuanApi.getDeviceCapabilities(entry.rtspChannel));
|
|
609
|
+
} finally {
|
|
610
|
+
// Ensure the temporary socket used for adoption is not leaked.
|
|
611
|
+
try {
|
|
612
|
+
await baichuanApi.close({ reason: "adoptDevice" });
|
|
613
|
+
} catch {
|
|
614
|
+
// ignore
|
|
615
|
+
}
|
|
616
|
+
}
|
|
606
617
|
const { interfaces, type } = getDeviceInterfaces({
|
|
607
618
|
capabilities,
|
|
608
619
|
logger: this.getBaichuanLogger(),
|
package/src/stream-utils.ts
CHANGED
|
@@ -420,7 +420,7 @@ export class StreamManager {
|
|
|
420
420
|
? Boolean(compositeApis) && !(this.opts.sharedConnection ?? false)
|
|
421
421
|
: !(this.opts.sharedConnection ?? false);
|
|
422
422
|
|
|
423
|
-
let created:
|
|
423
|
+
let created: Rfc4571TcpServer;
|
|
424
424
|
try {
|
|
425
425
|
const compositeOptions = isComposite
|
|
426
426
|
? options.compositeOptions
|