livedesk 0.1.37 → 0.1.38

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.
@@ -3659,6 +3659,41 @@ export function createRemoteHub(options = {}) {
3659
3659
  });
3660
3660
  }
3661
3661
 
3662
+ function makeStableLiveStreamId(deviceId) {
3663
+ const hash = crypto.createHash('sha1').update(String(deviceId || 'device')).digest('hex').slice(0, 16);
3664
+ return `live-${hash}`;
3665
+ }
3666
+
3667
+ function normalizeLiveStreamStartOptions(options = {}) {
3668
+ const fps = clampNumber(options.fps, 1, 24, 8);
3669
+ const maxWidth = clampNumber(options.maxWidth, 320, 2560, 640);
3670
+ const maxHeight = clampNumber(options.maxHeight, 180, 1440, 360);
3671
+ const quality = clampNumber(options.quality, 20, 95, 45);
3672
+ const monitorIndex = normalizeMonitorIndex(options.monitorIndex ?? options.screenIndex ?? options.displayIndex);
3673
+ const transfer = buildRemoteFrameTransferDescriptor(options.mode || options.frameMode || DEFAULT_REMOTE_FRAME_MODE);
3674
+ return { fps, maxWidth, maxHeight, quality, monitorIndex, transfer };
3675
+ }
3676
+
3677
+ function liveStreamStartStillPending(activeLiveStream) {
3678
+ if (!activeLiveStream?.active || activeLiveStream.open === true) {
3679
+ return false;
3680
+ }
3681
+ const startedAt = Date.parse(activeLiveStream.startedAt || '');
3682
+ return Number.isFinite(startedAt) && Date.now() - startedAt < 5000;
3683
+ }
3684
+
3685
+ function liveStreamMatchesOptions(activeLiveStream, normalized) {
3686
+ if (!activeLiveStream?.active) {
3687
+ return false;
3688
+ }
3689
+ return activeLiveStream.frameMode === normalized.transfer.frameMode
3690
+ && Number(activeLiveStream.fps || 0) === normalized.fps
3691
+ && Number(activeLiveStream.maxWidth || 0) === normalized.maxWidth
3692
+ && Number(activeLiveStream.maxHeight || 0) === normalized.maxHeight
3693
+ && Number(activeLiveStream.quality || 0) === normalized.quality
3694
+ && Number(activeLiveStream.monitorIndex || 0) === normalized.monitorIndex;
3695
+ }
3696
+
3662
3697
  function startLiveStream(deviceId, options = {}) {
3663
3698
  const device = devices.get(String(deviceId || ''));
3664
3699
  if (device?.synthetic === true && device.connected) {
@@ -3667,11 +3702,25 @@ export function createRemoteHub(options = {}) {
3667
3702
  }
3668
3703
 
3669
3704
  const now = new Date().toISOString();
3670
- const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
3671
- const fps = clampNumber(options.fps, 1, 24, 2);
3672
- const monitorIndex = normalizeMonitorIndex(options.monitorIndex ?? options.screenIndex ?? options.displayIndex);
3705
+ const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId);
3706
+ const normalized = normalizeLiveStreamStartOptions({ fps: 2, ...options });
3707
+ const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer } = normalized;
3673
3708
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
3674
- const transfer = buildRemoteFrameTransferDescriptor(options.mode || DEFAULT_REMOTE_FRAME_MODE);
3709
+ if (device.activeLiveStream?.streamId === streamId
3710
+ && liveStreamMatchesOptions(device.activeLiveStream, normalized)
3711
+ && (device.activeLiveStream.open === true || liveStreamStartStillPending(device.activeLiveStream))) {
3712
+ return {
3713
+ ok: true,
3714
+ commandId: device.activeLiveStream.commandId,
3715
+ streamId,
3716
+ fps,
3717
+ mode: transfer.mode,
3718
+ frameMode: transfer.frameMode,
3719
+ monitorIndex,
3720
+ synthetic: true,
3721
+ reused: true
3722
+ };
3723
+ }
3675
3724
  device.counters.commandsSent += 1;
