@midscene/android-playground 1.10.5-beta-20260716114658.0 → 1.10.5
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/es/bin.mjs +51 -96
- package/dist/es/index.mjs +51 -96
- package/dist/lib/bin.js +51 -96
- package/dist/lib/index.js +51 -96
- package/dist/types/platform.d.ts +2 -6
- package/dist/types/scrcpy-preview-status.d.ts +0 -8
- package/dist/types/scrcpy-server.d.ts +0 -2
- package/package.json +5 -5
- package/static/index.html +1 -1
- package/static/static/css/index.1293237f.css +2 -0
- package/static/static/css/index.1293237f.css.map +1 -0
- package/static/static/js/{890.0322764c.js → 445.b67c87ae.js} +3 -3
- package/static/static/js/{890.0322764c.js.LICENSE.txt → 445.b67c87ae.js.LICENSE.txt} +2 -0
- package/static/static/js/445.b67c87ae.js.map +1 -0
- package/static/static/js/index.7b82808d.js +960 -0
- package/static/static/js/index.7b82808d.js.map +1 -0
- package/static/static/css/index.2b6b43ad.css +0 -2
- package/static/static/css/index.2b6b43ad.css.map +0 -1
- package/static/static/js/890.0322764c.js.map +0 -1
- package/static/static/js/index.544515fb.js +0 -960
- package/static/static/js/index.544515fb.js.map +0 -1
- /package/static/static/js/{index.544515fb.js.LICENSE.txt → index.7b82808d.js.LICENSE.txt} +0 -0
package/dist/es/bin.mjs
CHANGED
|
@@ -120,7 +120,7 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
120
120
|
await options.scrcpyServer?.launch(scrcpyPort);
|
|
121
121
|
},
|
|
122
122
|
stop: async ()=>{
|
|
123
|
-
|
|
123
|
+
options.scrcpyServer?.close();
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
] : void 0,
|
|
@@ -146,14 +146,6 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
148
|
});
|
|
149
|
-
function buildScrcpyPreviewErrorEvent(reason, sessionId, message) {
|
|
150
|
-
return {
|
|
151
|
-
reason,
|
|
152
|
-
sessionId,
|
|
153
|
-
message,
|
|
154
|
-
recoverable: 'process-exited' === reason || 'startup-timeout' === reason || 'stream-ended' === reason || 'stream-read-failed' === reason
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
149
|
function getScrcpyPreviewStatusMessage(phase) {
|
|
158
150
|
switch(phase){
|
|
159
151
|
case 'connecting-device':
|
|
@@ -232,11 +224,6 @@ const LOOPBACK_HOSTS = new Set([
|
|
|
232
224
|
'::1',
|
|
233
225
|
'[::1]'
|
|
234
226
|
]);
|
|
235
|
-
const MAX_SCRCPY_OUTPUT_LINES = 100;
|
|
236
|
-
function appendBoundedScrcpyOutput(outputLines, line, maxLines = MAX_SCRCPY_OUTPUT_LINES) {
|
|
237
|
-
outputLines.push(line);
|
|
238
|
-
if (outputLines.length > maxLines) outputLines.splice(0, outputLines.length - maxLines);
|
|
239
|
-
}
|
|
240
227
|
function isPrivateIP(hostname) {
|
|
241
228
|
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);
|
|
242
229
|
}
|
|
@@ -398,48 +385,8 @@ class ScrcpyServer {
|
|
|
398
385
|
setupSocketHandlers() {
|
|
399
386
|
this.io.on('connection', async (socket)=>{
|
|
400
387
|
debugPage('client connected, id: %s, client address: %s', socket.id, socket.handshake.address);
|
|
401
|
-
let
|
|
402
|
-
let sessionGeneration = 0;
|
|
388
|
+
let scrcpyClient = null;
|
|
403
389
|
let adb = null;
|
|
404
|
-
const closeScrcpySession = async (reason, sessionToClose = activeSession)=>{
|
|
405
|
-
if (!sessionToClose) return;
|
|
406
|
-
sessionToClose.closeReason ??= reason;
|
|
407
|
-
if (activeSession === sessionToClose) activeSession = null;
|
|
408
|
-
try {
|
|
409
|
-
debugPage('closing scrcpy session %s, reason: %s', sessionToClose.id, reason);
|
|
410
|
-
await sessionToClose.client.close();
|
|
411
|
-
} catch (error) {
|
|
412
|
-
console.error(`failed to close scrcpy client (${reason}):`, error);
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
const emitPreviewError = (session, reason, message)=>{
|
|
416
|
-
if (session.failureReported || session.closeReason) return;
|
|
417
|
-
session.failureReported = true;
|
|
418
|
-
debugPage('scrcpy session %s failed (%s), recent output:\n%s', session.id, reason, session.outputLines.join('\n'));
|
|
419
|
-
if (socket.connected) socket.emit('preview-error', buildScrcpyPreviewErrorEvent(reason, session.id, message));
|
|
420
|
-
};
|
|
421
|
-
const monitorScrcpySession = (session)=>{
|
|
422
|
-
(async ()=>{
|
|
423
|
-
try {
|
|
424
|
-
const reader = session.client.output.getReader();
|
|
425
|
-
while(true){
|
|
426
|
-
const { done, value } = await reader.read();
|
|
427
|
-
if (done) break;
|
|
428
|
-
for (const line of String(value).split(/\r?\n/))if (line) {
|
|
429
|
-
appendBoundedScrcpyOutput(session.outputLines, line);
|
|
430
|
-
debugPage('scrcpy[%s]: %s', session.id, line);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
} catch (error) {
|
|
434
|
-
if (!session.closeReason) debugPage('failed reading scrcpy output for %s: %s', session.id, error);
|
|
435
|
-
}
|
|
436
|
-
})();
|
|
437
|
-
Promise.resolve(session.client.exited).then(()=>{
|
|
438
|
-
emitPreviewError(session, 'process-exited', 'Scrcpy service exited. Recovering preview.');
|
|
439
|
-
}).catch((error)=>{
|
|
440
|
-
emitPreviewError(session, 'process-exited', `Scrcpy service exited unexpectedly: ${error instanceof Error ? error.message : String(error)}`);
|
|
441
|
-
});
|
|
442
|
-
};
|
|
443
390
|
const emitPreviewStatus = (phase)=>{
|
|
444
391
|
socket.emit('preview-status', buildScrcpyPreviewStatusEvent(phase));
|
|
445
392
|
};
|
|
@@ -466,8 +413,10 @@ class ScrcpyServer {
|
|
|
466
413
|
socket.on('switch-device', async (deviceId)=>{
|
|
467
414
|
debugPage('received client request to switch device:', deviceId);
|
|
468
415
|
try {
|
|
469
|
-
|
|
470
|
-
|
|
416
|
+
if (scrcpyClient) {
|
|
417
|
+
await scrcpyClient.close();
|
|
418
|
+
scrcpyClient = null;
|
|
419
|
+
}
|
|
471
420
|
this.currentDeviceId = deviceId;
|
|
472
421
|
debugPage('device switched to:', deviceId);
|
|
473
422
|
socket.emit('device-switched', {
|
|
@@ -486,34 +435,21 @@ class ScrcpyServer {
|
|
|
486
435
|
});
|
|
487
436
|
socket.on('connect-device', async (options = {})=>{
|
|
488
437
|
const { ScrcpyVideoCodecId } = await import("@yume-chan/scrcpy");
|
|
489
|
-
const generation = sessionGeneration + 1;
|
|
490
|
-
sessionGeneration = generation;
|
|
491
|
-
const sessionId = `${socket.id}:${generation}`;
|
|
492
|
-
const isCurrentGeneration = ()=>socket.connected && sessionGeneration === generation;
|
|
493
438
|
try {
|
|
494
439
|
debugPage('received device connection request, options: %s, client id: %s', options, socket.id);
|
|
495
|
-
await closeScrcpySession('new connect-device request');
|
|
496
440
|
emitPreviewStatus('connecting-device');
|
|
497
441
|
const requestedDeviceId = resolveRequestedDeviceId(options, this.currentDeviceId);
|
|
498
442
|
if (requestedDeviceId) this.currentDeviceId = requestedDeviceId;
|
|
499
443
|
adb = await this.getAdb(requestedDeviceId);
|
|
500
444
|
if (!adb) {
|
|
501
445
|
console.error('no available device found');
|
|
502
|
-
socket.emit('
|
|
446
|
+
socket.emit('error', {
|
|
447
|
+
message: 'No device found'
|
|
448
|
+
});
|
|
503
449
|
return;
|
|
504
450
|
}
|
|
505
|
-
if (!isCurrentGeneration()) return;
|
|
506
451
|
debugPage('starting scrcpy service, device id: %s', this.currentDeviceId);
|
|
507
|
-
|
|
508
|
-
if (!isCurrentGeneration()) return void await scrcpyClient.close();
|
|
509
|
-
const session = {
|
|
510
|
-
client: scrcpyClient,
|
|
511
|
-
failureReported: false,
|
|
512
|
-
id: sessionId,
|
|
513
|
-
outputLines: []
|
|
514
|
-
};
|
|
515
|
-
activeSession = session;
|
|
516
|
-
monitorScrcpySession(session);
|
|
452
|
+
scrcpyClient = await this.startScrcpy(adb, options, emitPreviewStatus);
|
|
517
453
|
debugPage('scrcpy service started successfully');
|
|
518
454
|
debugPage('check scrcpyClient object structure: %s', Object.getOwnPropertyNames(scrcpyClient).map((name)=>{
|
|
519
455
|
const type = typeof scrcpyClient[name];
|
|
@@ -533,7 +469,6 @@ class ScrcpyServer {
|
|
|
533
469
|
emitPreviewStatus('waiting-for-video');
|
|
534
470
|
videoStream = scrcpyClient.videoStream;
|
|
535
471
|
}
|
|
536
|
-
if (!isCurrentGeneration() || activeSession !== session) return void await closeScrcpySession('stale video stream', session);
|
|
537
472
|
debugPage('video stream fetched successfully, metadata: %s', videoStream.metadata);
|
|
538
473
|
const metadata = videoStream.metadata || {};
|
|
539
474
|
debugPage('original metadata: %s', metadata);
|
|
@@ -550,16 +485,14 @@ class ScrcpyServer {
|
|
|
550
485
|
socket.emit('video-metadata', metadata);
|
|
551
486
|
debugPage('video-metadata event sent to client, id: %s, timeout budget: %ss', socket.id, Math.round(SCRCPY_PREVIEW_METADATA_TIMEOUT_MS / 1000));
|
|
552
487
|
const { stream } = videoStream;
|
|
553
|
-
const streamSession = session;
|
|
554
488
|
const reader = stream.getReader();
|
|
555
489
|
const processStream = async ()=>{
|
|
556
490
|
try {
|
|
557
491
|
while(true){
|
|
558
492
|
const { done, value } = await reader.read();
|
|
559
493
|
if (done) break;
|
|
560
|
-
if (!isCurrentGeneration() || activeSession !== streamSession) return;
|
|
561
494
|
const frameType = value.type || 'data';
|
|
562
|
-
socket.
|
|
495
|
+
socket.emit('video-data', {
|
|
563
496
|
data: value.data,
|
|
564
497
|
type: frameType,
|
|
565
498
|
timestamp: Date.now(),
|
|
@@ -568,39 +501,63 @@ class ScrcpyServer {
|
|
|
568
501
|
}
|
|
569
502
|
} catch (error) {
|
|
570
503
|
console.error('error processing video stream:', error);
|
|
571
|
-
|
|
572
|
-
|
|
504
|
+
if (socket.connected) socket.emit('error', {
|
|
505
|
+
message: 'video stream processing error'
|
|
506
|
+
});
|
|
573
507
|
return;
|
|
574
508
|
}
|
|
575
|
-
|
|
576
|
-
|
|
509
|
+
if (socket.connected) socket.emit('error', {
|
|
510
|
+
message: 'video stream ended'
|
|
511
|
+
});
|
|
512
|
+
if (scrcpyClient) {
|
|
513
|
+
try {
|
|
514
|
+
await scrcpyClient.close();
|
|
515
|
+
} catch (closeError) {
|
|
516
|
+
console.error('failed to close scrcpy client after stream ended:', closeError);
|
|
517
|
+
}
|
|
518
|
+
scrcpyClient = null;
|
|
519
|
+
}
|
|
577
520
|
};
|
|
578
521
|
processStream();
|
|
579
522
|
} else {
|
|
580
523
|
console.error('scrcpyClient object does not have videoStream property');
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
524
|
+
socket.emit('error', {
|
|
525
|
+
message: 'Video stream not available in scrcpy client'
|
|
526
|
+
});
|
|
584
527
|
}
|
|
585
528
|
} catch (error) {
|
|
586
529
|
console.error('error processing video stream:', error);
|
|
587
|
-
|
|
588
|
-
|
|
530
|
+
socket.emit('error', {
|
|
531
|
+
message: `Video stream processing error: ${error.message}`
|
|
532
|
+
});
|
|
589
533
|
}
|
|
590
534
|
if (scrcpyClient?.controller) socket.emit('control-ready');
|
|
591
535
|
} catch (error) {
|
|
592
536
|
console.error('failed to connect device:', error);
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
537
|
+
if (scrcpyClient) {
|
|
538
|
+
try {
|
|
539
|
+
await scrcpyClient.close();
|
|
540
|
+
} catch (closeError) {
|
|
541
|
+
console.error('failed to close scrcpy client after error:', closeError);
|
|
542
|
+
}
|
|
543
|
+
scrcpyClient = null;
|
|
544
|
+
}
|
|
545
|
+
socket.emit('error', {
|
|
546
|
+
message: `Failed to connect device: ${error?.message || 'Unknown error'}`
|
|
547
|
+
});
|
|
598
548
|
}
|
|
599
549
|
});
|
|
600
550
|
socket.on('disconnect', async (reason)=>{
|
|
601
551
|
debugPage('client disconnected, id: %s, reason: %s', socket.id, reason);
|
|
602
|
-
|
|
603
|
-
|
|
552
|
+
if (scrcpyClient) {
|
|
553
|
+
try {
|
|
554
|
+
debugPage('closing scrcpy client');
|
|
555
|
+
await scrcpyClient.close();
|
|
556
|
+
} catch (error) {
|
|
557
|
+
console.error('failed to close scrcpy client:', error);
|
|
558
|
+
}
|
|
559
|
+
scrcpyClient = null;
|
|
560
|
+
}
|
|
604
561
|
});
|
|
605
562
|
sendDevicesList();
|
|
606
563
|
});
|
|
@@ -664,8 +621,6 @@ class ScrcpyServer {
|
|
|
664
621
|
this.app = express();
|
|
665
622
|
this.httpServer = createServer(this.app);
|
|
666
623
|
this.io = new Server(this.httpServer, {
|
|
667
|
-
pingInterval: 25000,
|
|
668
|
-
pingTimeout: 60000,
|
|
669
624
|
cors: {
|
|
670
625
|
origin (origin, callback) {
|
|
671
626
|
callback(null, isAllowedOrigin(origin));
|
package/dist/es/index.mjs
CHANGED
|
@@ -120,7 +120,7 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
120
120
|
await options.scrcpyServer?.launch(scrcpyPort);
|
|
121
121
|
},
|
|
122
122
|
stop: async ()=>{
|
|
123
|
-
|
|
123
|
+
options.scrcpyServer?.close();
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
] : void 0,
|
|
@@ -146,14 +146,6 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
146
146
|
};
|
|
147
147
|
}
|
|
148
148
|
});
|
|
149
|
-
function buildScrcpyPreviewErrorEvent(reason, sessionId, message) {
|
|
150
|
-
return {
|
|
151
|
-
reason,
|
|
152
|
-
sessionId,
|
|
153
|
-
message,
|
|
154
|
-
recoverable: 'process-exited' === reason || 'startup-timeout' === reason || 'stream-ended' === reason || 'stream-read-failed' === reason
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
149
|
function getScrcpyPreviewStatusMessage(phase) {
|
|
158
150
|
switch(phase){
|
|
159
151
|
case 'connecting-device':
|
|
@@ -232,11 +224,6 @@ const LOOPBACK_HOSTS = new Set([
|
|
|
232
224
|
'::1',
|
|
233
225
|
'[::1]'
|
|
234
226
|
]);
|
|
235
|
-
const MAX_SCRCPY_OUTPUT_LINES = 100;
|
|
236
|
-
function appendBoundedScrcpyOutput(outputLines, line, maxLines = MAX_SCRCPY_OUTPUT_LINES) {
|
|
237
|
-
outputLines.push(line);
|
|
238
|
-
if (outputLines.length > maxLines) outputLines.splice(0, outputLines.length - maxLines);
|
|
239
|
-
}
|
|
240
227
|
function isPrivateIP(hostname) {
|
|
241
228
|
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);
|
|
242
229
|
}
|
|
@@ -398,48 +385,8 @@ class ScrcpyServer {
|
|
|
398
385
|
setupSocketHandlers() {
|
|
399
386
|
this.io.on('connection', async (socket)=>{
|
|
400
387
|
debugPage('client connected, id: %s, client address: %s', socket.id, socket.handshake.address);
|
|
401
|
-
let
|
|
402
|
-
let sessionGeneration = 0;
|
|
388
|
+
let scrcpyClient = null;
|
|
403
389
|
let adb = null;
|
|
404
|
-
const closeScrcpySession = async (reason, sessionToClose = activeSession)=>{
|
|
405
|
-
if (!sessionToClose) return;
|
|
406
|
-
sessionToClose.closeReason ??= reason;
|
|
407
|
-
if (activeSession === sessionToClose) activeSession = null;
|
|
408
|
-
try {
|
|
409
|
-
debugPage('closing scrcpy session %s, reason: %s', sessionToClose.id, reason);
|
|
410
|
-
await sessionToClose.client.close();
|
|
411
|
-
} catch (error) {
|
|
412
|
-
console.error(`failed to close scrcpy client (${reason}):`, error);
|
|
413
|
-
}
|
|
414
|
-
};
|
|
415
|
-
const emitPreviewError = (session, reason, message)=>{
|
|
416
|
-
if (session.failureReported || session.closeReason) return;
|
|
417
|
-
session.failureReported = true;
|
|
418
|
-
debugPage('scrcpy session %s failed (%s), recent output:\n%s', session.id, reason, session.outputLines.join('\n'));
|
|
419
|
-
if (socket.connected) socket.emit('preview-error', buildScrcpyPreviewErrorEvent(reason, session.id, message));
|
|
420
|
-
};
|
|
421
|
-
const monitorScrcpySession = (session)=>{
|
|
422
|
-
(async ()=>{
|
|
423
|
-
try {
|
|
424
|
-
const reader = session.client.output.getReader();
|
|
425
|
-
while(true){
|
|
426
|
-
const { done, value } = await reader.read();
|
|
427
|
-
if (done) break;
|
|
428
|
-
for (const line of String(value).split(/\r?\n/))if (line) {
|
|
429
|
-
appendBoundedScrcpyOutput(session.outputLines, line);
|
|
430
|
-
debugPage('scrcpy[%s]: %s', session.id, line);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
} catch (error) {
|
|
434
|
-
if (!session.closeReason) debugPage('failed reading scrcpy output for %s: %s', session.id, error);
|
|
435
|
-
}
|
|
436
|
-
})();
|
|
437
|
-
Promise.resolve(session.client.exited).then(()=>{
|
|
438
|
-
emitPreviewError(session, 'process-exited', 'Scrcpy service exited. Recovering preview.');
|
|
439
|
-
}).catch((error)=>{
|
|
440
|
-
emitPreviewError(session, 'process-exited', `Scrcpy service exited unexpectedly: ${error instanceof Error ? error.message : String(error)}`);
|
|
441
|
-
});
|
|
442
|
-
};
|
|
443
390
|
const emitPreviewStatus = (phase)=>{
|
|
444
391
|
socket.emit('preview-status', buildScrcpyPreviewStatusEvent(phase));
|
|
445
392
|
};
|
|
@@ -466,8 +413,10 @@ class ScrcpyServer {
|
|
|
466
413
|
socket.on('switch-device', async (deviceId)=>{
|
|
467
414
|
debugPage('received client request to switch device:', deviceId);
|
|
468
415
|
try {
|
|
469
|
-
|
|
470
|
-
|
|
416
|
+
if (scrcpyClient) {
|
|
417
|
+
await scrcpyClient.close();
|
|
418
|
+
scrcpyClient = null;
|
|
419
|
+
}
|
|
471
420
|
this.currentDeviceId = deviceId;
|
|
472
421
|
debugPage('device switched to:', deviceId);
|
|
473
422
|
socket.emit('device-switched', {
|
|
@@ -486,34 +435,21 @@ class ScrcpyServer {
|
|
|
486
435
|
});
|
|
487
436
|
socket.on('connect-device', async (options = {})=>{
|
|
488
437
|
const { ScrcpyVideoCodecId } = await import("@yume-chan/scrcpy");
|
|
489
|
-
const generation = sessionGeneration + 1;
|
|
490
|
-
sessionGeneration = generation;
|
|
491
|
-
const sessionId = `${socket.id}:${generation}`;
|
|
492
|
-
const isCurrentGeneration = ()=>socket.connected && sessionGeneration === generation;
|
|
493
438
|
try {
|
|
494
439
|
debugPage('received device connection request, options: %s, client id: %s', options, socket.id);
|
|
495
|
-
await closeScrcpySession('new connect-device request');
|
|
496
440
|
emitPreviewStatus('connecting-device');
|
|
497
441
|
const requestedDeviceId = resolveRequestedDeviceId(options, this.currentDeviceId);
|
|
498
442
|
if (requestedDeviceId) this.currentDeviceId = requestedDeviceId;
|
|
499
443
|
adb = await this.getAdb(requestedDeviceId);
|
|
500
444
|
if (!adb) {
|
|
501
445
|
console.error('no available device found');
|
|
502
|
-
socket.emit('
|
|
446
|
+
socket.emit('error', {
|
|
447
|
+
message: 'No device found'
|
|
448
|
+
});
|
|
503
449
|
return;
|
|
504
450
|
}
|
|
505
|
-
if (!isCurrentGeneration()) return;
|
|
506
451
|
debugPage('starting scrcpy service, device id: %s', this.currentDeviceId);
|
|
507
|
-
|
|
508
|
-
if (!isCurrentGeneration()) return void await scrcpyClient.close();
|
|
509
|
-
const session = {
|
|
510
|
-
client: scrcpyClient,
|
|
511
|
-
failureReported: false,
|
|
512
|
-
id: sessionId,
|
|
513
|
-
outputLines: []
|
|
514
|
-
};
|
|
515
|
-
activeSession = session;
|
|
516
|
-
monitorScrcpySession(session);
|
|
452
|
+
scrcpyClient = await this.startScrcpy(adb, options, emitPreviewStatus);
|
|
517
453
|
debugPage('scrcpy service started successfully');
|
|
518
454
|
debugPage('check scrcpyClient object structure: %s', Object.getOwnPropertyNames(scrcpyClient).map((name)=>{
|
|
519
455
|
const type = typeof scrcpyClient[name];
|
|
@@ -533,7 +469,6 @@ class ScrcpyServer {
|
|
|
533
469
|
emitPreviewStatus('waiting-for-video');
|
|
534
470
|
videoStream = scrcpyClient.videoStream;
|
|
535
471
|
}
|
|
536
|
-
if (!isCurrentGeneration() || activeSession !== session) return void await closeScrcpySession('stale video stream', session);
|
|
537
472
|
debugPage('video stream fetched successfully, metadata: %s', videoStream.metadata);
|
|
538
473
|
const metadata = videoStream.metadata || {};
|
|
539
474
|
debugPage('original metadata: %s', metadata);
|
|
@@ -550,16 +485,14 @@ class ScrcpyServer {
|
|
|
550
485
|
socket.emit('video-metadata', metadata);
|
|
551
486
|
debugPage('video-metadata event sent to client, id: %s, timeout budget: %ss', socket.id, Math.round(SCRCPY_PREVIEW_METADATA_TIMEOUT_MS / 1000));
|
|
552
487
|
const { stream } = videoStream;
|
|
553
|
-
const streamSession = session;
|
|
554
488
|
const reader = stream.getReader();
|
|
555
489
|
const processStream = async ()=>{
|
|
556
490
|
try {
|
|
557
491
|
while(true){
|
|
558
492
|
const { done, value } = await reader.read();
|
|
559
493
|
if (done) break;
|
|
560
|
-
if (!isCurrentGeneration() || activeSession !== streamSession) return;
|
|
561
494
|
const frameType = value.type || 'data';
|
|
562
|
-
socket.
|
|
495
|
+
socket.emit('video-data', {
|
|
563
496
|
data: value.data,
|
|
564
497
|
type: frameType,
|
|
565
498
|
timestamp: Date.now(),
|
|
@@ -568,39 +501,63 @@ class ScrcpyServer {
|
|
|
568
501
|
}
|
|
569
502
|
} catch (error) {
|
|
570
503
|
console.error('error processing video stream:', error);
|
|
571
|
-
|
|
572
|
-
|
|
504
|
+
if (socket.connected) socket.emit('error', {
|
|
505
|
+
message: 'video stream processing error'
|
|
506
|
+
});
|
|
573
507
|
return;
|
|
574
508
|
}
|
|
575
|
-
|
|
576
|
-
|
|
509
|
+
if (socket.connected) socket.emit('error', {
|
|
510
|
+
message: 'video stream ended'
|
|
511
|
+
});
|
|
512
|
+
if (scrcpyClient) {
|
|
513
|
+
try {
|
|
514
|
+
await scrcpyClient.close();
|
|
515
|
+
} catch (closeError) {
|
|
516
|
+
console.error('failed to close scrcpy client after stream ended:', closeError);
|
|
517
|
+
}
|
|
518
|
+
scrcpyClient = null;
|
|
519
|
+
}
|
|
577
520
|
};
|
|
578
521
|
processStream();
|
|
579
522
|
} else {
|
|
580
523
|
console.error('scrcpyClient object does not have videoStream property');
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
524
|
+
socket.emit('error', {
|
|
525
|
+
message: 'Video stream not available in scrcpy client'
|
|
526
|
+
});
|
|
584
527
|
}
|
|
585
528
|
} catch (error) {
|
|
586
529
|
console.error('error processing video stream:', error);
|
|
587
|
-
|
|
588
|
-
|
|
530
|
+
socket.emit('error', {
|
|
531
|
+
message: `Video stream processing error: ${error.message}`
|
|
532
|
+
});
|
|
589
533
|
}
|
|
590
534
|
if (scrcpyClient?.controller) socket.emit('control-ready');
|
|
591
535
|
} catch (error) {
|
|
592
536
|
console.error('failed to connect device:', error);
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
537
|
+
if (scrcpyClient) {
|
|
538
|
+
try {
|
|
539
|
+
await scrcpyClient.close();
|
|
540
|
+
} catch (closeError) {
|
|
541
|
+
console.error('failed to close scrcpy client after error:', closeError);
|
|
542
|
+
}
|
|
543
|
+
scrcpyClient = null;
|
|
544
|
+
}
|
|
545
|
+
socket.emit('error', {
|
|
546
|
+
message: `Failed to connect device: ${error?.message || 'Unknown error'}`
|
|
547
|
+
});
|
|
598
548
|
}
|
|
599
549
|
});
|
|
600
550
|
socket.on('disconnect', async (reason)=>{
|
|
601
551
|
debugPage('client disconnected, id: %s, reason: %s', socket.id, reason);
|
|
602
|
-
|
|
603
|
-
|
|
552
|
+
if (scrcpyClient) {
|
|
553
|
+
try {
|
|
554
|
+
debugPage('closing scrcpy client');
|
|
555
|
+
await scrcpyClient.close();
|
|
556
|
+
} catch (error) {
|
|
557
|
+
console.error('failed to close scrcpy client:', error);
|
|
558
|
+
}
|
|
559
|
+
scrcpyClient = null;
|
|
560
|
+
}
|
|
604
561
|
});
|
|
605
562
|
sendDevicesList();
|
|
606
563
|
});
|
|
@@ -664,8 +621,6 @@ class ScrcpyServer {
|
|
|
664
621
|
this.app = express();
|
|
665
622
|
this.httpServer = createServer(this.app);
|
|
666
623
|
this.io = new Server(this.httpServer, {
|
|
667
|
-
pingInterval: 25000,
|
|
668
|
-
pingTimeout: 60000,
|
|
669
624
|
cors: {
|
|
670
625
|
origin (origin, callback) {
|
|
671
626
|
callback(null, isAllowedOrigin(origin));
|