@midscene/android-playground 1.7.6 → 1.7.7-beta-20260428092036.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/es/bin.mjs +59 -20
- package/dist/es/index.mjs +59 -20
- package/dist/lib/bin.js +59 -20
- package/dist/lib/index.js +59 -20
- package/dist/types/scrcpy-server.d.ts +16 -1
- package/package.json +5 -5
- package/static/index.html +1 -1
- package/static/static/css/index.a02873b3.css +2 -0
- package/static/static/css/index.a02873b3.css.map +1 -0
- package/static/static/js/{index.2b64a7a3.js → index.ca3f2ae9.js} +44 -44
- package/static/static/js/index.ca3f2ae9.js.map +1 -0
- package/static/static/css/index.dc500f18.css +0 -2
- package/static/static/css/index.dc500f18.css.map +0 -1
- package/static/static/js/index.2b64a7a3.js.map +0 -1
- /package/static/static/js/{index.2b64a7a3.js.LICENSE.txt → index.ca3f2ae9.js.LICENSE.txt} +0 -0
package/dist/es/bin.mjs
CHANGED
|
@@ -25,6 +25,18 @@ async function getAdbTargets() {
|
|
|
25
25
|
isDefault: 0 === index
|
|
26
26
|
}));
|
|
27
27
|
}
|
|
28
|
+
async function getAdbTargetsSafe() {
|
|
29
|
+
try {
|
|
30
|
+
return {
|
|
31
|
+
targets: await getAdbTargets()
|
|
32
|
+
};
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return {
|
|
35
|
+
targets: [],
|
|
36
|
+
error: error instanceof Error ? error.message : String(error)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
28
40
|
const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
29
41
|
id: 'android',
|
|
30
42
|
title: 'Midscene Android Playground',
|
|
@@ -40,12 +52,17 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
40
52
|
if (scrcpyPort !== SCRCPY_SERVER_PORT) console.log(`⚠️ Port ${SCRCPY_SERVER_PORT} is busy, using port ${scrcpyPort} instead`);
|
|
41
53
|
const sessionManager = {
|
|
42
54
|
async getSetupSchema () {
|
|
43
|
-
const targets = await
|
|
55
|
+
const { targets, error } = await getAdbTargetsSafe();
|
|
44
56
|
return {
|
|
45
57
|
title: 'Welcome to\nMidscene.js Playground!',
|
|
46
58
|
description: 'Select an available ADB device to create the current Android Agent',
|
|
47
59
|
primaryActionLabel: 'Create Agent',
|
|
48
60
|
autoSubmitWhenReady: 1 === targets.length,
|
|
61
|
+
notice: error ? {
|
|
62
|
+
type: 'warning',
|
|
63
|
+
message: 'Android device discovery failed',
|
|
64
|
+
description: error
|
|
65
|
+
} : void 0,
|
|
49
66
|
fields: [
|
|
50
67
|
{
|
|
51
68
|
key: 'deviceId',
|
|
@@ -64,7 +81,7 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
64
81
|
targets
|
|
65
82
|
};
|
|
66
83
|
},
|
|
67
|
-
listTargets:
|
|
84
|
+
listTargets: async ()=>(await getAdbTargetsSafe()).targets,
|
|
68
85
|
async createSession (input) {
|
|
69
86
|
const targets = await getAdbTargets();
|
|
70
87
|
const deviceId = 'string' == typeof input?.deviceId && input.deviceId ? input.deviceId : targets.find((target)=>target.isDefault)?.id;
|
|
@@ -240,6 +257,7 @@ class ScrcpyServer {
|
|
|
240
257
|
});
|
|
241
258
|
}
|
|
242
259
|
async getDevicesList() {
|
|
260
|
+
if (this.deviceListSource) return this.deviceListSource.getDevices();
|
|
243
261
|
try {
|
|
244
262
|
debugPage('start to get devices list');
|
|
245
263
|
const client = await this.getAdbClient();
|
|
@@ -271,6 +289,24 @@ class ScrcpyServer {
|
|
|
271
289
|
return [];
|
|
272
290
|
}
|
|
273
291
|
}
|
|
292
|
+
broadcastDevicesList(devices) {
|
|
293
|
+
const currentDevicesJson = JSON.stringify(devices);
|
|
294
|
+
if (this.lastDeviceList === currentDevicesJson) return;
|
|
295
|
+
debugPage('devices list changed, push to all connected clients');
|
|
296
|
+
this.lastDeviceList = currentDevicesJson;
|
|
297
|
+
if (this.currentDeviceId && !devices.some((device)=>device.id === this.currentDeviceId)) this.currentDeviceId = null;
|
|
298
|
+
if (!this.currentDeviceId && devices.length > 0) {
|
|
299
|
+
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
300
|
+
if (onlineDevices.length > 0) {
|
|
301
|
+
this.currentDeviceId = onlineDevices[0].id;
|
|
302
|
+
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
this.io.emit('devices-list', {
|
|
306
|
+
devices,
|
|
307
|
+
currentDeviceId: this.currentDeviceId
|
|
308
|
+
});
|
|
309
|
+
}
|
|
274
310
|
async getAdbClient() {
|
|
275
311
|
const { AdbServerClient } = await import("@yume-chan/adb");
|
|
276
312
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
@@ -526,25 +562,21 @@ class ScrcpyServer {
|
|
|
526
562
|
});
|
|
527
563
|
}
|
|
528
564
|
startDeviceMonitoring() {
|
|
565
|
+
if (this.deviceListSource) {
|
|
566
|
+
this.deviceListSourceUnsubscribe = this.deviceListSource.subscribe((devices)=>{
|
|
567
|
+
this.broadcastDevicesList(devices);
|
|
568
|
+
});
|
|
569
|
+
this.getDevicesList().then((devices)=>{
|
|
570
|
+
this.broadcastDevicesList(devices);
|
|
571
|
+
}).catch((error)=>{
|
|
572
|
+
console.error('device monitoring error:', error);
|
|
573
|
+
});
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
529
576
|
this.devicePollInterval = setInterval(async ()=>{
|
|
530
577
|
try {
|
|
531
578
|
const devices = await this.getDevicesList();
|
|
532
|
-
|
|
533
|
-
if (this.lastDeviceList !== currentDevicesJson) {
|
|
534
|
-
debugPage('devices list changed, push to all connected clients');
|
|
535
|
-
this.lastDeviceList = currentDevicesJson;
|
|
536
|
-
if (!this.currentDeviceId && devices.length > 0) {
|
|
537
|
-
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
538
|
-
if (onlineDevices.length > 0) {
|
|
539
|
-
this.currentDeviceId = onlineDevices[0].id;
|
|
540
|
-
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
this.io.emit('devices-list', {
|
|
544
|
-
devices,
|
|
545
|
-
currentDeviceId: this.currentDeviceId
|
|
546
|
-
});
|
|
547
|
-
}
|
|
579
|
+
this.broadcastDevicesList(devices);
|
|
548
580
|
} catch (error) {
|
|
549
581
|
console.error('device monitoring error:', error);
|
|
550
582
|
}
|
|
@@ -555,9 +587,13 @@ class ScrcpyServer {
|
|
|
555
587
|
clearInterval(this.devicePollInterval);
|
|
556
588
|
this.devicePollInterval = null;
|
|
557
589
|
}
|
|
558
|
-
if (this.
|
|
590
|
+
if (this.deviceListSourceUnsubscribe) {
|
|
591
|
+
this.deviceListSourceUnsubscribe();
|
|
592
|
+
this.deviceListSourceUnsubscribe = void 0;
|
|
593
|
+
}
|
|
594
|
+
if (this.httpServer?.listening) return this.httpServer.close();
|
|
559
595
|
}
|
|
560
|
-
constructor(){
|
|
596
|
+
constructor(options = {}){
|
|
561
597
|
scrcpy_server_define_property(this, "app", void 0);
|
|
562
598
|
scrcpy_server_define_property(this, "httpServer", void 0);
|
|
563
599
|
scrcpy_server_define_property(this, "io", void 0);
|
|
@@ -566,7 +602,10 @@ class ScrcpyServer {
|
|
|
566
602
|
scrcpy_server_define_property(this, "adbClient", null);
|
|
567
603
|
scrcpy_server_define_property(this, "currentDeviceId", null);
|
|
568
604
|
scrcpy_server_define_property(this, "devicePollInterval", null);
|
|
605
|
+
scrcpy_server_define_property(this, "deviceListSource", void 0);
|
|
606
|
+
scrcpy_server_define_property(this, "deviceListSourceUnsubscribe", void 0);
|
|
569
607
|
scrcpy_server_define_property(this, "lastDeviceList", '');
|
|
608
|
+
this.deviceListSource = options.deviceListSource;
|
|
570
609
|
this.app = express();
|
|
571
610
|
this.httpServer = createServer(this.app);
|
|
572
611
|
this.io = new Server(this.httpServer, {
|
package/dist/es/index.mjs
CHANGED
|
@@ -25,6 +25,18 @@ async function getAdbTargets() {
|
|
|
25
25
|
isDefault: 0 === index
|
|
26
26
|
}));
|
|
27
27
|
}
|
|
28
|
+
async function getAdbTargetsSafe() {
|
|
29
|
+
try {
|
|
30
|
+
return {
|
|
31
|
+
targets: await getAdbTargets()
|
|
32
|
+
};
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return {
|
|
35
|
+
targets: [],
|
|
36
|
+
error: error instanceof Error ? error.message : String(error)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
28
40
|
const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
29
41
|
id: 'android',
|
|
30
42
|
title: 'Midscene Android Playground',
|
|
@@ -40,12 +52,17 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
40
52
|
if (scrcpyPort !== SCRCPY_SERVER_PORT) console.log(`⚠️ Port ${SCRCPY_SERVER_PORT} is busy, using port ${scrcpyPort} instead`);
|
|
41
53
|
const sessionManager = {
|
|
42
54
|
async getSetupSchema () {
|
|
43
|
-
const targets = await
|
|
55
|
+
const { targets, error } = await getAdbTargetsSafe();
|
|
44
56
|
return {
|
|
45
57
|
title: 'Welcome to\nMidscene.js Playground!',
|
|
46
58
|
description: 'Select an available ADB device to create the current Android Agent',
|
|
47
59
|
primaryActionLabel: 'Create Agent',
|
|
48
60
|
autoSubmitWhenReady: 1 === targets.length,
|
|
61
|
+
notice: error ? {
|
|
62
|
+
type: 'warning',
|
|
63
|
+
message: 'Android device discovery failed',
|
|
64
|
+
description: error
|
|
65
|
+
} : void 0,
|
|
49
66
|
fields: [
|
|
50
67
|
{
|
|
51
68
|
key: 'deviceId',
|
|
@@ -64,7 +81,7 @@ const androidPlaygroundPlatform = definePlaygroundPlatform({
|
|
|
64
81
|
targets
|
|
65
82
|
};
|
|
66
83
|
},
|
|
67
|
-
listTargets:
|
|
84
|
+
listTargets: async ()=>(await getAdbTargetsSafe()).targets,
|
|
68
85
|
async createSession (input) {
|
|
69
86
|
const targets = await getAdbTargets();
|
|
70
87
|
const deviceId = 'string' == typeof input?.deviceId && input.deviceId ? input.deviceId : targets.find((target)=>target.isDefault)?.id;
|
|
@@ -240,6 +257,7 @@ class ScrcpyServer {
|
|
|
240
257
|
});
|
|
241
258
|
}
|
|
242
259
|
async getDevicesList() {
|
|
260
|
+
if (this.deviceListSource) return this.deviceListSource.getDevices();
|
|
243
261
|
try {
|
|
244
262
|
debugPage('start to get devices list');
|
|
245
263
|
const client = await this.getAdbClient();
|
|
@@ -271,6 +289,24 @@ class ScrcpyServer {
|
|
|
271
289
|
return [];
|
|
272
290
|
}
|
|
273
291
|
}
|
|
292
|
+
broadcastDevicesList(devices) {
|
|
293
|
+
const currentDevicesJson = JSON.stringify(devices);
|
|
294
|
+
if (this.lastDeviceList === currentDevicesJson) return;
|
|
295
|
+
debugPage('devices list changed, push to all connected clients');
|
|
296
|
+
this.lastDeviceList = currentDevicesJson;
|
|
297
|
+
if (this.currentDeviceId && !devices.some((device)=>device.id === this.currentDeviceId)) this.currentDeviceId = null;
|
|
298
|
+
if (!this.currentDeviceId && devices.length > 0) {
|
|
299
|
+
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
300
|
+
if (onlineDevices.length > 0) {
|
|
301
|
+
this.currentDeviceId = onlineDevices[0].id;
|
|
302
|
+
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
this.io.emit('devices-list', {
|
|
306
|
+
devices,
|
|
307
|
+
currentDeviceId: this.currentDeviceId
|
|
308
|
+
});
|
|
309
|
+
}
|
|
274
310
|
async getAdbClient() {
|
|
275
311
|
const { AdbServerClient } = await import("@yume-chan/adb");
|
|
276
312
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
@@ -526,25 +562,21 @@ class ScrcpyServer {
|
|
|
526
562
|
});
|
|
527
563
|
}
|
|
528
564
|
startDeviceMonitoring() {
|
|
565
|
+
if (this.deviceListSource) {
|
|
566
|
+
this.deviceListSourceUnsubscribe = this.deviceListSource.subscribe((devices)=>{
|
|
567
|
+
this.broadcastDevicesList(devices);
|
|
568
|
+
});
|
|
569
|
+
this.getDevicesList().then((devices)=>{
|
|
570
|
+
this.broadcastDevicesList(devices);
|
|
571
|
+
}).catch((error)=>{
|
|
572
|
+
console.error('device monitoring error:', error);
|
|
573
|
+
});
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
529
576
|
this.devicePollInterval = setInterval(async ()=>{
|
|
530
577
|
try {
|
|
531
578
|
const devices = await this.getDevicesList();
|
|
532
|
-
|
|
533
|
-
if (this.lastDeviceList !== currentDevicesJson) {
|
|
534
|
-
debugPage('devices list changed, push to all connected clients');
|
|
535
|
-
this.lastDeviceList = currentDevicesJson;
|
|
536
|
-
if (!this.currentDeviceId && devices.length > 0) {
|
|
537
|
-
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
538
|
-
if (onlineDevices.length > 0) {
|
|
539
|
-
this.currentDeviceId = onlineDevices[0].id;
|
|
540
|
-
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
this.io.emit('devices-list', {
|
|
544
|
-
devices,
|
|
545
|
-
currentDeviceId: this.currentDeviceId
|
|
546
|
-
});
|
|
547
|
-
}
|
|
579
|
+
this.broadcastDevicesList(devices);
|
|
548
580
|
} catch (error) {
|
|
549
581
|
console.error('device monitoring error:', error);
|
|
550
582
|
}
|
|
@@ -555,9 +587,13 @@ class ScrcpyServer {
|
|
|
555
587
|
clearInterval(this.devicePollInterval);
|
|
556
588
|
this.devicePollInterval = null;
|
|
557
589
|
}
|
|
558
|
-
if (this.
|
|
590
|
+
if (this.deviceListSourceUnsubscribe) {
|
|
591
|
+
this.deviceListSourceUnsubscribe();
|
|
592
|
+
this.deviceListSourceUnsubscribe = void 0;
|
|
593
|
+
}
|
|
594
|
+
if (this.httpServer?.listening) return this.httpServer.close();
|
|
559
595
|
}
|
|
560
|
-
constructor(){
|
|
596
|
+
constructor(options = {}){
|
|
561
597
|
scrcpy_server_define_property(this, "app", void 0);
|
|
562
598
|
scrcpy_server_define_property(this, "httpServer", void 0);
|
|
563
599
|
scrcpy_server_define_property(this, "io", void 0);
|
|
@@ -566,7 +602,10 @@ class ScrcpyServer {
|
|
|
566
602
|
scrcpy_server_define_property(this, "adbClient", null);
|
|
567
603
|
scrcpy_server_define_property(this, "currentDeviceId", null);
|
|
568
604
|
scrcpy_server_define_property(this, "devicePollInterval", null);
|
|
605
|
+
scrcpy_server_define_property(this, "deviceListSource", void 0);
|
|
606
|
+
scrcpy_server_define_property(this, "deviceListSourceUnsubscribe", void 0);
|
|
569
607
|
scrcpy_server_define_property(this, "lastDeviceList", '');
|
|
608
|
+
this.deviceListSource = options.deviceListSource;
|
|
570
609
|
this.app = express();
|
|
571
610
|
this.httpServer = createServer(this.app);
|
|
572
611
|
this.io = new Server(this.httpServer, {
|
package/dist/lib/bin.js
CHANGED
|
@@ -40,6 +40,18 @@ async function getAdbTargets() {
|
|
|
40
40
|
isDefault: 0 === index
|
|
41
41
|
}));
|
|
42
42
|
}
|
|
43
|
+
async function getAdbTargetsSafe() {
|
|
44
|
+
try {
|
|
45
|
+
return {
|
|
46
|
+
targets: await getAdbTargets()
|
|
47
|
+
};
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
targets: [],
|
|
51
|
+
error: error instanceof Error ? error.message : String(error)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
43
55
|
const androidPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroundPlatform)({
|
|
44
56
|
id: 'android',
|
|
45
57
|
title: 'Midscene Android Playground',
|
|
@@ -55,12 +67,17 @@ const androidPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroun
|
|
|
55
67
|
if (scrcpyPort !== constants_namespaceObject.SCRCPY_SERVER_PORT) console.log(`⚠️ Port ${constants_namespaceObject.SCRCPY_SERVER_PORT} is busy, using port ${scrcpyPort} instead`);
|
|
56
68
|
const sessionManager = {
|
|
57
69
|
async getSetupSchema () {
|
|
58
|
-
const targets = await
|
|
70
|
+
const { targets, error } = await getAdbTargetsSafe();
|
|
59
71
|
return {
|
|
60
72
|
title: 'Welcome to\nMidscene.js Playground!',
|
|
61
73
|
description: 'Select an available ADB device to create the current Android Agent',
|
|
62
74
|
primaryActionLabel: 'Create Agent',
|
|
63
75
|
autoSubmitWhenReady: 1 === targets.length,
|
|
76
|
+
notice: error ? {
|
|
77
|
+
type: 'warning',
|
|
78
|
+
message: 'Android device discovery failed',
|
|
79
|
+
description: error
|
|
80
|
+
} : void 0,
|
|
64
81
|
fields: [
|
|
65
82
|
{
|
|
66
83
|
key: 'deviceId',
|
|
@@ -79,7 +96,7 @@ const androidPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroun
|
|
|
79
96
|
targets
|
|
80
97
|
};
|
|
81
98
|
},
|
|
82
|
-
listTargets:
|
|
99
|
+
listTargets: async ()=>(await getAdbTargetsSafe()).targets,
|
|
83
100
|
async createSession (input) {
|
|
84
101
|
const targets = await getAdbTargets();
|
|
85
102
|
const deviceId = 'string' == typeof input?.deviceId && input.deviceId ? input.deviceId : targets.find((target)=>target.isDefault)?.id;
|
|
@@ -266,6 +283,7 @@ class ScrcpyServer {
|
|
|
266
283
|
});
|
|
267
284
|
}
|
|
268
285
|
async getDevicesList() {
|
|
286
|
+
if (this.deviceListSource) return this.deviceListSource.getDevices();
|
|
269
287
|
try {
|
|
270
288
|
debugPage('start to get devices list');
|
|
271
289
|
const client = await this.getAdbClient();
|
|
@@ -297,6 +315,24 @@ class ScrcpyServer {
|
|
|
297
315
|
return [];
|
|
298
316
|
}
|
|
299
317
|
}
|
|
318
|
+
broadcastDevicesList(devices) {
|
|
319
|
+
const currentDevicesJson = JSON.stringify(devices);
|
|
320
|
+
if (this.lastDeviceList === currentDevicesJson) return;
|
|
321
|
+
debugPage('devices list changed, push to all connected clients');
|
|
322
|
+
this.lastDeviceList = currentDevicesJson;
|
|
323
|
+
if (this.currentDeviceId && !devices.some((device)=>device.id === this.currentDeviceId)) this.currentDeviceId = null;
|
|
324
|
+
if (!this.currentDeviceId && devices.length > 0) {
|
|
325
|
+
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
326
|
+
if (onlineDevices.length > 0) {
|
|
327
|
+
this.currentDeviceId = onlineDevices[0].id;
|
|
328
|
+
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
this.io.emit('devices-list', {
|
|
332
|
+
devices,
|
|
333
|
+
currentDeviceId: this.currentDeviceId
|
|
334
|
+
});
|
|
335
|
+
}
|
|
300
336
|
async getAdbClient() {
|
|
301
337
|
const { AdbServerClient } = await import("@yume-chan/adb");
|
|
302
338
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
@@ -552,25 +588,21 @@ class ScrcpyServer {
|
|
|
552
588
|
});
|
|
553
589
|
}
|
|
554
590
|
startDeviceMonitoring() {
|
|
591
|
+
if (this.deviceListSource) {
|
|
592
|
+
this.deviceListSourceUnsubscribe = this.deviceListSource.subscribe((devices)=>{
|
|
593
|
+
this.broadcastDevicesList(devices);
|
|
594
|
+
});
|
|
595
|
+
this.getDevicesList().then((devices)=>{
|
|
596
|
+
this.broadcastDevicesList(devices);
|
|
597
|
+
}).catch((error)=>{
|
|
598
|
+
console.error('device monitoring error:', error);
|
|
599
|
+
});
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
555
602
|
this.devicePollInterval = setInterval(async ()=>{
|
|
556
603
|
try {
|
|
557
604
|
const devices = await this.getDevicesList();
|
|
558
|
-
|
|
559
|
-
if (this.lastDeviceList !== currentDevicesJson) {
|
|
560
|
-
debugPage('devices list changed, push to all connected clients');
|
|
561
|
-
this.lastDeviceList = currentDevicesJson;
|
|
562
|
-
if (!this.currentDeviceId && devices.length > 0) {
|
|
563
|
-
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
564
|
-
if (onlineDevices.length > 0) {
|
|
565
|
-
this.currentDeviceId = onlineDevices[0].id;
|
|
566
|
-
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
this.io.emit('devices-list', {
|
|
570
|
-
devices,
|
|
571
|
-
currentDeviceId: this.currentDeviceId
|
|
572
|
-
});
|
|
573
|
-
}
|
|
605
|
+
this.broadcastDevicesList(devices);
|
|
574
606
|
} catch (error) {
|
|
575
607
|
console.error('device monitoring error:', error);
|
|
576
608
|
}
|
|
@@ -581,9 +613,13 @@ class ScrcpyServer {
|
|
|
581
613
|
clearInterval(this.devicePollInterval);
|
|
582
614
|
this.devicePollInterval = null;
|
|
583
615
|
}
|
|
584
|
-
if (this.
|
|
616
|
+
if (this.deviceListSourceUnsubscribe) {
|
|
617
|
+
this.deviceListSourceUnsubscribe();
|
|
618
|
+
this.deviceListSourceUnsubscribe = void 0;
|
|
619
|
+
}
|
|
620
|
+
if (this.httpServer?.listening) return this.httpServer.close();
|
|
585
621
|
}
|
|
586
|
-
constructor(){
|
|
622
|
+
constructor(options = {}){
|
|
587
623
|
scrcpy_server_define_property(this, "app", void 0);
|
|
588
624
|
scrcpy_server_define_property(this, "httpServer", void 0);
|
|
589
625
|
scrcpy_server_define_property(this, "io", void 0);
|
|
@@ -592,7 +628,10 @@ class ScrcpyServer {
|
|
|
592
628
|
scrcpy_server_define_property(this, "adbClient", null);
|
|
593
629
|
scrcpy_server_define_property(this, "currentDeviceId", null);
|
|
594
630
|
scrcpy_server_define_property(this, "devicePollInterval", null);
|
|
631
|
+
scrcpy_server_define_property(this, "deviceListSource", void 0);
|
|
632
|
+
scrcpy_server_define_property(this, "deviceListSourceUnsubscribe", void 0);
|
|
595
633
|
scrcpy_server_define_property(this, "lastDeviceList", '');
|
|
634
|
+
this.deviceListSource = options.deviceListSource;
|
|
596
635
|
this.app = external_express_default()();
|
|
597
636
|
this.httpServer = (0, external_node_http_namespaceObject.createServer)(this.app);
|
|
598
637
|
this.io = new external_socket_io_namespaceObject.Server(this.httpServer, {
|
package/dist/lib/index.js
CHANGED
|
@@ -55,6 +55,18 @@ async function getAdbTargets() {
|
|
|
55
55
|
isDefault: 0 === index
|
|
56
56
|
}));
|
|
57
57
|
}
|
|
58
|
+
async function getAdbTargetsSafe() {
|
|
59
|
+
try {
|
|
60
|
+
return {
|
|
61
|
+
targets: await getAdbTargets()
|
|
62
|
+
};
|
|
63
|
+
} catch (error) {
|
|
64
|
+
return {
|
|
65
|
+
targets: [],
|
|
66
|
+
error: error instanceof Error ? error.message : String(error)
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
58
70
|
const androidPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroundPlatform)({
|
|
59
71
|
id: 'android',
|
|
60
72
|
title: 'Midscene Android Playground',
|
|
@@ -70,12 +82,17 @@ const androidPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroun
|
|
|
70
82
|
if (scrcpyPort !== constants_namespaceObject.SCRCPY_SERVER_PORT) console.log(`⚠️ Port ${constants_namespaceObject.SCRCPY_SERVER_PORT} is busy, using port ${scrcpyPort} instead`);
|
|
71
83
|
const sessionManager = {
|
|
72
84
|
async getSetupSchema () {
|
|
73
|
-
const targets = await
|
|
85
|
+
const { targets, error } = await getAdbTargetsSafe();
|
|
74
86
|
return {
|
|
75
87
|
title: 'Welcome to\nMidscene.js Playground!',
|
|
76
88
|
description: 'Select an available ADB device to create the current Android Agent',
|
|
77
89
|
primaryActionLabel: 'Create Agent',
|
|
78
90
|
autoSubmitWhenReady: 1 === targets.length,
|
|
91
|
+
notice: error ? {
|
|
92
|
+
type: 'warning',
|
|
93
|
+
message: 'Android device discovery failed',
|
|
94
|
+
description: error
|
|
95
|
+
} : void 0,
|
|
79
96
|
fields: [
|
|
80
97
|
{
|
|
81
98
|
key: 'deviceId',
|
|
@@ -94,7 +111,7 @@ const androidPlaygroundPlatform = (0, playground_namespaceObject.definePlaygroun
|
|
|
94
111
|
targets
|
|
95
112
|
};
|
|
96
113
|
},
|
|
97
|
-
listTargets:
|
|
114
|
+
listTargets: async ()=>(await getAdbTargetsSafe()).targets,
|
|
98
115
|
async createSession (input) {
|
|
99
116
|
const targets = await getAdbTargets();
|
|
100
117
|
const deviceId = 'string' == typeof input?.deviceId && input.deviceId ? input.deviceId : targets.find((target)=>target.isDefault)?.id;
|
|
@@ -281,6 +298,7 @@ class ScrcpyServer {
|
|
|
281
298
|
});
|
|
282
299
|
}
|
|
283
300
|
async getDevicesList() {
|
|
301
|
+
if (this.deviceListSource) return this.deviceListSource.getDevices();
|
|
284
302
|
try {
|
|
285
303
|
debugPage('start to get devices list');
|
|
286
304
|
const client = await this.getAdbClient();
|
|
@@ -312,6 +330,24 @@ class ScrcpyServer {
|
|
|
312
330
|
return [];
|
|
313
331
|
}
|
|
314
332
|
}
|
|
333
|
+
broadcastDevicesList(devices) {
|
|
334
|
+
const currentDevicesJson = JSON.stringify(devices);
|
|
335
|
+
if (this.lastDeviceList === currentDevicesJson) return;
|
|
336
|
+
debugPage('devices list changed, push to all connected clients');
|
|
337
|
+
this.lastDeviceList = currentDevicesJson;
|
|
338
|
+
if (this.currentDeviceId && !devices.some((device)=>device.id === this.currentDeviceId)) this.currentDeviceId = null;
|
|
339
|
+
if (!this.currentDeviceId && devices.length > 0) {
|
|
340
|
+
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
341
|
+
if (onlineDevices.length > 0) {
|
|
342
|
+
this.currentDeviceId = onlineDevices[0].id;
|
|
343
|
+
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
this.io.emit('devices-list', {
|
|
347
|
+
devices,
|
|
348
|
+
currentDeviceId: this.currentDeviceId
|
|
349
|
+
});
|
|
350
|
+
}
|
|
315
351
|
async getAdbClient() {
|
|
316
352
|
const { AdbServerClient } = await import("@yume-chan/adb");
|
|
317
353
|
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
@@ -567,25 +603,21 @@ class ScrcpyServer {
|
|
|
567
603
|
});
|
|
568
604
|
}
|
|
569
605
|
startDeviceMonitoring() {
|
|
606
|
+
if (this.deviceListSource) {
|
|
607
|
+
this.deviceListSourceUnsubscribe = this.deviceListSource.subscribe((devices)=>{
|
|
608
|
+
this.broadcastDevicesList(devices);
|
|
609
|
+
});
|
|
610
|
+
this.getDevicesList().then((devices)=>{
|
|
611
|
+
this.broadcastDevicesList(devices);
|
|
612
|
+
}).catch((error)=>{
|
|
613
|
+
console.error('device monitoring error:', error);
|
|
614
|
+
});
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
570
617
|
this.devicePollInterval = setInterval(async ()=>{
|
|
571
618
|
try {
|
|
572
619
|
const devices = await this.getDevicesList();
|
|
573
|
-
|
|
574
|
-
if (this.lastDeviceList !== currentDevicesJson) {
|
|
575
|
-
debugPage('devices list changed, push to all connected clients');
|
|
576
|
-
this.lastDeviceList = currentDevicesJson;
|
|
577
|
-
if (!this.currentDeviceId && devices.length > 0) {
|
|
578
|
-
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
579
|
-
if (onlineDevices.length > 0) {
|
|
580
|
-
this.currentDeviceId = onlineDevices[0].id;
|
|
581
|
-
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
this.io.emit('devices-list', {
|
|
585
|
-
devices,
|
|
586
|
-
currentDeviceId: this.currentDeviceId
|
|
587
|
-
});
|
|
588
|
-
}
|
|
620
|
+
this.broadcastDevicesList(devices);
|
|
589
621
|
} catch (error) {
|
|
590
622
|
console.error('device monitoring error:', error);
|
|
591
623
|
}
|
|
@@ -596,9 +628,13 @@ class ScrcpyServer {
|
|
|
596
628
|
clearInterval(this.devicePollInterval);
|
|
597
629
|
this.devicePollInterval = null;
|
|
598
630
|
}
|
|
599
|
-
if (this.
|
|
631
|
+
if (this.deviceListSourceUnsubscribe) {
|
|
632
|
+
this.deviceListSourceUnsubscribe();
|
|
633
|
+
this.deviceListSourceUnsubscribe = void 0;
|
|
634
|
+
}
|
|
635
|
+
if (this.httpServer?.listening) return this.httpServer.close();
|
|
600
636
|
}
|
|
601
|
-
constructor(){
|
|
637
|
+
constructor(options = {}){
|
|
602
638
|
scrcpy_server_define_property(this, "app", void 0);
|
|
603
639
|
scrcpy_server_define_property(this, "httpServer", void 0);
|
|
604
640
|
scrcpy_server_define_property(this, "io", void 0);
|
|
@@ -607,7 +643,10 @@ class ScrcpyServer {
|
|
|
607
643
|
scrcpy_server_define_property(this, "adbClient", null);
|
|
608
644
|
scrcpy_server_define_property(this, "currentDeviceId", null);
|
|
609
645
|
scrcpy_server_define_property(this, "devicePollInterval", null);
|
|
646
|
+
scrcpy_server_define_property(this, "deviceListSource", void 0);
|
|
647
|
+
scrcpy_server_define_property(this, "deviceListSourceUnsubscribe", void 0);
|
|
610
648
|
scrcpy_server_define_property(this, "lastDeviceList", '');
|
|
649
|
+
this.deviceListSource = options.deviceListSource;
|
|
611
650
|
this.app = external_express_default()();
|
|
612
651
|
this.httpServer = (0, external_node_http_namespaceObject.createServer)(this.app);
|
|
613
652
|
this.io = new external_socket_io_namespaceObject.Server(this.httpServer, {
|
|
@@ -7,6 +7,18 @@ export interface ScrcpyConnectDeviceRequest {
|
|
|
7
7
|
deviceId?: string;
|
|
8
8
|
maxSize?: number;
|
|
9
9
|
}
|
|
10
|
+
export interface ScrcpyListedDevice {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
status: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ScrcpyDeviceListSource {
|
|
16
|
+
getDevices(): Promise<ScrcpyListedDevice[]>;
|
|
17
|
+
subscribe(listener: (devices: ScrcpyListedDevice[]) => void): () => void;
|
|
18
|
+
}
|
|
19
|
+
export interface ScrcpyServerOptions {
|
|
20
|
+
deviceListSource?: ScrcpyDeviceListSource;
|
|
21
|
+
}
|
|
10
22
|
export declare function resolveRequestedDeviceId(options: ScrcpyConnectDeviceRequest | undefined, currentDeviceId: string | null): string | undefined;
|
|
11
23
|
export default class ScrcpyServer {
|
|
12
24
|
app: express.Application;
|
|
@@ -17,10 +29,13 @@ export default class ScrcpyServer {
|
|
|
17
29
|
adbClient: AdbServerClient | null;
|
|
18
30
|
currentDeviceId: string | null;
|
|
19
31
|
devicePollInterval: NodeJS.Timeout | null;
|
|
32
|
+
private deviceListSource?;
|
|
33
|
+
private deviceListSourceUnsubscribe?;
|
|
20
34
|
lastDeviceList: string;
|
|
21
|
-
constructor();
|
|
35
|
+
constructor(options?: ScrcpyServerOptions);
|
|
22
36
|
private setupApiRoutes;
|
|
23
37
|
private getDevicesList;
|
|
38
|
+
private broadcastDevicesList;
|
|
24
39
|
private getAdbClient;
|
|
25
40
|
private getAdb;
|
|
26
41
|
private startScrcpy;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/android-playground",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.7-beta-20260428092036.0",
|
|
4
4
|
"description": "Android playground for Midscene",
|
|
5
5
|
"main": "./dist/lib/index.js",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"express": "^4.21.2",
|
|
35
35
|
"open": "10.1.0",
|
|
36
36
|
"socket.io": "^4.8.1",
|
|
37
|
-
"@midscene/android": "1.7.
|
|
38
|
-
"@midscene/core": "1.7.
|
|
39
|
-
"@midscene/playground": "1.7.
|
|
40
|
-
"@midscene/shared": "1.7.
|
|
37
|
+
"@midscene/android": "1.7.7-beta-20260428092036.0",
|
|
38
|
+
"@midscene/core": "1.7.7-beta-20260428092036.0",
|
|
39
|
+
"@midscene/playground": "1.7.7-beta-20260428092036.0",
|
|
40
|
+
"@midscene/shared": "1.7.7-beta-20260428092036.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@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/883.91ca0de7.js"></script><script defer src="/static/js/index.
|
|
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/883.91ca0de7.js"></script><script defer src="/static/js/index.ca3f2ae9.js"></script><link href="/static/css/index.a02873b3.css" rel="stylesheet"></head><body><div id="root"></div></body></html>
|