3676
3725
  device.activeLiveStream = {
3677
3726
  streamId,
@@ -3690,6 +3739,9 @@ export function createRemoteHub(options = {}) {
3690
3739
  transferProtocolVersion: transfer.transferProtocolVersion,
3691
3740
  handshake: transfer.handshake,
3692
3741
  fps,
3742
+ maxWidth,
3743
+ maxHeight,
3744
+ quality,
3693
3745
  monitorIndex,
3694
3746
  monitorCount: 1,
3695
3747
  startedAt: now,
@@ -3702,8 +3754,8 @@ export function createRemoteHub(options = {}) {
3702
3754
  device.counters.liveStreamsStarted += 1;
3703
3755
  applySyntheticLiveFrame(device, streamId, {
3704
3756
  commandId,
3705
- maxWidth: options.maxWidth,
3706
- maxHeight: options.maxHeight,
3757
+ maxWidth,
3758
+ maxHeight,
3707
3759
  monitorIndex,
3708
3760
  fps
3709
3761
  });
@@ -3725,11 +3777,42 @@ export function createRemoteHub(options = {}) {
3725
3777
  }
3726
3778
 
3727
3779
  const now = new Date().toISOString();
3728
- const streamId = safeString(options.streamId, 128) || `live-${Date.now()}`;
3729
- const fps = clampNumber(options.fps, 1, 24, 8);
3730
- const monitorIndex = normalizeMonitorIndex(options.monitorIndex ?? options.screenIndex ?? options.displayIndex);
3780
+ const streamId = safeString(options.streamId, 128) || makeStableLiveStreamId(deviceId);
3781
+ const normalized = normalizeLiveStreamStartOptions(options);
3782
+ const { fps, maxWidth, maxHeight, quality, monitorIndex, transfer } = normalized;
3731
3783
  const commandId = safeString(options.commandId, 128) || crypto.randomUUID();
3732
- const transfer = buildRemoteFrameTransferDescriptor(options.mode || DEFAULT_REMOTE_FRAME_MODE);
3784
+ if (device.activeLiveStream?.streamId === streamId
3785
+ && liveStreamMatchesOptions(device.activeLiveStream, normalized)
3786
+ && (device.activeLiveStream.open === true || liveStreamStartStillPending(device.activeLiveStream))) {
3787
+ emitRemoteEvent('RemoteLiveStreamStartReused', device, {
3788
+ streamId,
3789
+ fps,
3790
+ mode: transfer.mode,
3791
+ frameMode: transfer.frameMode,
3792
+ monitorIndex
3793
+ });
3794
+ return {
3795
+ ok: true,
3796
+ commandId: device.activeLiveStream.commandId,
3797
+ streamId,
3798
+ fps,
3799
+ mode: transfer.mode,
3800
+ frameMode: transfer.frameMode,
3801
+ monitorIndex,
3802
+ reused: true
3803
+ };
3804
+ }
3805
+ if (device.activeLiveStream?.active && device.activeLiveStream.streamId && device.activeLiveStream.streamId !== streamId) {
3806
+ sendCommand(deviceId, {
3807
+ command: 'stream.stop',
3808
+ commandId: crypto.randomUUID(),
3809
+ payload: {
3810
+ streamId: device.activeLiveStream.streamId,
3811
+ reason: 'live-stream-replaced',
3812
+ requestedAt: now
3813
+ }
3814
+ });
3815
+ }
3733
3816
  const result = sendCommand(deviceId, {
3734
3817
  command: 'stream.start',
3735
3818
  commandId,
@@ -3745,9 +3828,9 @@ export function createRemoteHub(options = {}) {
3745
3828
  transferProtocolVersion: transfer.transferProtocolVersion,
3746
3829
  handshake: transfer.handshake,
3747
3830
  fps,
3748
- maxWidth: clampNumber(options.maxWidth, 320, 2560, 640),
3749
- maxHeight: clampNumber(options.maxHeight, 180, 1440, 360),
3750
- quality: clampNumber(options.quality, 20, 95, 45),
3831
+ maxWidth,
3832
+ maxHeight,
3833
+ quality,
3751
3834
  monitorIndex,
3752
3835
  requestedAt: now
3753
3836
  }
@@ -3774,6 +3857,9 @@ export function createRemoteHub(options = {}) {
3774
3857
  transferProtocolVersion: transfer.transferProtocolVersion,
3775
3858
  handshake: transfer.handshake,
3776
3859
  fps,
3860
+ maxWidth,
3861
+ maxHeight,
3862
+ quality,
3777
3863
  monitorIndex,
3778
3864
  monitorCount: 1,
3779
3865
  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 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livedesk",
3
- "version": "0.1.37",
3
+ "version": "0.1.38",
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.35",
33
+ "@livedesk/client": "0.1.36",
34
34
  "cors": "^2.8.5",
35
35
  "express": "^4.21.2",
36
36
  "ws": "^8.18.3"