@midscene/android-playground 0.30.10 → 0.30.11-beta-20251218071621.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 +28 -35
- package/dist/lib/bin.js +424 -474
- package/package.json +7 -7
- package/static/index.html +1 -1
- package/static/static/css/index.3784ddf6.css +2 -0
- package/static/static/css/index.3784ddf6.css.map +1 -0
- package/static/static/js/861.1a36955c.js +641 -0
- package/static/static/js/{374.e8fd2f39.js.LICENSE.txt → 861.1a36955c.js.LICENSE.txt} +2 -4
- package/static/static/js/861.1a36955c.js.map +1 -0
- package/static/static/js/async/195.05312dd9.js +3 -0
- package/static/static/js/async/195.05312dd9.js.map +1 -0
- package/static/static/js/async/{702.1f38a17e.js → 199.de925865.js} +17 -17
- package/static/static/js/async/199.de925865.js.map +1 -0
- package/static/static/js/async/221.213edee4.js +21 -0
- package/static/static/js/async/221.213edee4.js.map +1 -0
- package/static/static/js/async/271.1691b0d2.js +30 -0
- package/static/static/js/async/271.1691b0d2.js.map +1 -0
- package/static/static/js/async/35.918ea007.js +1 -0
- package/static/static/js/async/376.b7913581.js +2 -0
- package/static/static/js/async/376.b7913581.js.map +1 -0
- package/static/static/js/async/{644.910ce3d0.js → 467.9dc30840.js} +1 -1
- package/static/static/js/async/652.af2f1788.js +3 -0
- package/static/static/js/async/652.af2f1788.js.map +1 -0
- package/static/static/js/async/856.379e0ce3.js +158 -0
- package/static/static/js/async/{212.850ade70.js.map → 856.379e0ce3.js.map} +1 -1
- package/static/static/js/async/860.baac349b.js +2 -0
- package/static/static/js/async/860.baac349b.js.map +1 -0
- package/static/static/js/async/990.bb6496cd.js +26 -0
- package/static/static/js/async/990.bb6496cd.js.map +1 -0
- package/static/static/js/index.8764fe30.js +10 -0
- package/static/static/js/index.8764fe30.js.map +1 -0
- package/static/static/js/lib-react.574e3de9.js +3 -0
- package/static/static/js/lib-react.574e3de9.js.map +1 -0
- package/static/static/css/index.ea878c95.css +0 -2
- package/static/static/css/index.ea878c95.css.map +0 -1
- package/static/static/js/374.e8fd2f39.js +0 -650
- package/static/static/js/374.e8fd2f39.js.map +0 -1
- package/static/static/js/async/166.834644b5.js +0 -2
- package/static/static/js/async/166.834644b5.js.map +0 -1
- package/static/static/js/async/173.f2381e64.js +0 -3
- package/static/static/js/async/173.f2381e64.js.map +0 -1
- package/static/static/js/async/212.850ade70.js +0 -158
- package/static/static/js/async/329.261bc4a1.js +0 -26
- package/static/static/js/async/329.261bc4a1.js.map +0 -1
- package/static/static/js/async/364.d88c3cff.js +0 -30
- package/static/static/js/async/364.d88c3cff.js.map +0 -1
- package/static/static/js/async/544.18ac9afb.js +0 -2
- package/static/static/js/async/544.18ac9afb.js.map +0 -1
- package/static/static/js/async/582.8f4b5264.js +0 -21
- package/static/static/js/async/582.8f4b5264.js.map +0 -1
- package/static/static/js/async/624.8a1fe2e8.js +0 -3
- package/static/static/js/async/624.8a1fe2e8.js.map +0 -1
- package/static/static/js/async/659.d19e8c15.js +0 -21
- package/static/static/js/async/659.d19e8c15.js.map +0 -1
- package/static/static/js/async/702.1f38a17e.js.map +0 -1
- package/static/static/js/async/920.48d269c8.js +0 -2
- package/static/static/js/async/920.48d269c8.js.map +0 -1
- package/static/static/js/async/983.b98b40af.js +0 -1
- package/static/static/js/index.418b4242.js +0 -10
- package/static/static/js/index.418b4242.js.map +0 -1
- package/static/static/js/lib-react.c74a0742.js +0 -3
- package/static/static/js/lib-react.c74a0742.js.map +0 -1
- /package/static/static/js/{index.418b4242.js.LICENSE.txt → index.8764fe30.js.LICENSE.txt} +0 -0
- /package/static/static/js/{lib-react.c74a0742.js.LICENSE.txt → lib-react.574e3de9.js.LICENSE.txt} +0 -0
- /package/static/static/wasm/{9e906fbf55e08f98.module.wasm → 9e906fbf.module.wasm} +0 -0
package/dist/lib/bin.js
CHANGED
|
@@ -1,46 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var
|
|
3
|
-
"@yume-chan/adb": function(module) {
|
|
4
|
-
module.exports = import("@yume-chan/adb").then(function(module) {
|
|
5
|
-
return module;
|
|
6
|
-
});
|
|
7
|
-
},
|
|
8
|
-
"@yume-chan/adb-scrcpy": function(module) {
|
|
9
|
-
module.exports = import("@yume-chan/adb-scrcpy").then(function(module) {
|
|
10
|
-
return module;
|
|
11
|
-
});
|
|
12
|
-
},
|
|
13
|
-
"@yume-chan/adb-server-node-tcp": function(module) {
|
|
14
|
-
module.exports = import("@yume-chan/adb-server-node-tcp").then(function(module) {
|
|
15
|
-
return module;
|
|
16
|
-
});
|
|
17
|
-
},
|
|
18
|
-
"@yume-chan/scrcpy": function(module) {
|
|
19
|
-
module.exports = import("@yume-chan/scrcpy").then(function(module) {
|
|
20
|
-
return module;
|
|
21
|
-
});
|
|
22
|
-
},
|
|
23
|
-
"@yume-chan/stream-extra": function(module) {
|
|
24
|
-
module.exports = import("@yume-chan/stream-extra").then(function(module) {
|
|
25
|
-
return module;
|
|
26
|
-
});
|
|
27
|
-
},
|
|
28
|
-
open: function(module) {
|
|
29
|
-
module.exports = import("open").then(function(module) {
|
|
30
|
-
return module;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
var __webpack_module_cache__ = {};
|
|
35
|
-
function __webpack_require__(moduleId) {
|
|
36
|
-
var cachedModule = __webpack_module_cache__[moduleId];
|
|
37
|
-
if (void 0 !== cachedModule) return cachedModule.exports;
|
|
38
|
-
var module = __webpack_module_cache__[moduleId] = {
|
|
39
|
-
exports: {}
|
|
40
|
-
};
|
|
41
|
-
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
42
|
-
return module.exports;
|
|
43
|
-
}
|
|
2
|
+
var __webpack_require__ = {};
|
|
44
3
|
(()=>{
|
|
45
4
|
__webpack_require__.n = (module)=>{
|
|
46
5
|
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
@@ -62,482 +21,473 @@ function __webpack_require__(moduleId) {
|
|
|
62
21
|
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
63
22
|
})();
|
|
64
23
|
var __webpack_exports__ = {};
|
|
65
|
-
(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
this.app.get('/api/devices', async (req, res)=>{
|
|
99
|
-
try {
|
|
100
|
-
const devices = await this.getDevicesList();
|
|
101
|
-
res.json({
|
|
102
|
-
devices,
|
|
103
|
-
currentDeviceId: this.currentDeviceId
|
|
104
|
-
});
|
|
105
|
-
} catch (error) {
|
|
106
|
-
res.status(500).json({
|
|
107
|
-
error: error.message || 'Failed to get devices list'
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
async getDevicesList() {
|
|
24
|
+
const external_node_child_process_namespaceObject = require("node:child_process");
|
|
25
|
+
const external_node_net_namespaceObject = require("node:net");
|
|
26
|
+
const external_node_path_namespaceObject = require("node:path");
|
|
27
|
+
var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
|
|
28
|
+
const external_node_util_namespaceObject = require("node:util");
|
|
29
|
+
const prompts_namespaceObject = require("@inquirer/prompts");
|
|
30
|
+
const android_namespaceObject = require("@midscene/android");
|
|
31
|
+
const playground_namespaceObject = require("@midscene/playground");
|
|
32
|
+
const constants_namespaceObject = require("@midscene/shared/constants");
|
|
33
|
+
const external_node_fs_namespaceObject = require("node:fs");
|
|
34
|
+
const external_node_http_namespaceObject = require("node:http");
|
|
35
|
+
require("node:url");
|
|
36
|
+
const logger_namespaceObject = require("@midscene/shared/logger");
|
|
37
|
+
const external_cors_namespaceObject = require("cors");
|
|
38
|
+
var external_cors_default = /*#__PURE__*/ __webpack_require__.n(external_cors_namespaceObject);
|
|
39
|
+
const external_express_namespaceObject = require("express");
|
|
40
|
+
var external_express_default = /*#__PURE__*/ __webpack_require__.n(external_express_namespaceObject);
|
|
41
|
+
const external_socket_io_namespaceObject = require("socket.io");
|
|
42
|
+
function _define_property(obj, key, value) {
|
|
43
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
44
|
+
value: value,
|
|
45
|
+
enumerable: true,
|
|
46
|
+
configurable: true,
|
|
47
|
+
writable: true
|
|
48
|
+
});
|
|
49
|
+
else obj[key] = value;
|
|
50
|
+
return obj;
|
|
51
|
+
}
|
|
52
|
+
const debugPage = (0, logger_namespaceObject.getDebug)('android:playground');
|
|
53
|
+
const promiseExec = (0, external_node_util_namespaceObject.promisify)(external_node_child_process_namespaceObject.exec);
|
|
54
|
+
class ScrcpyServer {
|
|
55
|
+
setupApiRoutes() {
|
|
56
|
+
this.app.get('/api/devices', async (req, res)=>{
|
|
113
57
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return [];
|
|
119
|
-
}
|
|
120
|
-
debugPage('success to get adb client, start to request devices list');
|
|
121
|
-
let devices;
|
|
122
|
-
try {
|
|
123
|
-
devices = await client.getDevices();
|
|
124
|
-
debugPage('original devices list:', devices);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.error('failed to get devices list:', error);
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
if (!devices || 0 === devices.length) return [];
|
|
130
|
-
const formattedDevices = devices.map((device)=>{
|
|
131
|
-
const result = {
|
|
132
|
-
id: device.serial,
|
|
133
|
-
name: device.product || device.model || device.serial,
|
|
134
|
-
status: device.state || 'device'
|
|
135
|
-
};
|
|
136
|
-
return result;
|
|
58
|
+
const devices = await this.getDevicesList();
|
|
59
|
+
res.json({
|
|
60
|
+
devices,
|
|
61
|
+
currentDeviceId: this.currentDeviceId
|
|
137
62
|
});
|
|
138
|
-
return formattedDevices;
|
|
139
63
|
} catch (error) {
|
|
140
|
-
|
|
64
|
+
res.status(500).json({
|
|
65
|
+
error: error.message || 'Failed to get devices list'
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async getDevicesList() {
|
|
71
|
+
try {
|
|
72
|
+
debugPage('start to get devices list');
|
|
73
|
+
const client = await this.getAdbClient();
|
|
74
|
+
if (!client) {
|
|
75
|
+
console.warn('failed to get adb client');
|
|
141
76
|
return [];
|
|
142
77
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const { AdbServerClient } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "@yume-chan/adb"));
|
|
146
|
-
const { AdbServerNodeTcpConnector } = await Promise.resolve().then(__webpack_require__.bind(__webpack_require__, "@yume-chan/adb-server-node-tcp"));
|
|
78
|
+
debugPage('success to get adb client, start to request devices list');
|
|
79
|
+
let devices;
|
|
147
80
|
try {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
await promiseExec('adb start-server');
|
|
151
|
-
debugPage('adb server started');
|
|
152
|
-
debugPage('initialize adb client');
|
|
153
|
-
this.adbClient = new AdbServerClient(new AdbServerNodeTcpConnector({
|
|
154
|
-
host: '127.0.0.1',
|
|
155
|
-
port: 5037
|
|
156
|
-
}));
|
|
157
|
-
await debugPage('success to initialize adb client');
|
|
158
|
-
}
|
|
159
|
-
return this.adbClient;
|
|
81
|
+
devices = await client.getDevices();
|
|
82
|
+
debugPage('original devices list:', devices);
|
|
160
83
|
} catch (error) {
|
|
161
|
-
console.error('failed to get
|
|
162
|
-
return
|
|
84
|
+
console.error('failed to get devices list:', error);
|
|
85
|
+
return [];
|
|
163
86
|
}
|
|
87
|
+
if (!devices || 0 === devices.length) return [];
|
|
88
|
+
const formattedDevices = devices.map((device)=>{
|
|
89
|
+
const result = {
|
|
90
|
+
id: device.serial,
|
|
91
|
+
name: device.product || device.model || device.serial,
|
|
92
|
+
status: device.state || 'device'
|
|
93
|
+
};
|
|
94
|
+
return result;
|
|
95
|
+
});
|
|
96
|
+
return formattedDevices;
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error('failed to get devices list:', error);
|
|
99
|
+
return [];
|
|
164
100
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return new Adb(await client.createTransport(devices[0]));
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.error('failed to get adb client:', error);
|
|
183
|
-
return null;
|
|
101
|
+
}
|
|
102
|
+
async getAdbClient() {
|
|
103
|
+
const { AdbServerClient } = await import("@yume-chan/adb");
|
|
104
|
+
const { AdbServerNodeTcpConnector } = await import("@yume-chan/adb-server-node-tcp");
|
|
105
|
+
try {
|
|
106
|
+
if (this.adbClient) debugPage('use existing adb client');
|
|
107
|
+
else {
|
|
108
|
+
await promiseExec('adb start-server');
|
|
109
|
+
debugPage('adb server started');
|
|
110
|
+
debugPage('initialize adb client');
|
|
111
|
+
this.adbClient = new AdbServerClient(new AdbServerNodeTcpConnector({
|
|
112
|
+
host: '127.0.0.1',
|
|
113
|
+
port: 5037
|
|
114
|
+
}));
|
|
115
|
+
await debugPage('success to initialize adb client');
|
|
184
116
|
}
|
|
117
|
+
return this.adbClient;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.error('failed to get adb client:', error);
|
|
120
|
+
return null;
|
|
185
121
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
videoBitRate: 2000000,
|
|
199
|
-
...options
|
|
200
|
-
});
|
|
201
|
-
return await AdbScrcpyClient.start(adb, DefaultServerPath, new AdbScrcpyOptions2_1(scrcpyOptions));
|
|
202
|
-
} catch (error) {
|
|
203
|
-
console.error('failed to start scrcpy:', error);
|
|
204
|
-
throw error;
|
|
122
|
+
}
|
|
123
|
+
async getAdb(deviceId) {
|
|
124
|
+
const { Adb } = await import("@yume-chan/adb");
|
|
125
|
+
try {
|
|
126
|
+
const client = await this.getAdbClient();
|
|
127
|
+
if (!client) return null;
|
|
128
|
+
const targetDeviceId = deviceId || this.currentDeviceId;
|
|
129
|
+
if (targetDeviceId) {
|
|
130
|
+
this.currentDeviceId = targetDeviceId;
|
|
131
|
+
return new Adb(await client.createTransport({
|
|
132
|
+
serial: targetDeviceId
|
|
133
|
+
}));
|
|
205
134
|
}
|
|
135
|
+
const devices = await client.getDevices();
|
|
136
|
+
if (0 === devices.length) return null;
|
|
137
|
+
this.currentDeviceId = devices[0].serial;
|
|
138
|
+
return new Adb(await client.createTransport(devices[0]));
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error('failed to get adb client:', error);
|
|
141
|
+
return null;
|
|
206
142
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
143
|
+
}
|
|
144
|
+
async startScrcpy(adb, options = {}) {
|
|
145
|
+
const { AdbScrcpyClient, AdbScrcpyOptions2_1 } = await import("@yume-chan/adb-scrcpy");
|
|
146
|
+
const { ReadableStream } = await import("@yume-chan/stream-extra");
|
|
147
|
+
const { ScrcpyOptions3_1, DefaultServerPath } = await import("@yume-chan/scrcpy");
|
|
148
|
+
const currentDir = __dirname;
|
|
149
|
+
const serverBinPath = external_node_path_default().resolve(currentDir, '../../bin/server.bin');
|
|
150
|
+
try {
|
|
151
|
+
await AdbScrcpyClient.pushServer(adb, ReadableStream.from((0, external_node_fs_namespaceObject.createReadStream)(serverBinPath)));
|
|
152
|
+
const scrcpyOptions = new ScrcpyOptions3_1({
|
|
153
|
+
audio: false,
|
|
154
|
+
control: true,
|
|
155
|
+
maxSize: 1024,
|
|
156
|
+
videoBitRate: 2000000,
|
|
157
|
+
...options
|
|
158
|
+
});
|
|
159
|
+
return await AdbScrcpyClient.start(adb, DefaultServerPath, new AdbScrcpyOptions2_1(scrcpyOptions));
|
|
160
|
+
} catch (error) {
|
|
161
|
+
console.error('failed to start scrcpy:', error);
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
setupSocketHandlers() {
|
|
166
|
+
this.io.on('connection', async (socket)=>{
|
|
167
|
+
debugPage('client connected, id: %s, client address: %s', socket.id, socket.handshake.address);
|
|
168
|
+
let scrcpyClient = null;
|
|
169
|
+
let adb = null;
|
|
170
|
+
const sendDevicesList = async ()=>{
|
|
171
|
+
try {
|
|
172
|
+
debugPage('Socket request to get devices list');
|
|
173
|
+
const devices = await this.getDevicesList();
|
|
174
|
+
debugPage('send devices list to client:', devices);
|
|
175
|
+
socket.emit('devices-list', {
|
|
176
|
+
devices,
|
|
177
|
+
currentDeviceId: this.currentDeviceId
|
|
178
|
+
});
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('failed to send devices list:', error);
|
|
181
|
+
socket.emit('error', {
|
|
182
|
+
message: 'failed to get devices list'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
await sendDevicesList();
|
|
187
|
+
socket.on('get-devices', async ()=>{
|
|
188
|
+
debugPage('received client request to get devices list');
|
|
228
189
|
await sendDevicesList();
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
190
|
+
});
|
|
191
|
+
socket.on('switch-device', async (deviceId)=>{
|
|
192
|
+
debugPage('received client request to switch device:', deviceId);
|
|
193
|
+
try {
|
|
194
|
+
if (scrcpyClient) {
|
|
195
|
+
await scrcpyClient.close();
|
|
196
|
+
scrcpyClient = null;
|
|
197
|
+
}
|
|
198
|
+
this.currentDeviceId = deviceId;
|
|
199
|
+
debugPage('device switched to:', deviceId);
|
|
200
|
+
socket.emit('device-switched', {
|
|
201
|
+
deviceId
|
|
202
|
+
});
|
|
203
|
+
this.io.emit('global-device-switched', {
|
|
204
|
+
deviceId,
|
|
205
|
+
timestamp: Date.now()
|
|
206
|
+
});
|
|
207
|
+
} catch (error) {
|
|
208
|
+
console.error('failed to switch device:', error);
|
|
209
|
+
socket.emit('error', {
|
|
210
|
+
message: `Failed to switch device: ${error?.message || 'Unknown error'}`
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
socket.on('connect-device', async (options)=>{
|
|
215
|
+
const { ScrcpyVideoCodecId } = await import("@yume-chan/scrcpy");
|
|
216
|
+
try {
|
|
217
|
+
debugPage('received device connection request, options: %s, client id: %s', options, socket.id);
|
|
218
|
+
adb = await this.getAdb(this.currentDeviceId || void 0);
|
|
219
|
+
if (!adb) {
|
|
220
|
+
console.error('no available device found');
|
|
251
221
|
socket.emit('error', {
|
|
252
|
-
message:
|
|
222
|
+
message: 'No device found'
|
|
253
223
|
});
|
|
224
|
+
return;
|
|
254
225
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
226
|
+
debugPage('starting scrcpy service, device id: %s', this.currentDeviceId);
|
|
227
|
+
scrcpyClient = await this.startScrcpy(adb, options);
|
|
228
|
+
debugPage('scrcpy service started successfully');
|
|
229
|
+
debugPage('check scrcpyClient object structure: %s', Object.getOwnPropertyNames(scrcpyClient).map((name)=>{
|
|
230
|
+
const type = typeof scrcpyClient[name];
|
|
231
|
+
const isPromise = 'object' === type && scrcpyClient[name] && 'function' == typeof scrcpyClient[name].then;
|
|
232
|
+
return `${name}: ${type}${isPromise ? ' (Promise)' : ''}`;
|
|
233
|
+
}));
|
|
258
234
|
try {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
});
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
debugPage('starting scrcpy service, device id: %s', this.currentDeviceId);
|
|
269
|
-
scrcpyClient = await this.startScrcpy(adb, options);
|
|
270
|
-
debugPage('scrcpy service started successfully');
|
|
271
|
-
debugPage('check scrcpyClient object structure: %s', Object.getOwnPropertyNames(scrcpyClient).map((name)=>{
|
|
272
|
-
const type = typeof scrcpyClient[name];
|
|
273
|
-
const isPromise = 'object' === type && scrcpyClient[name] && 'function' == typeof scrcpyClient[name].then;
|
|
274
|
-
return `${name}: ${type}${isPromise ? ' (Promise)' : ''}`;
|
|
275
|
-
}));
|
|
276
|
-
try {
|
|
277
|
-
if (scrcpyClient.videoStream) {
|
|
278
|
-
debugPage('videoStream exists, type: %s', typeof scrcpyClient.videoStream);
|
|
279
|
-
let videoStream;
|
|
280
|
-
if ('object' == typeof scrcpyClient.videoStream && 'function' == typeof scrcpyClient.videoStream.then) {
|
|
281
|
-
debugPage('videoStream is a Promise, waiting for resolution...');
|
|
282
|
-
videoStream = await scrcpyClient.videoStream;
|
|
283
|
-
} else {
|
|
284
|
-
debugPage('videoStream is not a Promise, directly use');
|
|
285
|
-
videoStream = scrcpyClient.videoStream;
|
|
286
|
-
}
|
|
287
|
-
debugPage('video stream fetched successfully, metadata: %s', videoStream.metadata);
|
|
288
|
-
const metadata = videoStream.metadata || {};
|
|
289
|
-
debugPage('original metadata: %s', metadata);
|
|
290
|
-
if (!metadata.codec) {
|
|
291
|
-
debugPage('metadata does not have codec field, use H264 by default');
|
|
292
|
-
metadata.codec = ScrcpyVideoCodecId.H264;
|
|
293
|
-
}
|
|
294
|
-
if (!metadata.width || !metadata.height) {
|
|
295
|
-
debugPage('metadata does not have width or height field, use default values');
|
|
296
|
-
metadata.width = metadata.width || 1080;
|
|
297
|
-
metadata.height = metadata.height || 1920;
|
|
298
|
-
}
|
|
299
|
-
debugPage('prepare to send video-metadata event to client, data: %s', JSON.stringify(metadata));
|
|
300
|
-
socket.emit('video-metadata', metadata);
|
|
301
|
-
debugPage('video-metadata event sent to client, id: %s', socket.id);
|
|
302
|
-
const { stream } = videoStream;
|
|
303
|
-
const reader = stream.getReader();
|
|
304
|
-
const processStream = async ()=>{
|
|
305
|
-
try {
|
|
306
|
-
while(true){
|
|
307
|
-
const { done, value } = await reader.read();
|
|
308
|
-
if (done) break;
|
|
309
|
-
const frameType = value.type || 'data';
|
|
310
|
-
socket.emit('video-data', {
|
|
311
|
-
data: Array.from(value.data),
|
|
312
|
-
type: frameType,
|
|
313
|
-
timestamp: Date.now(),
|
|
314
|
-
keyFrame: value.keyFrame
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
} catch (error) {
|
|
318
|
-
console.error('error processing video stream:', error);
|
|
319
|
-
socket.emit('error', {
|
|
320
|
-
message: 'video stream processing error'
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
processStream();
|
|
235
|
+
if (scrcpyClient.videoStream) {
|
|
236
|
+
debugPage('videoStream exists, type: %s', typeof scrcpyClient.videoStream);
|
|
237
|
+
let videoStream;
|
|
238
|
+
if ('object' == typeof scrcpyClient.videoStream && 'function' == typeof scrcpyClient.videoStream.then) {
|
|
239
|
+
debugPage('videoStream is a Promise, waiting for resolution...');
|
|
240
|
+
videoStream = await scrcpyClient.videoStream;
|
|
325
241
|
} else {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
242
|
+
debugPage('videoStream is not a Promise, directly use');
|
|
243
|
+
videoStream = scrcpyClient.videoStream;
|
|
244
|
+
}
|
|
245
|
+
debugPage('video stream fetched successfully, metadata: %s', videoStream.metadata);
|
|
246
|
+
const metadata = videoStream.metadata || {};
|
|
247
|
+
debugPage('original metadata: %s', metadata);
|
|
248
|
+
if (!metadata.codec) {
|
|
249
|
+
debugPage('metadata does not have codec field, use H264 by default');
|
|
250
|
+
metadata.codec = ScrcpyVideoCodecId.H264;
|
|
251
|
+
}
|
|
252
|
+
if (!metadata.width || !metadata.height) {
|
|
253
|
+
debugPage('metadata does not have width or height field, use default values');
|
|
254
|
+
metadata.width = metadata.width || 1080;
|
|
255
|
+
metadata.height = metadata.height || 1920;
|
|
330
256
|
}
|
|
331
|
-
|
|
332
|
-
|
|
257
|
+
debugPage('prepare to send video-metadata event to client, data: %s', JSON.stringify(metadata));
|
|
258
|
+
socket.emit('video-metadata', metadata);
|
|
259
|
+
debugPage('video-metadata event sent to client, id: %s', socket.id);
|
|
260
|
+
const { stream } = videoStream;
|
|
261
|
+
const reader = stream.getReader();
|
|
262
|
+
const processStream = async ()=>{
|
|
263
|
+
try {
|
|
264
|
+
while(true){
|
|
265
|
+
const { done, value } = await reader.read();
|
|
266
|
+
if (done) break;
|
|
267
|
+
const frameType = value.type || 'data';
|
|
268
|
+
socket.emit('video-data', {
|
|
269
|
+
data: Array.from(value.data),
|
|
270
|
+
type: frameType,
|
|
271
|
+
timestamp: Date.now(),
|
|
272
|
+
keyFrame: value.keyFrame
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
console.error('error processing video stream:', error);
|
|
277
|
+
socket.emit('error', {
|
|
278
|
+
message: 'video stream processing error'
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
processStream();
|
|
283
|
+
} else {
|
|
284
|
+
console.error('scrcpyClient object does not have videoStream property');
|
|
333
285
|
socket.emit('error', {
|
|
334
|
-
message:
|
|
286
|
+
message: 'Video stream not available in scrcpy client'
|
|
335
287
|
});
|
|
336
288
|
}
|
|
337
|
-
if (null == scrcpyClient ? void 0 : scrcpyClient.controller) socket.emit('control-ready');
|
|
338
289
|
} catch (error) {
|
|
339
|
-
console.error('
|
|
290
|
+
console.error('error processing video stream:', error);
|
|
340
291
|
socket.emit('error', {
|
|
341
|
-
message: `
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
socket.on('disconnect', async (reason)=>{
|
|
346
|
-
debugPage('client disconnected, id: %s, reason: %s', socket.id, reason);
|
|
347
|
-
if (scrcpyClient) {
|
|
348
|
-
try {
|
|
349
|
-
debugPage('closing scrcpy client');
|
|
350
|
-
await scrcpyClient.close();
|
|
351
|
-
} catch (error) {
|
|
352
|
-
console.error('failed to close scrcpy client:', error);
|
|
353
|
-
}
|
|
354
|
-
scrcpyClient = null;
|
|
355
|
-
}
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
async launch(port) {
|
|
360
|
-
this.port = port || this.defaultPort;
|
|
361
|
-
return new Promise((resolve)=>{
|
|
362
|
-
this.httpServer.listen(this.port, ()=>{
|
|
363
|
-
console.log(`Scrcpy server running at: http://localhost:${this.port}`);
|
|
364
|
-
this.startDeviceMonitoring();
|
|
365
|
-
resolve(this);
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
startDeviceMonitoring() {
|
|
370
|
-
this.devicePollInterval = setInterval(async ()=>{
|
|
371
|
-
try {
|
|
372
|
-
const devices = await this.getDevicesList();
|
|
373
|
-
const currentDevicesJson = JSON.stringify(devices);
|
|
374
|
-
if (this.lastDeviceList !== currentDevicesJson) {
|
|
375
|
-
debugPage('devices list changed, push to all connected clients');
|
|
376
|
-
this.lastDeviceList = currentDevicesJson;
|
|
377
|
-
if (!this.currentDeviceId && devices.length > 0) {
|
|
378
|
-
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
379
|
-
if (onlineDevices.length > 0) {
|
|
380
|
-
this.currentDeviceId = onlineDevices[0].id;
|
|
381
|
-
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
this.io.emit('devices-list', {
|
|
385
|
-
devices,
|
|
386
|
-
currentDeviceId: this.currentDeviceId
|
|
292
|
+
message: `Video stream processing error: ${error.message}`
|
|
387
293
|
});
|
|
388
294
|
}
|
|
295
|
+
if (scrcpyClient?.controller) socket.emit('control-ready');
|
|
389
296
|
} catch (error) {
|
|
390
|
-
console.error('
|
|
297
|
+
console.error('failed to connect device:', error);
|
|
298
|
+
socket.emit('error', {
|
|
299
|
+
message: `Failed to connect device: ${error?.message || 'Unknown error'}`
|
|
300
|
+
});
|
|
391
301
|
}
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
_define_property(this, "httpServer", void 0);
|
|
404
|
-
_define_property(this, "io", void 0);
|
|
405
|
-
_define_property(this, "port", void 0);
|
|
406
|
-
_define_property(this, "defaultPort", constants_namespaceObject.SCRCPY_SERVER_PORT);
|
|
407
|
-
_define_property(this, "adbClient", null);
|
|
408
|
-
_define_property(this, "currentDeviceId", null);
|
|
409
|
-
_define_property(this, "devicePollInterval", null);
|
|
410
|
-
_define_property(this, "lastDeviceList", '');
|
|
411
|
-
this.app = external_express_default()();
|
|
412
|
-
this.httpServer = (0, external_node_http_namespaceObject.createServer)(this.app);
|
|
413
|
-
this.io = new external_socket_io_namespaceObject.Server(this.httpServer, {
|
|
414
|
-
cors: {
|
|
415
|
-
origin: [
|
|
416
|
-
/^http:\/\/localhost(:\d+)?$/,
|
|
417
|
-
/^http:\/\/127\.0\.0\.1(:\d+)?$/
|
|
418
|
-
],
|
|
419
|
-
methods: [
|
|
420
|
-
'GET',
|
|
421
|
-
'POST'
|
|
422
|
-
],
|
|
423
|
-
credentials: true
|
|
302
|
+
});
|
|
303
|
+
socket.on('disconnect', async (reason)=>{
|
|
304
|
+
debugPage('client disconnected, id: %s, reason: %s', socket.id, reason);
|
|
305
|
+
if (scrcpyClient) {
|
|
306
|
+
try {
|
|
307
|
+
debugPage('closing scrcpy client');
|
|
308
|
+
await scrcpyClient.close();
|
|
309
|
+
} catch (error) {
|
|
310
|
+
console.error('failed to close scrcpy client:', error);
|
|
311
|
+
}
|
|
312
|
+
scrcpyClient = null;
|
|
424
313
|
}
|
|
425
314
|
});
|
|
426
|
-
|
|
427
|
-
origin: '*',
|
|
428
|
-
credentials: true
|
|
429
|
-
}));
|
|
430
|
-
this.setupSocketHandlers();
|
|
431
|
-
this.setupApiRoutes();
|
|
432
|
-
}
|
|
315
|
+
});
|
|
433
316
|
}
|
|
434
|
-
|
|
435
|
-
|
|
317
|
+
async launch(port) {
|
|
318
|
+
this.port = port || this.defaultPort;
|
|
436
319
|
return new Promise((resolve)=>{
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
320
|
+
this.httpServer.listen(this.port, ()=>{
|
|
321
|
+
console.log(`Scrcpy server running at: http://localhost:${this.port}`);
|
|
322
|
+
this.startDeviceMonitoring();
|
|
323
|
+
resolve(this);
|
|
441
324
|
});
|
|
442
325
|
});
|
|
443
326
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
327
|
+
startDeviceMonitoring() {
|
|
328
|
+
this.devicePollInterval = setInterval(async ()=>{
|
|
329
|
+
try {
|
|
330
|
+
const devices = await this.getDevicesList();
|
|
331
|
+
const currentDevicesJson = JSON.stringify(devices);
|
|
332
|
+
if (this.lastDeviceList !== currentDevicesJson) {
|
|
333
|
+
debugPage('devices list changed, push to all connected clients');
|
|
334
|
+
this.lastDeviceList = currentDevicesJson;
|
|
335
|
+
if (!this.currentDeviceId && devices.length > 0) {
|
|
336
|
+
const onlineDevices = devices.filter((device)=>'device' === device.status.toLowerCase());
|
|
337
|
+
if (onlineDevices.length > 0) {
|
|
338
|
+
this.currentDeviceId = onlineDevices[0].id;
|
|
339
|
+
debugPage('auto select the first online device:', this.currentDeviceId);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
this.io.emit('devices-list', {
|
|
343
|
+
devices,
|
|
344
|
+
currentDeviceId: this.currentDeviceId
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error('device monitoring error:', error);
|
|
453
349
|
}
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
return port;
|
|
350
|
+
}, 3000);
|
|
457
351
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const lines = stdout.trim().split('\n').slice(1);
|
|
463
|
-
const devices = lines.map((line)=>{
|
|
464
|
-
const parts = line.trim().split('\t');
|
|
465
|
-
if (parts.length >= 2) return {
|
|
466
|
-
id: parts[0],
|
|
467
|
-
status: parts[1],
|
|
468
|
-
name: parts[0]
|
|
469
|
-
};
|
|
470
|
-
return null;
|
|
471
|
-
}).filter((device)=>null !== device && 'device' === device.status);
|
|
472
|
-
return devices;
|
|
473
|
-
} catch (error) {
|
|
474
|
-
console.error('Error getting ADB devices:', error);
|
|
475
|
-
return [];
|
|
352
|
+
close() {
|
|
353
|
+
if (this.devicePollInterval) {
|
|
354
|
+
clearInterval(this.devicePollInterval);
|
|
355
|
+
this.devicePollInterval = null;
|
|
476
356
|
}
|
|
357
|
+
if (this.httpServer) return this.httpServer.close();
|
|
477
358
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
359
|
+
constructor(){
|
|
360
|
+
_define_property(this, "app", void 0);
|
|
361
|
+
_define_property(this, "httpServer", void 0);
|
|
362
|
+
_define_property(this, "io", void 0);
|
|
363
|
+
_define_property(this, "port", void 0);
|
|
364
|
+
_define_property(this, "defaultPort", constants_namespaceObject.SCRCPY_SERVER_PORT);
|
|
365
|
+
_define_property(this, "adbClient", null);
|
|
366
|
+
_define_property(this, "currentDeviceId", null);
|
|
367
|
+
_define_property(this, "devicePollInterval", null);
|
|
368
|
+
_define_property(this, "lastDeviceList", '');
|
|
369
|
+
this.app = external_express_default()();
|
|
370
|
+
this.httpServer = (0, external_node_http_namespaceObject.createServer)(this.app);
|
|
371
|
+
this.io = new external_socket_io_namespaceObject.Server(this.httpServer, {
|
|
372
|
+
cors: {
|
|
373
|
+
origin: [
|
|
374
|
+
/^http:\/\/localhost(:\d+)?$/,
|
|
375
|
+
/^http:\/\/127\.0\.0\.1(:\d+)?$/
|
|
376
|
+
],
|
|
377
|
+
methods: [
|
|
378
|
+
'GET',
|
|
379
|
+
'POST'
|
|
380
|
+
],
|
|
381
|
+
credentials: true
|
|
382
|
+
}
|
|
500
383
|
});
|
|
501
|
-
|
|
384
|
+
this.app.use(external_cors_default()({
|
|
385
|
+
origin: '*',
|
|
386
|
+
credentials: true
|
|
387
|
+
}));
|
|
388
|
+
this.setupSocketHandlers();
|
|
389
|
+
this.setupApiRoutes();
|
|
502
390
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
playgroundServer.launch(availablePlaygroundPort),
|
|
523
|
-
scrcpyServer.launch(availableScrcpyPort)
|
|
524
|
-
]);
|
|
525
|
-
global.scrcpyServerPort = availableScrcpyPort;
|
|
526
|
-
console.log('');
|
|
527
|
-
console.log("\u2728 Midscene Android Playground is ready!");
|
|
528
|
-
console.log(`\u{1F3AE} Playground: http://localhost:${playgroundServer.port}`);
|
|
529
|
-
console.log(`\u{1F4F1} Device: ${selectedDeviceId}`);
|
|
530
|
-
console.log(`\u{1F511} Generated Server ID: ${playgroundServer.id}`);
|
|
531
|
-
console.log('');
|
|
532
|
-
open(`http://localhost:${playgroundServer.port}`);
|
|
533
|
-
} catch (error) {
|
|
534
|
-
console.error('Failed to start servers:', error);
|
|
391
|
+
}
|
|
392
|
+
(0, external_node_util_namespaceObject.promisify)(external_node_child_process_namespaceObject.exec);
|
|
393
|
+
async function isPortAvailable(port) {
|
|
394
|
+
return new Promise((resolve)=>{
|
|
395
|
+
const server = (0, external_node_net_namespaceObject.createServer)();
|
|
396
|
+
server.on('error', ()=>resolve(false));
|
|
397
|
+
server.listen(port, ()=>{
|
|
398
|
+
server.close(()=>resolve(true));
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
async function findAvailablePort(startPort) {
|
|
403
|
+
let port = startPort;
|
|
404
|
+
let attempts = 0;
|
|
405
|
+
const maxAttempts = 15;
|
|
406
|
+
while(!await isPortAvailable(port)){
|
|
407
|
+
attempts++;
|
|
408
|
+
if (attempts >= maxAttempts) {
|
|
409
|
+
console.error(`❌ Unable to find available port after ${maxAttempts} attempts starting from ${startPort}`);
|
|
535
410
|
process.exit(1);
|
|
536
411
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
412
|
+
port++;
|
|
413
|
+
}
|
|
414
|
+
return port;
|
|
415
|
+
}
|
|
416
|
+
async function getAdbDevices() {
|
|
417
|
+
try {
|
|
418
|
+
const devices = await (0, android_namespaceObject.getConnectedDevices)();
|
|
419
|
+
return devices.filter((device)=>'device' === device.state).map((device)=>({
|
|
420
|
+
id: device.udid,
|
|
421
|
+
status: device.state,
|
|
422
|
+
name: device.udid
|
|
423
|
+
}));
|
|
424
|
+
} catch (error) {
|
|
425
|
+
console.error('Error getting ADB devices:', error);
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
async function selectDevice() {
|
|
430
|
+
console.log('🔍 Scanning for Android devices...');
|
|
431
|
+
const devices = await getAdbDevices();
|
|
432
|
+
if (0 === devices.length) {
|
|
433
|
+
console.error('❌ No Android devices found!');
|
|
434
|
+
console.log('📱 Please ensure:');
|
|
435
|
+
console.log(' • Your device is connected via USB');
|
|
436
|
+
console.log(' • USB debugging is enabled');
|
|
437
|
+
console.log(' • Device is authorized for debugging');
|
|
438
|
+
process.exit(1);
|
|
439
|
+
}
|
|
440
|
+
if (1 === devices.length) {
|
|
441
|
+
console.log(`📱 Found device: ${devices[0].name} (${devices[0].id})`);
|
|
442
|
+
return devices[0].id;
|
|
443
|
+
}
|
|
444
|
+
const choices = devices.map((device)=>({
|
|
445
|
+
name: `${device.name} (${device.id})`,
|
|
446
|
+
value: device.id
|
|
447
|
+
}));
|
|
448
|
+
const selectedDevice = await (0, prompts_namespaceObject.select)({
|
|
449
|
+
message: '📱 Multiple devices found. Please select one:',
|
|
450
|
+
choices
|
|
451
|
+
});
|
|
452
|
+
return selectedDevice;
|
|
453
|
+
}
|
|
454
|
+
const staticDir = external_node_path_default().join(__dirname, '../../static');
|
|
455
|
+
const main = async ()=>{
|
|
456
|
+
const { default: open } = await import("open");
|
|
457
|
+
try {
|
|
458
|
+
const selectedDeviceId = await selectDevice();
|
|
459
|
+
console.log(`✅ Selected device: ${selectedDeviceId}`);
|
|
460
|
+
const playgroundServer = new playground_namespaceObject.PlaygroundServer(async ()=>{
|
|
461
|
+
const device = new android_namespaceObject.AndroidDevice(selectedDeviceId);
|
|
462
|
+
await device.connect();
|
|
463
|
+
return new android_namespaceObject.AndroidAgent(device);
|
|
464
|
+
}, staticDir);
|
|
465
|
+
const scrcpyServer = new ScrcpyServer();
|
|
466
|
+
scrcpyServer.currentDeviceId = selectedDeviceId;
|
|
467
|
+
console.log('🚀 Starting servers...');
|
|
468
|
+
const availablePlaygroundPort = await findAvailablePort(constants_namespaceObject.PLAYGROUND_SERVER_PORT);
|
|
469
|
+
const availableScrcpyPort = await findAvailablePort(constants_namespaceObject.SCRCPY_SERVER_PORT);
|
|
470
|
+
if (availablePlaygroundPort !== constants_namespaceObject.PLAYGROUND_SERVER_PORT) console.log(`⚠️ Port ${constants_namespaceObject.PLAYGROUND_SERVER_PORT} is busy, using port ${availablePlaygroundPort} instead`);
|
|
471
|
+
if (availableScrcpyPort !== constants_namespaceObject.SCRCPY_SERVER_PORT) console.log(`⚠️ Port ${constants_namespaceObject.SCRCPY_SERVER_PORT} is busy, using port ${availableScrcpyPort} instead`);
|
|
472
|
+
await Promise.all([
|
|
473
|
+
playgroundServer.launch(availablePlaygroundPort),
|
|
474
|
+
scrcpyServer.launch(availableScrcpyPort)
|
|
475
|
+
]);
|
|
476
|
+
global.scrcpyServerPort = availableScrcpyPort;
|
|
477
|
+
console.log('');
|
|
478
|
+
console.log('✨ Midscene Android Playground is ready!');
|
|
479
|
+
console.log(`🎮 Playground: http://localhost:${playgroundServer.port}`);
|
|
480
|
+
console.log(`📱 Device: ${selectedDeviceId}`);
|
|
481
|
+
console.log(`🔑 Generated Server ID: ${playgroundServer.id}`);
|
|
482
|
+
console.log('');
|
|
483
|
+
open(`http://localhost:${playgroundServer.port}`);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error('Failed to start servers:', error);
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
main();
|
|
490
|
+
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
541
491
|
Object.defineProperty(exports, '__esModule', {
|
|
542
492
|
value: true
|
|
543
493
|
});
|