@midscene/android-playground 1.10.4 → 1.10.5-beta-20260714101501.0

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/lib/bin.js CHANGED
@@ -172,6 +172,14 @@ var external_cors_default = /*#__PURE__*/ __webpack_require__.n(external_cors_na
172
172
  const external_express_namespaceObject = require("express");
173
173
  var external_express_default = /*#__PURE__*/ __webpack_require__.n(external_express_namespaceObject);
174
174
  const external_socket_io_namespaceObject = require("socket.io");
175
+ function buildScrcpyPreviewErrorEvent(reason, sessionId, message) {
176
+ return {
177
+ reason,
178
+ sessionId,
179
+ message,
180
+ recoverable: 'process-exited' === reason || 'startup-timeout' === reason || 'stream-ended' === reason || 'stream-read-failed' === reason
181
+ };
182
+ }
175
183
  function getScrcpyPreviewStatusMessage(phase) {
176
184
  switch(phase){
177
185
  case 'connecting-device':
@@ -250,6 +258,11 @@ const LOOPBACK_HOSTS = new Set([
250
258
  '::1',
251
259
  '[::1]'
252
260
  ]);
261
+ const MAX_SCRCPY_OUTPUT_LINES = 100;
262
+ function appendBoundedScrcpyOutput(outputLines, line, maxLines = MAX_SCRCPY_OUTPUT_LINES) {
263
+ outputLines.push(line);
264
+ if (outputLines.length > maxLines) outputLines.splice(0, outputLines.length - maxLines);
265
+ }
253
266
  function isPrivateIP(hostname) {
254
267
  return LOOPBACK_HOSTS.has(hostname) || /^10\./.test(hostname) || /^172\.(1[6-9]|2\d|3[01])\./.test(hostname) || /^192\.168\./.test(hostname) || /^100\.(6[4-9]|[7-9]\d|1[0-2]\d)\./.test(hostname);
255
268
  }
@@ -411,8 +424,48 @@ class ScrcpyServer {
411
424
  setupSocketHandlers() {
412
425
  this.io.on('connection', async (socket)=>{
413
426
  debugPage('client connected, id: %s, client address: %s', socket.id, socket.handshake.address);
414
- let scrcpyClient = null;
427
+ let activeSession = null;
428
+ let sessionGeneration = 0;
415
429
  let adb = null;
430
+ const closeScrcpySession = async (reason, sessionToClose = activeSession)=>{
431
+ if (!sessionToClose) return;
432
+ sessionToClose.closeReason ??= reason;
433
+ if (activeSession === sessionToClose) activeSession = null;
434
+ try {
435
+ debugPage('closing scrcpy session %s, reason: %s', sessionToClose.id, reason);
436
+ await sessionToClose.client.close();
437
+ } catch (error) {
438
+ console.error(`failed to close scrcpy client (${reason}):`, error);
439
+ }
440
+ };
441
+ const emitPreviewError = (session, reason, message)=>{
442
+ if (session.failureReported || session.closeReason) return;
443
+ session.failureReported = true;
444
+ debugPage('scrcpy session %s failed (%s), recent output:\n%s', session.id, reason, session.outputLines.join('\n'));
445
+ if (socket.connected) socket.emit('preview-error', buildScrcpyPreviewErrorEvent(reason, session.id, message));
446
+ };
447
+ const monitorScrcpySession = (session)=>{
448
+ (async ()=>{
449
+ try {
450
+ const reader = session.client.output.getReader();
451
+ while(true){
452
+ const { done, value } = await reader.read();
453
+ if (done) break;
454
+ for (const line of String(value).split(/\r?\n/))if (line) {
455
+ appendBoundedScrcpyOutput(session.outputLines, line);
456
+ debugPage('scrcpy[%s]: %s', session.id, line);
457
+ }
458
+ }
459
+ } catch (error) {
460
+ if (!session.closeReason) debugPage('failed reading scrcpy output for %s: %s', session.id, error);
461
+ }
462
+ })();
463
+ Promise.resolve(session.client.exited).then(()=>{
464
+ emitPreviewError(session, 'process-exited', 'Scrcpy service exited. Recovering preview.');
465
+ }).catch((error)=>{
466
+ emitPreviewError(session, 'process-exited', `Scrcpy service exited unexpectedly: ${error instanceof Error ? error.message : String(error)}`);
467
+ });
468
+ };
416
469
  const emitPreviewStatus = (phase)=>{
417
470
  socket.emit('preview-status', buildScrcpyPreviewStatusEvent(phase));
418
471
  };
@@ -439,10 +492,8 @@ class ScrcpyServer {
439
492
  socket.on('switch-device', async (deviceId)=>{
440
493
  debugPage('received client request to switch device:', deviceId);
441
494
  try {
442
- if (scrcpyClient) {
443
- await scrcpyClient.close();
444
- scrcpyClient = null;
445
- }
495
+ sessionGeneration += 1;
496
+ await closeScrcpySession('switch device');
446
497
  this.currentDeviceId = deviceId;
447
498
  debugPage('device switched to:', deviceId);
448
499
  socket.emit('device-switched', {
@@ -461,21 +512,34 @@ class ScrcpyServer {
461
512
  });
462
513
  socket.on('connect-device', async (options = {})=>{
463
514
  const { ScrcpyVideoCodecId } = await import("@yume-chan/scrcpy");
515
+ const generation = sessionGeneration + 1;
516
+ sessionGeneration = generation;
517
+ const sessionId = `${socket.id}:${generation}`;
518
+ const isCurrentGeneration = ()=>socket.connected && sessionGeneration === generation;
464
519
  try {
465
520
  debugPage('received device connection request, options: %s, client id: %s', options, socket.id);
521
+ await closeScrcpySession('new connect-device request');
466
522
  emitPreviewStatus('connecting-device');
467
523
  const requestedDeviceId = resolveRequestedDeviceId(options, this.currentDeviceId);
468
524
  if (requestedDeviceId) this.currentDeviceId = requestedDeviceId;
469
525
  adb = await this.getAdb(requestedDeviceId);
470
526
  if (!adb) {
471
527
  console.error('no available device found');
472
- socket.emit('error', {
473
- message: 'No device found'
474
- });
528
+ socket.emit('preview-error', buildScrcpyPreviewErrorEvent('adb-unavailable', sessionId, 'No Android device is available.'));
475
529
  return;
476
530
  }
531
+ if (!isCurrentGeneration()) return;
477
532
  debugPage('starting scrcpy service, device id: %s', this.currentDeviceId);
478
- scrcpyClient = await this.startScrcpy(adb, options, emitPreviewStatus);
533
+ const scrcpyClient = await this.startScrcpy(adb, options, emitPreviewStatus);
534
+ if (!isCurrentGeneration()) return void await scrcpyClient.close();
535
+ const session = {
536
+ client: scrcpyClient,
537
+ failureReported: false,
538
+ id: sessionId,
539
+ outputLines: []
540
+ };
541
+ activeSession = session;
542
+ monitorScrcpySession(session);
479
543
  debugPage('scrcpy service started successfully');
480
544
  debugPage('check scrcpyClient object structure: %s', Object.getOwnPropertyNames(scrcpyClient).map((name)=>{
481
545
  const type = typeof scrcpyClient[name];
@@ -495,6 +559,7 @@ class ScrcpyServer {
495
559
  emitPreviewStatus('waiting-for-video');
496
560
  videoStream = scrcpyClient.videoStream;
497
561
  }
562
+ if (!isCurrentGeneration() || activeSession !== session) return void await closeScrcpySession('stale video stream', session);
498
563
  debugPage('video stream fetched successfully, metadata: %s', videoStream.metadata);
499
564
  const metadata = videoStream.metadata || {};
500
565
  debugPage('original metadata: %s', metadata);
@@ -511,14 +576,16 @@ class ScrcpyServer {
511
576
  socket.emit('video-metadata', metadata);
512
577
  debugPage('video-metadata event sent to client, id: %s, timeout budget: %ss', socket.id, Math.round(constants_namespaceObject.SCRCPY_PREVIEW_METADATA_TIMEOUT_MS / 1000));
513
578
  const { stream } = videoStream;
579
+ const streamSession = session;
514
580
  const reader = stream.getReader();
515
581
  const processStream = async ()=>{
516
582
  try {
517
583
  while(true){
518
584
  const { done, value } = await reader.read();
519
585
  if (done) break;
586
+ if (!isCurrentGeneration() || activeSession !== streamSession) return;
520
587
  const frameType = value.type || 'data';
521
- socket.emit('video-data', {
588
+ socket.volatile.emit('video-data', {
522
589
  data: value.data,
523
590
  type: frameType,
524
591
  timestamp: Date.now(),
@@ -527,63 +594,39 @@ class ScrcpyServer {
527
594
  }
528
595
  } catch (error) {
529
596
  console.error('error processing video stream:', error);
530
- if (socket.connected) socket.emit('error', {
531
- message: 'video stream processing error'
532
- });
597
+ emitPreviewError(streamSession, 'stream-read-failed', `Scrcpy video stream failed: ${error instanceof Error ? error.message : String(error)}`);
598
+ closeScrcpySession('video stream error', streamSession);
533
599
  return;
534
600
  }
535
- if (socket.connected) socket.emit('error', {
536
- message: 'video stream ended'
537
- });
538
- if (scrcpyClient) {
539
- try {
540
- await scrcpyClient.close();
541
- } catch (closeError) {
542
- console.error('failed to close scrcpy client after stream ended:', closeError);
543
- }
544
- scrcpyClient = null;
545
- }
601
+ emitPreviewError(streamSession, 'stream-ended', 'Scrcpy video stream ended. Recovering preview.');
602
+ closeScrcpySession('video stream ended', streamSession);
546
603
  };
547
604
  processStream();
548
605
  } else {
549
606
  console.error('scrcpyClient object does not have videoStream property');
550
- socket.emit('error', {
551
- message: 'Video stream not available in scrcpy client'
552
- });
607
+ emitPreviewError(session, 'unknown', 'Video stream is not available in the scrcpy client.');
608
+ await closeScrcpySession('video stream unavailable', session);
609
+ return;
553
610
  }
554
611
  } catch (error) {
555
612
  console.error('error processing video stream:', error);
556
- socket.emit('error', {
557
- message: `Video stream processing error: ${error.message}`
558
- });
613
+ emitPreviewError(session, error?.message?.includes('Timed out') ? 'startup-timeout' : 'unknown', `Video stream processing error: ${error.message}`);
614
+ await closeScrcpySession('video stream setup error', session);
559
615
  }
560
616
  if (scrcpyClient?.controller) socket.emit('control-ready');
561
617
  } catch (error) {
562
618
  console.error('failed to connect device:', error);
563
- if (scrcpyClient) {
564
- try {
565
- await scrcpyClient.close();
566
- } catch (closeError) {
567
- console.error('failed to close scrcpy client after error:', closeError);
568
- }
569
- scrcpyClient = null;
570
- }
571
- socket.emit('error', {
572
- message: `Failed to connect device: ${error?.message || 'Unknown error'}`
573
- });
619
+ const session = activeSession;
620
+ if (session?.id === sessionId) {
621
+ emitPreviewError(session, error?.message?.includes('Timed out') ? 'startup-timeout' : 'unknown', `Failed to connect device: ${error?.message || 'Unknown error'}`);
622
+ await closeScrcpySession('connect-device error', session);
623
+ } else if (isCurrentGeneration()) socket.emit('preview-error', buildScrcpyPreviewErrorEvent(error?.message?.includes('Timed out') ? 'startup-timeout' : 'unknown', sessionId, `Failed to connect device: ${error?.message || 'Unknown error'}`));
574
624
  }
575
625
  });
576
626
  socket.on('disconnect', async (reason)=>{
577
627
  debugPage('client disconnected, id: %s, reason: %s', socket.id, reason);
578
- if (scrcpyClient) {
579
- try {
580
- debugPage('closing scrcpy client');
581
- await scrcpyClient.close();
582
- } catch (error) {
583
- console.error('failed to close scrcpy client:', error);
584
- }
585
- scrcpyClient = null;
586
- }
628
+ sessionGeneration += 1;
629
+ await closeScrcpySession(`socket disconnect: ${reason}`);
587
630
  });
588
631
  sendDevicesList();
589
632
  });
package/dist/lib/index.js CHANGED
@@ -187,6 +187,14 @@ var external_cors_default = /*#__PURE__*/ __webpack_require__.n(external_cors_na
187
187
  const external_express_namespaceObject = require("express");
188
188
  var external_express_default = /*#__PURE__*/ __webpack_require__.n(external_express_namespaceObject);
189
189
  const external_socket_io_namespaceObject = require("socket.io");
190
+ function buildScrcpyPreviewErrorEvent(reason, sessionId, message) {
191
+ return {
192
+ reason,
193
+ sessionId,
194
+ message,
195
+ recoverable: 'process-exited' === reason || 'startup-timeout' === reason || 'stream-ended' === reason || 'stream-read-failed' === reason
196
+ };
197
+ }
190
198
  function getScrcpyPreviewStatusMessage(phase) {
191
199
  switch(phase){
192
200
  case 'connecting-device':
@@ -265,6 +273,11 @@ const LOOPBACK_HOSTS = new Set([
265
273
  '::1',
266
274
  '[::1]'
267
275
  ]);
276
+ const MAX_SCRCPY_OUTPUT_LINES = 100;
277
+ function appendBoundedScrcpyOutput(outputLines, line, maxLines = MAX_SCRCPY_OUTPUT_LINES) {
278
+ outputLines.push(line);
279
+ if (outputLines.length > maxLines) outputLines.splice(0, outputLines.length - maxLines);
280
+ }
268
281
  function isPrivateIP(hostname) {
269
282
  return LOOPBACK_HOSTS.has(hostname) || /^10\./.test(hostname) || /^172\.(1[6-9]|2\d|3[01])\./.test(hostname) || /^192\.168\./.test(hostname) || /^100\.(6[4-9]|[7-9]\d|1[0-2]\d)\./.test(hostname);
270
283
  }
@@ -426,8 +439,48 @@ class ScrcpyServer {
426
439
  setupSocketHandlers() {
427
440
  this.io.on('connection', async (socket)=>{
428
441
  debugPage('client connected, id: %s, client address: %s', socket.id, socket.handshake.address);
429
- let scrcpyClient = null;
442
+ let activeSession = null;
443
+ let sessionGeneration = 0;
430
444
  let adb = null;
445
+ const closeScrcpySession = async (reason, sessionToClose = activeSession)=>{
446
+ if (!sessionToClose) return;
447
+ sessionToClose.closeReason ??= reason;
448
+ if (activeSession === sessionToClose) activeSession = null;
449
+ try {
450
+ debugPage('closing scrcpy session %s, reason: %s', sessionToClose.id, reason);
451
+ await sessionToClose.client.close();
452
+ } catch (error) {
453
+ console.error(`failed to close scrcpy client (${reason}):`, error);
454
+ }
455
+ };
456
+ const emitPreviewError = (session, reason, message)=>{
457
+ if (session.failureReported || session.closeReason) return;
458
+ session.failureReported = true;
459
+ debugPage('scrcpy session %s failed (%s), recent output:\n%s', session.id, reason, session.outputLines.join('\n'));
460
+ if (socket.connected) socket.emit('preview-error', buildScrcpyPreviewErrorEvent(reason, session.id, message));
461
+ };
462
+ const monitorScrcpySession = (session)=>{
463
+ (async ()=>{
464
+ try {
465
+ const reader = session.client.output.getReader();
466
+ while(true){
467
+ const { done, value } = await reader.read();
468
+ if (done) break;
469
+ for (const line of String(value).split(/\r?\n/))if (line) {
470
+ appendBoundedScrcpyOutput(session.outputLines, line);
471
+ debugPage('scrcpy[%s]: %s', session.id, line);
472
+ }
473
+ }
474
+ } catch (error) {
475
+ if (!session.closeReason) debugPage('failed reading scrcpy output for %s: %s', session.id, error);
476
+ }
477
+ })();
478
+ Promise.resolve(session.client.exited).then(()=>{
479
+ emitPreviewError(session, 'process-exited', 'Scrcpy service exited. Recovering preview.');
480
+ }).catch((error)=>{
481
+ emitPreviewError(session, 'process-exited', `Scrcpy service exited unexpectedly: ${error instanceof Error ? error.message : String(error)}`);
482
+ });
483
+ };
431
484
  const emitPreviewStatus = (phase)=>{
432
485
  socket.emit('preview-status', buildScrcpyPreviewStatusEvent(phase));
433
486
  };
@@ -454,10 +507,8 @@ class ScrcpyServer {
454
507
  socket.on('switch-device', async (deviceId)=>{
455
508
  debugPage('received client request to switch device:', deviceId);
456
509
  try {
457
- if (scrcpyClient) {
458
- await scrcpyClient.close();
459
- scrcpyClient = null;
460
- }
510
+ sessionGeneration += 1;
511
+ await closeScrcpySession('switch device');
461
512
  this.currentDeviceId = deviceId;
462
513
  debugPage('device switched to:', deviceId);
463
514
  socket.emit('device-switched', {
@@ -476,21 +527,34 @@ class ScrcpyServer {
476
527
  });
477
528
  socket.on('connect-device', async (options = {})=>{
478
529
  const { ScrcpyVideoCodecId } = await import("@yume-chan/scrcpy");
530
+ const generation = sessionGeneration + 1;
531
+ sessionGeneration = generation;
532
+ const sessionId = `${socket.id}:${generation}`;
533
+ const isCurrentGeneration = ()=>socket.connected && sessionGeneration === generation;
479
534
  try {
480
535
  debugPage('received device connection request, options: %s, client id: %s', options, socket.id);
536
+ await closeScrcpySession('new connect-device request');
481
537
  emitPreviewStatus('connecting-device');
482
538
  const requestedDeviceId = resolveRequestedDeviceId(options, this.currentDeviceId);
483
539
  if (requestedDeviceId) this.currentDeviceId = requestedDeviceId;
484
540
  adb = await this.getAdb(requestedDeviceId);
485
541
  if (!adb) {
486
542
  console.error('no available device found');
487
- socket.emit('error', {
488
- message: 'No device found'
489
- });
543
+ socket.emit('preview-error', buildScrcpyPreviewErrorEvent('adb-unavailable', sessionId, 'No Android device is available.'));
490
544
  return;
491
545
  }
546
+ if (!isCurrentGeneration()) return;
492
547
  debugPage('starting scrcpy service, device id: %s', this.currentDeviceId);
493
- scrcpyClient = await this.startScrcpy(adb, options, emitPreviewStatus);
548
+ const scrcpyClient = await this.startScrcpy(adb, options, emitPreviewStatus);
549
+ if (!isCurrentGeneration()) return void await scrcpyClient.close();
550
+ const session = {
551
+ client: scrcpyClient,
552
+ failureReported: false,
553
+ id: sessionId,
554
+ outputLines: []
555
+ };
556
+ activeSession = session;
557
+ monitorScrcpySession(session);
494
558
  debugPage('scrcpy service started successfully');
495
559
  debugPage('check scrcpyClient object structure: %s', Object.getOwnPropertyNames(scrcpyClient).map((name)=>{
496
560
  const type = typeof scrcpyClient[name];
@@ -510,6 +574,7 @@ class ScrcpyServer {
510
574
  emitPreviewStatus('waiting-for-video');
511
575
  videoStream = scrcpyClient.videoStream;
512
576
  }
577
+ if (!isCurrentGeneration() || activeSession !== session) return void await closeScrcpySession('stale video stream', session);
513
578
  debugPage('video stream fetched successfully, metadata: %s', videoStream.metadata);
514
579
  const metadata = videoStream.metadata || {};
515
580
  debugPage('original metadata: %s', metadata);
@@ -526,14 +591,16 @@ class ScrcpyServer {
526
591
  socket.emit('video-metadata', metadata);
527
592
  debugPage('video-metadata event sent to client, id: %s, timeout budget: %ss', socket.id, Math.round(constants_namespaceObject.SCRCPY_PREVIEW_METADATA_TIMEOUT_MS / 1000));
528
593
  const { stream } = videoStream;
594
+ const streamSession = session;
529
595
  const reader = stream.getReader();
530
596
  const processStream = async ()=>{
531
597
  try {
532
598
  while(true){
533
599
  const { done, value } = await reader.read();
534
600
  if (done) break;
601
+ if (!isCurrentGeneration() || activeSession !== streamSession) return;
535
602
  const frameType = value.type || 'data';
536
- socket.emit('video-data', {
603
+ socket.volatile.emit('video-data', {
537
604
  data: value.data,
538
605
  type: frameType,
539
606
  timestamp: Date.now(),
@@ -542,63 +609,39 @@ class ScrcpyServer {
542
609
  }
543
610
  } catch (error) {
544
611
  console.error('error processing video stream:', error);
545
- if (socket.connected) socket.emit('error', {
546
- message: 'video stream processing error'
547
- });
612
+ emitPreviewError(streamSession, 'stream-read-failed', `Scrcpy video stream failed: ${error instanceof Error ? error.message : String(error)}`);
613
+ closeScrcpySession('video stream error', streamSession);
548
614
  return;
549
615
  }
550
- if (socket.connected) socket.emit('error', {
551
- message: 'video stream ended'
552
- });
553
- if (scrcpyClient) {
554
- try {
555
- await scrcpyClient.close();
556
- } catch (closeError) {
557
- console.error('failed to close scrcpy client after stream ended:', closeError);
558
- }
559
- scrcpyClient = null;
560
- }
616
+ emitPreviewError(streamSession, 'stream-ended', 'Scrcpy video stream ended. Recovering preview.');
617
+ closeScrcpySession('video stream ended', streamSession);
561
618
  };
562
619
  processStream();
563
620
  } else {
564
621
  console.error('scrcpyClient object does not have videoStream property');
565
- socket.emit('error', {
566
- message: 'Video stream not available in scrcpy client'
567
- });
622
+ emitPreviewError(session, 'unknown', 'Video stream is not available in the scrcpy client.');
623
+ await closeScrcpySession('video stream unavailable', session);
624
+ return;
568
625
  }
569
626
  } catch (error) {
570
627
  console.error('error processing video stream:', error);
571
- socket.emit('error', {
572
- message: `Video stream processing error: ${error.message}`
573
- });
628
+ emitPreviewError(session, error?.message?.includes('Timed out') ? 'startup-timeout' : 'unknown', `Video stream processing error: ${error.message}`);
629
+ await closeScrcpySession('video stream setup error', session);
574
630
  }
575
631
  if (scrcpyClient?.controller) socket.emit('control-ready');
576
632
  } catch (error) {
577
633
  console.error('failed to connect device:', error);
578
- if (scrcpyClient) {
579
- try {
580
- await scrcpyClient.close();
581
- } catch (closeError) {
582
- console.error('failed to close scrcpy client after error:', closeError);
583
- }
584
- scrcpyClient = null;
585
- }
586
- socket.emit('error', {
587
- message: `Failed to connect device: ${error?.message || 'Unknown error'}`
588
- });
634
+ const session = activeSession;
635
+ if (session?.id === sessionId) {
636
+ emitPreviewError(session, error?.message?.includes('Timed out') ? 'startup-timeout' : 'unknown', `Failed to connect device: ${error?.message || 'Unknown error'}`);
637
+ await closeScrcpySession('connect-device error', session);
638
+ } else if (isCurrentGeneration()) socket.emit('preview-error', buildScrcpyPreviewErrorEvent(error?.message?.includes('Timed out') ? 'startup-timeout' : 'unknown', sessionId, `Failed to connect device: ${error?.message || 'Unknown error'}`));
589
639
  }
590
640
  });
591
641
  socket.on('disconnect', async (reason)=>{
592
642
  debugPage('client disconnected, id: %s, reason: %s', socket.id, reason);
593
- if (scrcpyClient) {
594
- try {
595
- debugPage('closing scrcpy client');
596
- await scrcpyClient.close();
597
- } catch (error) {
598
- console.error('failed to close scrcpy client:', error);
599
- }
600
- scrcpyClient = null;
601
- }
643
+ sessionGeneration += 1;
644
+ await closeScrcpySession(`socket disconnect: ${reason}`);
602
645
  });
603
646
  sendDevicesList();
604
647
  });
@@ -3,5 +3,13 @@ export interface ScrcpyPreviewStatusEvent {
3
3
  phase: ScrcpyPreviewPhase;
4
4
  message: string;
5
5
  }
6
+ export type ScrcpyPreviewErrorReason = 'adb-unavailable' | 'process-exited' | 'startup-timeout' | 'stream-ended' | 'stream-read-failed' | 'unknown';
7
+ export interface ScrcpyPreviewErrorEvent {
8
+ message: string;
9
+ reason: ScrcpyPreviewErrorReason;
10
+ recoverable: boolean;
11
+ sessionId: string;
12
+ }
13
+ export declare function buildScrcpyPreviewErrorEvent(reason: ScrcpyPreviewErrorReason, sessionId: string, message: string): ScrcpyPreviewErrorEvent;
6
14
  export declare function getScrcpyPreviewStatusMessage(phase: ScrcpyPreviewPhase): string;
7
15
  export declare function buildScrcpyPreviewStatusEvent(phase: ScrcpyPreviewPhase): ScrcpyPreviewStatusEvent;
@@ -3,9 +3,11 @@ import type { AdbServerClient } from '@yume-chan/adb';
3
3
  import express from 'express';
4
4
  import { Server } from 'socket.io';
5
5
  export declare const debugPage: import("@midscene/shared/logger").DebugFunction;
6
+ export declare function appendBoundedScrcpyOutput(outputLines: string[], line: string, maxLines?: number): void;
6
7
  export interface ScrcpyConnectDeviceRequest {
7
8
  deviceId?: string;
8
9
  maxSize?: number;
10
+ videoBitRate?: number;
9
11
  }
10
12
  export interface ScrcpyListedDevice {
11
13
  id: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@midscene/android-playground",
3
- "version": "1.10.4",
3
+ "version": "1.10.5-beta-20260714101501.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/web-infra-dev/midscene.git",
@@ -39,10 +39,10 @@
39
39
  "express": "^4.21.2",
40
40
  "open": "10.1.0",
41
41
  "socket.io": "^4.8.1",
42
- "@midscene/android": "1.10.4",
43
- "@midscene/shared": "1.10.4",
44
- "@midscene/playground": "1.10.4",
45
- "@midscene/core": "1.10.4"
42
+ "@midscene/android": "1.10.5-beta-20260714101501.0",
43
+ "@midscene/core": "1.10.5-beta-20260714101501.0",
44
+ "@midscene/shared": "1.10.5-beta-20260714101501.0",
45
+ "@midscene/playground": "1.10.5-beta-20260714101501.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@rslib/core": "^0.18.3",
package/static/index.html CHANGED
@@ -1 +1 @@
1
- <!doctype html><html><head><title>Midscene Android Playground</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script defer src="/static/js/lib-react.ed140d90.js"></script><script defer src="/static/js/445.b67c87ae.js"></script><script defer src="/static/js/index.4506e773.js"></script><link href="/static/css/index.1293237f.css" rel="stylesheet"></head><body><div id="root"></div></body></html>
1
+ <!doctype html><html><head><title>Midscene Android Playground</title><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script defer src="/static/js/lib-react.ed140d90.js"></script><script defer src="/static/js/890.0322764c.js"></script><script defer src="/static/js/index.e8830865.js"></script><link href="/static/css/index.2b6b43ad.css" rel="stylesheet"></head><body><div id="root"></div></body></html>