livedesk 0.1.102 → 0.1.103
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/hub/src/remote-hub.js
CHANGED
|
@@ -1211,6 +1211,7 @@ export function createRemoteHub(options = {}) {
|
|
|
1211
1211
|
const taskBatches = new Map();
|
|
1212
1212
|
const sockets = new Map();
|
|
1213
1213
|
const inputSockets = new Map();
|
|
1214
|
+
const frameSockets = new Map();
|
|
1214
1215
|
const allSockets = new Set();
|
|
1215
1216
|
const duplicateDeviceLogAt = new Map();
|
|
1216
1217
|
let server = null;
|
|
@@ -1745,6 +1746,7 @@ export function createRemoteHub(options = {}) {
|
|
|
1745
1746
|
function closeExistingDeviceSocket(deviceId, nextSessionId) {
|
|
1746
1747
|
const existing = devices.get(deviceId);
|
|
1747
1748
|
closeInputSocket(existing, 'replaced-by-new-session');
|
|
1749
|
+
closeFrameSocket(existing, 'replaced-by-new-session');
|
|
1748
1750
|
if (!existing?.socket || existing.socket.destroyed) {
|
|
1749
1751
|
return;
|
|
1750
1752
|
}
|
|
@@ -1759,6 +1761,26 @@ export function createRemoteHub(options = {}) {
|
|
|
1759
1761
|
existing.socket.destroy();
|
|
1760
1762
|
}
|
|
1761
1763
|
|
|
1764
|
+
function closeFrameSocket(device, reason = 'frame-socket-closed') {
|
|
1765
|
+
const socket = device?.frameSocket;
|
|
1766
|
+
if (!socket) {
|
|
1767
|
+
return;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
device.frameSocket = null;
|
|
1771
|
+
frameSockets.delete(socket);
|
|
1772
|
+
try {
|
|
1773
|
+
writeJsonLine(socket, { type: 'disconnect', channel: 'frame', reason });
|
|
1774
|
+
} catch {
|
|
1775
|
+
// Best-effort notice before closing the side channel.
|
|
1776
|
+
}
|
|
1777
|
+
try {
|
|
1778
|
+
socket.destroy?.();
|
|
1779
|
+
} catch {
|
|
1780
|
+
// Ignore close failures.
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1762
1784
|
function closeInputSocket(device, reason = 'input-socket-closed') {
|
|
1763
1785
|
const socket = device?.inputSocket;
|
|
1764
1786
|
if (!socket) {
|
|
@@ -1796,6 +1818,23 @@ export function createRemoteHub(options = {}) {
|
|
|
1796
1818
|
emitRemoteEvent('RemoteInputSocketDisconnected', device, { reason });
|
|
1797
1819
|
}
|
|
1798
1820
|
|
|
1821
|
+
function detachFrameSocket(socket, reason = 'frame-socket-closed') {
|
|
1822
|
+
const deviceId = frameSockets.get(socket);
|
|
1823
|
+
frameSockets.delete(socket);
|
|
1824
|
+
if (!deviceId) {
|
|
1825
|
+
return;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
const device = devices.get(deviceId);
|
|
1829
|
+
if (!device || device.frameSocket !== socket) {
|
|
1830
|
+
return;
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
device.frameSocket = null;
|
|
1834
|
+
device.frameLastSeenAt = new Date().toISOString();
|
|
1835
|
+
emitRemoteEvent('RemoteFrameSocketDisconnected', device, { reason });
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1799
1838
|
function getDeviceActivityMs(device) {
|
|
1800
1839
|
return Math.max(
|
|
1801
1840
|
Date.parse(device?.lastSeenAt || '') || 0,
|
|
@@ -2181,6 +2220,7 @@ export function createRemoteHub(options = {}) {
|
|
|
2181
2220
|
const device = {
|
|
2182
2221
|
socket,
|
|
2183
2222
|
inputSocket: null,
|
|
2223
|
+
frameSocket: null,
|
|
2184
2224
|
deviceId,
|
|
2185
2225
|
sessionId,
|
|
2186
2226
|
deviceName: safeString(hello.deviceName || hello.hostname || deviceId, 120),
|
|
@@ -2199,6 +2239,8 @@ export function createRemoteHub(options = {}) {
|
|
|
2199
2239
|
connectedAt: now,
|
|
2200
2240
|
inputConnectedAt: '',
|
|
2201
2241
|
inputLastSeenAt: '',
|
|
2242
|
+
frameConnectedAt: '',
|
|
2243
|
+
frameLastSeenAt: '',
|
|
2202
2244
|
disconnectedAt: '',
|
|
2203
2245
|
lastSeenAt: now,
|
|
2204
2246
|
lastStatusAt: '',
|
|
@@ -2273,6 +2315,39 @@ export function createRemoteHub(options = {}) {
|
|
|
2273
2315
|
return device;
|
|
2274
2316
|
}
|
|
2275
2317
|
|
|
2318
|
+
function attachFrameSocket(socket, hello) {
|
|
2319
|
+
const deviceId = normalizeDeviceId(hello.deviceId);
|
|
2320
|
+
const device = devices.get(deviceId);
|
|
2321
|
+
if (!device || !device.connected || !device.socket || device.socket.destroyed) {
|
|
2322
|
+
writeJsonLine(socket, { type: 'error', error: 'device-not-connected' });
|
|
2323
|
+
socket.destroy();
|
|
2324
|
+
return null;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
closeFrameSocket(device, 'replaced-by-new-frame-socket');
|
|
2328
|
+
|
|
2329
|
+
const now = new Date().toISOString();
|
|
2330
|
+
device.frameSocket = socket;
|
|
2331
|
+
device.frameConnectedAt = now;
|
|
2332
|
+
device.frameLastSeenAt = now;
|
|
2333
|
+
frameSockets.set(socket, deviceId);
|
|
2334
|
+
|
|
2335
|
+
writeJsonLine(socket, {
|
|
2336
|
+
type: 'welcome',
|
|
2337
|
+
channel: 'frame',
|
|
2338
|
+
protocol: REMOTE_AGENT_PROTOCOL,
|
|
2339
|
+
protocolVersion: REMOTE_PROTOCOL_VERSION,
|
|
2340
|
+
sessionId: device.sessionId,
|
|
2341
|
+
deviceId,
|
|
2342
|
+
frameProtocol: buildRemoteFrameProtocolDescriptor(),
|
|
2343
|
+
frameModes: getSupportedRemoteFrameModeProfiles(),
|
|
2344
|
+
serverTime: now
|
|
2345
|
+
});
|
|
2346
|
+
|
|
2347
|
+
emitRemoteEvent('RemoteFrameSocketConnected', device);
|
|
2348
|
+
return device;
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2276
2351
|
function detachSocket(socket, reason = 'socket-closed') {
|
|
2277
2352
|
const deviceId = sockets.get(socket);
|
|
2278
2353
|
sockets.delete(socket);
|
|
@@ -2288,6 +2363,7 @@ export function createRemoteHub(options = {}) {
|
|
|
2288
2363
|
device.connected = false;
|
|
2289
2364
|
device.socket = null;
|
|
2290
2365
|
closeInputSocket(device, reason);
|
|
2366
|
+
closeFrameSocket(device, reason);
|
|
2291
2367
|
device.disconnectedAt = new Date().toISOString();
|
|
2292
2368
|
device.lastDisconnectReason = reason;
|
|
2293
2369
|
if (device.activeLiveStream) {
|
|
@@ -3060,6 +3136,9 @@ export function createRemoteHub(options = {}) {
|
|
|
3060
3136
|
|
|
3061
3137
|
device.counters.messagesReceived += 1;
|
|
3062
3138
|
device.lastSeenAt = new Date().toISOString();
|
|
3139
|
+
if (state.frameOnly) {
|
|
3140
|
+
device.frameLastSeenAt = device.lastSeenAt;
|
|
3141
|
+
}
|
|
3063
3142
|
|
|
3064
3143
|
const frameKind = safeString(header.frameKind || header.kind || header.frameType || '', 40).toLowerCase();
|
|
3065
3144
|
if (frameKind === 'thumbnail' || header.type === 'thumbnail.binary') {
|
|
@@ -3113,6 +3192,17 @@ export function createRemoteHub(options = {}) {
|
|
|
3113
3192
|
state.inputOnly = true;
|
|
3114
3193
|
return;
|
|
3115
3194
|
}
|
|
3195
|
+
if (channel === 'frame') {
|
|
3196
|
+
const device = attachFrameSocket(socket, message);
|
|
3197
|
+
if (!device) {
|
|
3198
|
+
return;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
state.authenticated = true;
|
|
3202
|
+
state.device = device;
|
|
3203
|
+
state.frameOnly = true;
|
|
3204
|
+
return;
|
|
3205
|
+
}
|
|
3116
3206
|
|
|
3117
3207
|
const device = attachDevice(socket, message);
|
|
3118
3208
|
if (!device) {
|
|
@@ -3134,6 +3224,10 @@ export function createRemoteHub(options = {}) {
|
|
|
3134
3224
|
device.inputLastSeenAt = new Date().toISOString();
|
|
3135
3225
|
return;
|
|
3136
3226
|
}
|
|
3227
|
+
if (state.frameOnly) {
|
|
3228
|
+
device.frameLastSeenAt = new Date().toISOString();
|
|
3229
|
+
return;
|
|
3230
|
+
}
|
|
3137
3231
|
|
|
3138
3232
|
device.counters.messagesReceived += 1;
|
|
3139
3233
|
device.lastSeenAt = new Date().toISOString();
|
|
@@ -3207,7 +3301,8 @@ export function createRemoteHub(options = {}) {
|
|
|
3207
3301
|
const state = {
|
|
3208
3302
|
authenticated: false,
|
|
3209
3303
|
device: null,
|
|
3210
|
-
inputOnly: false
|
|
3304
|
+
inputOnly: false,
|
|
3305
|
+
frameOnly: false
|
|
3211
3306
|
};
|
|
3212
3307
|
|
|
3213
3308
|
const helloTimer = setTimeout(() => {
|
|
@@ -3259,6 +3354,7 @@ export function createRemoteHub(options = {}) {
|
|
|
3259
3354
|
allSockets.delete(socket);
|
|
3260
3355
|
clearTimeout(helloTimer);
|
|
3261
3356
|
detachInputSocket(socket, reason);
|
|
3357
|
+
detachFrameSocket(socket, reason);
|
|
3262
3358
|
detachSocket(socket, reason);
|
|
3263
3359
|
};
|
|
3264
3360
|
|
|
@@ -4009,6 +4105,21 @@ export function createRemoteHub(options = {}) {
|
|
|
4009
4105
|
const normalized = normalizeLiveStreamStartOptions(options);
|
|
4010
4106
|
const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer } = normalized;
|
|
4011
4107
|
const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
|
|
4108
|
+
if (device.activeLiveStream?.streamId === streamId
|
|
4109
|
+
&& options.forceRestart !== true
|
|
4110
|
+
&& options.reuseExisting === true
|
|
4111
|
+
&& liveStreamIsReusable(device.activeLiveStream)) {
|
|
4112
|
+
return {
|
|
4113
|
+
ok: true,
|
|
4114
|
+
commandId: device.activeLiveStream.commandId,
|
|
4115
|
+
streamId,
|
|
4116
|
+
fps: Number(device.activeLiveStream.fps || fps),
|
|
4117
|
+
mode: device.activeLiveStream.mode || transfer.mode,
|
|
4118
|
+
frameMode: device.activeLiveStream.frameMode || transfer.frameMode,
|
|
4119
|
+
monitorIndex: Number(device.activeLiveStream.monitorIndex || monitorIndex),
|
|
4120
|
+
reused: true
|
|
4121
|
+
};
|
|
4122
|
+
}
|
|
4012
4123
|
if (device.activeLiveStream?.streamId === streamId
|
|
4013
4124
|
&& options.forceRestart !== true
|
|
4014
4125
|
&& liveStreamMatchesOptions(device.activeLiveStream, normalized)
|
package/hub/src/server.js
CHANGED
|
@@ -27,7 +27,6 @@ const audioClients = new Set();
|
|
|
27
27
|
let frameClientSeq = 0;
|
|
28
28
|
let inputClientSeq = 0;
|
|
29
29
|
let audioClientSeq = 0;
|
|
30
|
-
const FRAME_LIVE_WATCHDOG_MS = 2500;
|
|
31
30
|
|
|
32
31
|
function handleRemoteHubEvent(type, event) {
|
|
33
32
|
if (type !== 'RemoteDeviceConnected') {
|
|
@@ -221,6 +220,9 @@ function safeWebSocketSend(ws, payload, options = undefined) {
|
|
|
221
220
|
}
|
|
222
221
|
|
|
223
222
|
function updateFrameSubscription(ws, payload = {}) {
|
|
223
|
+
const previousDeviceIds = ws.liveDeskDeviceIds instanceof Set
|
|
224
|
+
? new Set(ws.liveDeskDeviceIds)
|
|
225
|
+
: new Set();
|
|
224
226
|
const deviceIds = normalizeDeviceIds(payload.deviceIds ?? payload.devices ?? payload.deviceId);
|
|
225
227
|
ws.liveDeskDeviceIds = new Set(deviceIds);
|
|
226
228
|
ws.liveDeskAutoStart = /^(1|true|yes|on|live)$/i.test(String(payload.autoStartLive ?? payload.startLive ?? ''));
|
|
@@ -232,7 +234,11 @@ function updateFrameSubscription(ws, payload = {}) {
|
|
|
232
234
|
autoStartLive: ws.liveDeskAutoStart
|
|
233
235
|
});
|
|
234
236
|
|
|
235
|
-
|
|
237
|
+
const newlySubscribedIds = deviceIds.filter(deviceId => !previousDeviceIds.has(deviceId));
|
|
238
|
+
const targetIds = newlySubscribedIds.length > 0 ? newlySubscribedIds : deviceIds;
|
|
239
|
+
for (const deviceId of targetIds.slice(0, 80)) {
|
|
240
|
+
startFrameSubscriptionLive(ws, 'subscribe', deviceId, { reuseExisting: true });
|
|
241
|
+
}
|
|
236
242
|
}
|
|
237
243
|
|
|
238
244
|
function startFrameSubscriptionLive(ws, reason = 'subscribe', onlyDeviceId = '', overrideLiveOptions = null) {
|
|
@@ -271,6 +277,7 @@ function startFrameSubscriptionLive(ws, reason = 'subscribe', onlyDeviceId = '',
|
|
|
271
277
|
const result = remoteHub.startLiveStream(deviceId, {
|
|
272
278
|
...liveOptions,
|
|
273
279
|
monitorIndex,
|
|
280
|
+
reuseExisting: liveOptions.reuseExisting === true || reason === 'subscribe' || reason === 'watchdog',
|
|
274
281
|
silentReuse: reason === 'watchdog' && liveOptions.forceRestart !== true
|
|
275
282
|
});
|
|
276
283
|
if (result?.ok) {
|
|
@@ -761,14 +768,7 @@ httpServer.on('upgrade', (req, socket, head) => {
|
|
|
761
768
|
frameWss.on('connection', (ws, req) => {
|
|
762
769
|
frameClients.add(ws);
|
|
763
770
|
ws.liveDeskFrameClientId = `rfws-${++frameClientSeq}`;
|
|
764
|
-
const liveWatchdog = setInterval(() => {
|
|
765
|
-
if (ws.readyState === 1) {
|
|
766
|
-
startFrameSubscriptionLive(ws, 'watchdog');
|
|
767
|
-
}
|
|
768
|
-
}, FRAME_LIVE_WATCHDOG_MS);
|
|
769
|
-
liveWatchdog.unref?.();
|
|
770
771
|
const cleanup = () => {
|
|
771
|
-
clearInterval(liveWatchdog);
|
|
772
772
|
frameClients.delete(ws);
|
|
773
773
|
};
|
|
774
774
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livedesk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.103",
|
|
4
4
|
"description": "LiveDesk Hub and client launcher",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"node": ">=20"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@livedesk/client": "0.1.
|
|
33
|
+
"@livedesk/client": "0.1.75",
|
|
34
34
|
"cors": "^2.8.5",
|
|
35
35
|
"express": "^4.21.2",
|
|
36
36
|
"ws": "^8.18.3"
|