@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 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 getAdbTargets();
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: getAdbTargets,
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
- const currentDevicesJson = JSON.stringify(devices);
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.httpServer) return this.httpServer.close();
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 getAdbTargets();
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: getAdbTargets,
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
- const currentDevicesJson = JSON.stringify(devices);
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.httpServer) return this.httpServer.close();
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 getAdbTargets();
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: getAdbTargets,
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
- const currentDevicesJson = JSON.stringify(devices);
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.httpServer) return this.httpServer.close();
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 getAdbTargets();
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: getAdbTargets,
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
- const currentDevicesJson = JSON.stringify(devices);
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.httpServer) return this.httpServer.close();
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.6",
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.6",
38
- "@midscene/core": "1.7.6",
39
- "@midscene/playground": "1.7.6",
40
- "@midscene/shared": "1.7.6"
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.2b64a7a3.js"></script><link href="/static/css/index.dc500f18.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/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>