livedesk 0.1.37 → 0.1.39
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 +127 -14
- package/hub/src/server.js +2 -2
- package/package.json +2 -2
- package/web/dist/assets/index-CIFEp9yR.js +38 -0
- package/web/dist/index.html +1 -1
- package/web/dist/assets/index-CYkNgzXZ.js +0 -38
package/hub/src/remote-hub.js
CHANGED
|
@@ -2657,6 +2657,19 @@ export function createRemoteHub(options = {}) {
|
|
|
2657
2657
|
return false;
|
|
2658
2658
|
}
|
|
2659
2659
|
|
|
2660
|
+
const commandId = safeString(message.commandId, 128);
|
|
2661
|
+
const activeCommandId = safeString(device.activeLiveStream.commandId, 128);
|
|
2662
|
+
if (commandId && activeCommandId && commandId !== activeCommandId) {
|
|
2663
|
+
emitRemoteEvent('RemoteLiveStreamOpenIgnored', device, {
|
|
2664
|
+
reason: 'stale-live-stream-generation',
|
|
2665
|
+
streamId,
|
|
2666
|
+
commandId,
|
|
2667
|
+
activeCommandId,
|
|
2668
|
+
transport
|
|
2669
|
+
});
|
|
2670
|
+
return false;
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2660
2673
|
const transfer = buildRemoteFrameTransferDescriptorFromMessage(message, device.activeLiveStream.mode || DEFAULT_REMOTE_FRAME_MODE);
|
|
2661
2674
|
const openedAt = safeString(message.openedAt, 80) || device.lastSeenAt || new Date().toISOString();
|
|
2662
2675
|
device.activeLiveStream = {
|
|
@@ -2697,6 +2710,7 @@ export function createRemoteHub(options = {}) {
|
|
|
2697
2710
|
: safeString(framePayload, MAX_STREAM_BASE64_CHARS + 1);
|
|
2698
2711
|
const frameSeq = Number(message.frameSeq);
|
|
2699
2712
|
const streamId = safeString(message.streamId, 128) || 'live';
|
|
2713
|
+
const commandId = safeString(message.commandId, 128);
|
|
2700
2714
|
if (!device.activeLiveStream?.active || device.activeLiveStream.streamId !== streamId) {
|
|
2701
2715
|
device.counters.liveFramesDropped += 1;
|
|
2702
2716
|
emitRemoteEvent('RemoteFrameDropped', device, {
|
|
@@ -2707,6 +2721,19 @@ export function createRemoteHub(options = {}) {
|
|
|
2707
2721
|
});
|
|
2708
2722
|
return false;
|
|
2709
2723
|
}
|
|
2724
|
+
const activeCommandId = safeString(device.activeLiveStream.commandId, 128);
|
|
2725
|
+
if (commandId && activeCommandId && commandId !== activeCommandId) {
|
|
2726
|
+
device.counters.liveFramesDropped += 1;
|
|
2727
|
+
emitRemoteEvent('RemoteFrameDropped', device, {
|
|
2728
|
+
reason: 'stale-live-stream-generation-frame',
|
|
2729
|
+
streamId,
|
|
2730
|
+
commandId,
|
|
2731
|
+
activeCommandId,
|
|
2732
|
+
frameSeq: Number.isFinite(frameSeq) ? frameSeq : null,
|
|
2733
|
+
transport
|
|
2734
|
+
});
|
|
2735
|
+
return false;
|
|
2736
|
+
}
|
|
2710
2737
|
|
|
2711
2738
|
const byteLength = normalizeFrameByteLength(framePayload, frameData);
|
|
2712
2739
|
if ((!Buffer.isBuffer(framePayload) && !frameData)
|
|
@@ -2745,7 +2772,7 @@ export function createRemoteHub(options = {}) {
|
|
|
2745
2772
|
device.latestLiveFrame = {
|
|
2746
2773
|
streamId,
|
|
2747
2774
|
frameSeq,
|
|
2748
|
-
commandId
|
|
2775
|
+
commandId,
|
|
2749
2776
|
width: Number.isFinite(Number(message.width)) ? Number(message.width) : 0,
|
|
2750
2777
|
height: Number.isFinite(Number(message.height)) ? Number(message.height) : 0,
|
|
2751
2778
|
mimeType,
|
|
@@ -3659,6 +3686,41 @@ export function createRemoteHub(options = {}) {
|
|
|
3659
3686
|
});
|
|
3660
3687
|
}
|
|
3661
3688
|
|
|
3689
|
+
function makeStableLiveStreamId(deviceId) {
|
|
3690
|
+
const hash = crypto.createHash('sha1').update(String(deviceId || 'device')).digest('hex').slice(0, 16);
|
|
3691
|
+
return `live-${hash}`;
|
|
3692
|
+
}
|
|
3693
|
+
|
|
3694
|
+
function normalizeLiveStreamStartOptions(options = {}) {
|
|
3695
|
+
const fps = clampNumber(options.fps, 1, 24, 8);
|
|
3696
|
+
const maxWidth = clampNumber(options.maxWidth, 320, 2560, 640);
|
|
3697
|
+
const maxHeight = clampNumber(options.maxHeight, 180, 1440, 360);
|
|
3698
|
+
const quality = clampNumber(options.quality, 20, 95, 45);
|
|
3699
|
+
const monitorIndex = normalizeMonitorIndex(options.monitorIndex ?? options.screenIndex ?? options.displayIndex);
|
|
3700
|
+
const transfer = buildRemoteFrameTransferDescriptor(options.mode || options.frameMode || DEFAULT_REMOTE_FRAME_MODE);
|
|
3701
|
+
return { fps, maxWidth, maxHeight, quality, monitorIndex, transfer };
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3704
|
+
function liveStreamStartStillPending(activeLiveStream) {
|
|
3705
|
+
if (!activeLiveStream?.active || activeLiveStream.open === true) {
|
|
3706
|
+
return false;
|
|
3707
|
+
}
|
|
3708
|
+
const startedAt = Date.parse(activeLiveStream.startedAt || '');
|
|
3709
|
+
return Number.isFinite(startedAt) && Date.now() - startedAt < 5000;
|
|
3710
|
+
}
|
|
3711
|
+
|
|
3712
|
+
function liveStreamMatchesOptions(activeLiveStream, normalized) {
|
|
3713
|
+
if (!activeLiveStream?.active) {
|
|
3714
|
+
return false;
|
|
3715
|
+
}
|
|
3716
|
+
return activeLiveStream.frameMode === normalized.transfer.frameMode
|
|
3717
|
+
&& Number(activeLiveStream.fps || 0) === normalized.fps
|
|
3718
|
+
&& Number(activeLiveStream.maxWidth || 0) === normalized.maxWidth
|
|
3719
|
+
&& Number(activeLiveStream.maxHeight || 0) === normalized.maxHeight
|
|
3720
|
+
&& Number(activeLiveStream.quality || 0) === normalized.quality
|
|
3721
|
+
&& Number(activeLiveStream.monitorIndex || 0) === normalized.monitorIndex;
|
|
3722
|
+
}
|
|
3723
|
+
|
|
3662
3724
|
function startLiveStream(deviceId, options = {}) {
|
|
3663
3725
|
const device = devices.get(String(deviceId || ''));
|
|
3664
3726
|
if (device?.synthetic === true && device.connected) {
|
|
@@ -3667,11 +3729,25 @@ export function createRemoteHub(options = {}) {
|
|
|
3667
3729
|
}
|
|
3668
3730
|
|
|
3669
3731
|
const now = new Date().toISOString();
|
|
3670
|
-
const streamId = safeString(options.streamId, 128) ||
|
|
3671
|
-
const
|
|
3672
|
-
const
|
|
3732
|
+
const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId);
|
|
3733
|
+
const normalized = normalizeLiveStreamStartOptions({ fps: 2, ...options });
|
|
3734
|
+
const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer } = normalized;
|
|
3673
3735
|
const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
|
|
3674
|
-
|
|
3736
|
+
if (device.activeLiveStream?.streamId === streamId
|
|
3737
|
+
&& liveStreamMatchesOptions(device.activeLiveStream, normalized)
|
|
3738
|
+
&& (device.activeLiveStream.open === true || liveStreamStartStillPending(device.activeLiveStream))) {
|
|
3739
|
+
return {
|
|
3740
|
+
ok: true,
|
|
3741
|
+
commandId: device.activeLiveStream.commandId,
|
|
3742
|
+
streamId,
|
|
3743
|
+
fps,
|
|
3744
|
+
mode: transfer.mode,
|
|
3745
|
+
frameMode: transfer.frameMode,
|
|
3746
|
+
monitorIndex,
|
|
3747
|
+
synthetic: true,
|
|
3748
|
+
reused: true
|
|
3749
|
+
};
|
|
3750
|
+
}
|
|
3675
3751
|
device.counters.commandsSent += 1;
|
|
3676
3752
|
device.activeLiveStream = {
|
|
3677
3753
|
streamId,
|
|
@@ -3690,6 +3766,9 @@ export function createRemoteHub(options = {}) {
|
|
|
3690
3766
|
transferProtocolVersion: transfer.transferProtocolVersion,
|
|
3691
3767
|
handshake: transfer.handshake,
|
|
3692
3768
|
fps,
|
|
3769
|
+
maxWidth,
|
|
3770
|
+
maxHeight,
|
|
3771
|
+
quality,
|
|
3693
3772
|
monitorIndex,
|
|
3694
3773
|
monitorCount: 1,
|
|
3695
3774
|
startedAt: now,
|
|
@@ -3702,8 +3781,8 @@ export function createRemoteHub(options = {}) {
|
|
|
3702
3781
|
device.counters.liveStreamsStarted += 1;
|
|
3703
3782
|
applySyntheticLiveFrame(device, streamId, {
|
|
3704
3783
|
commandId,
|
|
3705
|
-
maxWidth
|
|
3706
|
-
maxHeight
|
|
3784
|
+
maxWidth,
|
|
3785
|
+
maxHeight,
|
|
3707
3786
|
monitorIndex,
|
|
3708
3787
|
fps
|
|
3709
3788
|
});
|
|
@@ -3725,11 +3804,42 @@ export function createRemoteHub(options = {}) {
|
|
|
3725
3804
|
}
|
|
3726
3805
|
|
|
3727
3806
|
const now = new Date().toISOString();
|
|
3728
|
-
const streamId = safeString(options.streamId, 128) ||
|
|
3729
|
-
const
|
|
3730
|
-
const
|
|
3807
|
+
const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId);
|
|
3808
|
+
const normalized = normalizeLiveStreamStartOptions(options);
|
|
3809
|
+
const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer } = normalized;
|
|
3731
3810
|
const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
|
|
3732
|
-
|
|
3811
|
+
if (device.activeLiveStream?.streamId === streamId
|
|
3812
|
+
&& liveStreamMatchesOptions(device.activeLiveStream, normalized)
|
|
3813
|
+
&& (device.activeLiveStream.open === true || liveStreamStartStillPending(device.activeLiveStream))) {
|
|
3814
|
+
emitRemoteEvent('RemoteLiveStreamStartReused', device, {
|
|
3815
|
+
streamId,
|
|
3816
|
+
fps,
|
|
3817
|
+
mode: transfer.mode,
|
|
3818
|
+
frameMode: transfer.frameMode,
|
|
3819
|
+
monitorIndex
|
|
3820
|
+
});
|
|
3821
|
+
return {
|
|
3822
|
+
ok: true,
|
|
3823
|
+
commandId: device.activeLiveStream.commandId,
|
|
3824
|
+
streamId,
|
|
3825
|
+
fps,
|
|
3826
|
+
mode: transfer.mode,
|
|
3827
|
+
frameMode: transfer.frameMode,
|
|
3828
|
+
monitorIndex,
|
|
3829
|
+
reused: true
|
|
3830
|
+
};
|
|
3831
|
+
}
|
|
3832
|
+
if (device.activeLiveStream?.active && device.activeLiveStream.streamId && device.activeLiveStream.streamId !== streamId) {
|
|
3833
|
+
sendCommand(deviceId, {
|
|
3834
|
+
command: 'stream.stop',
|
|
3835
|
+
commandId: crypto.randomUUID(),
|
|
3836
|
+
payload: {
|
|
3837
|
+
streamId: device.activeLiveStream.streamId,
|
|
3838
|
+
reason: 'live-stream-replaced',
|
|
3839
|
+
requestedAt: now
|
|
3840
|
+
}
|
|
3841
|
+
});
|
|
3842
|
+
}
|
|
3733
3843
|
const result = sendCommand(deviceId, {
|
|
3734
3844
|
command: 'stream.start',
|
|
3735
3845
|
commandId,
|
|
@@ -3745,9 +3855,9 @@ export function createRemoteHub(options = {}) {
|
|
|
3745
3855
|
transferProtocolVersion: transfer.transferProtocolVersion,
|
|
3746
3856
|
handshake: transfer.handshake,
|
|
3747
3857
|
fps,
|
|
3748
|
-
maxWidth
|
|
3749
|
-
maxHeight
|
|
3750
|
-
quality
|
|
3858
|
+
maxWidth,
|
|
3859
|
+
maxHeight,
|
|
3860
|
+
quality,
|
|
3751
3861
|
monitorIndex,
|
|
3752
3862
|
requestedAt: now
|
|
3753
3863
|
}
|
|
@@ -3774,6 +3884,9 @@ export function createRemoteHub(options = {}) {
|
|
|
3774
3884
|
transferProtocolVersion: transfer.transferProtocolVersion,
|
|
3775
3885
|
handshake: transfer.handshake,
|
|
3776
3886
|
fps,
|
|
3887
|
+
maxWidth,
|
|
3888
|
+
maxHeight,
|
|
3889
|
+
quality,
|
|
3777
3890
|
monitorIndex,
|
|
3778
3891
|
monitorCount: 1,
|
|
3779
3892
|
startedAt: now,
|
package/hub/src/server.js
CHANGED
|
@@ -205,8 +205,7 @@ function updateFrameSubscription(ws, payload = {}) {
|
|
|
205
205
|
: ws.liveDeskLiveOptions.monitorIndex;
|
|
206
206
|
const result = remoteHub.startLiveStream(deviceId, {
|
|
207
207
|
...ws.liveDeskLiveOptions,
|
|
208
|
-
monitorIndex
|
|
209
|
-
streamId: `livedesk-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
|
208
|
+
monitorIndex
|
|
210
209
|
});
|
|
211
210
|
if (result?.ok) {
|
|
212
211
|
started.push({ deviceId, streamId: result.streamId, fps: result.fps });
|
|
@@ -245,6 +244,7 @@ function buildRemoteFrameBinaryPacket(frameEvent) {
|
|
|
245
244
|
deviceId: frameEvent.deviceId || frame.deviceId || '',
|
|
246
245
|
frameSeq: Number(frame.frameSeq || 0) || 0,
|
|
247
246
|
streamId: frame.streamId || '',
|
|
247
|
+
commandId: frame.commandId || '',
|
|
248
248
|
width: Number(frame.width || 0) || 0,
|
|
249
249
|
height: Number(frame.height || 0) || 0,
|
|
250
250
|
mimeType: frameEvent.mimeType || frame.mimeType || frame.format || 'image/jpeg',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livedesk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
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.37",
|
|
34
34
|
"cors": "^2.8.5",
|
|
35
35
|
"express": "^4.21.2",
|
|
36
36
|
"ws": "^8.18.3"
|