@xcanwin/manyoyo 5.1.9 → 5.1.10
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/config.example.json +4 -0
- package/lib/plugin/playwright.js +132 -10
- package/package.json +1 -1
package/config.example.json
CHANGED
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
"vncPasswordEnvKey": "VNC_PASSWORD",
|
|
39
39
|
// playwright ext-download 的 CRX prodversion 参数
|
|
40
40
|
"extensionProdversion": "132.0.0.0",
|
|
41
|
+
// 注入 navigator.platform(默认与内置 UA 对齐为 MacIntel)
|
|
42
|
+
"navigatorPlatform": "MacIntel",
|
|
43
|
+
// 是否禁用 WebRTC(默认 false)
|
|
44
|
+
"disableWebRTC": false,
|
|
41
45
|
"ports": {
|
|
42
46
|
"contHeadless": 8931,
|
|
43
47
|
"contHeaded": 8932,
|
package/lib/plugin/playwright.js
CHANGED
|
@@ -65,6 +65,21 @@ const DEFAULT_FINGERPRINT_PROFILE = {
|
|
|
65
65
|
width: 1366,
|
|
66
66
|
height: 768
|
|
67
67
|
};
|
|
68
|
+
const DISABLE_WEBRTC_LAUNCH_ARGS = ['--disable-webrtc'];
|
|
69
|
+
|
|
70
|
+
function platformFromUserAgent(userAgent) {
|
|
71
|
+
const ua = String(userAgent || '').toLowerCase();
|
|
72
|
+
if (ua.includes('macintosh') || ua.includes('mac os x')) {
|
|
73
|
+
return 'MacIntel';
|
|
74
|
+
}
|
|
75
|
+
if (ua.includes('windows')) {
|
|
76
|
+
return 'Win32';
|
|
77
|
+
}
|
|
78
|
+
if (ua.includes('android') || ua.includes('linux')) {
|
|
79
|
+
return 'Linux x86_64';
|
|
80
|
+
}
|
|
81
|
+
return 'MacIntel';
|
|
82
|
+
}
|
|
68
83
|
|
|
69
84
|
function sleep(ms) {
|
|
70
85
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
@@ -94,6 +109,22 @@ function asStringArray(value, fallback) {
|
|
|
94
109
|
.filter(Boolean);
|
|
95
110
|
}
|
|
96
111
|
|
|
112
|
+
function asBoolean(value, fallback = false) {
|
|
113
|
+
if (typeof value === 'boolean') {
|
|
114
|
+
return value;
|
|
115
|
+
}
|
|
116
|
+
if (typeof value === 'string') {
|
|
117
|
+
const normalized = value.trim().toLowerCase();
|
|
118
|
+
if (normalized === 'true') {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
if (normalized === 'false') {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return fallback;
|
|
126
|
+
}
|
|
127
|
+
|
|
97
128
|
function isHostPermission(value) {
|
|
98
129
|
if (value === '<all_urls>') {
|
|
99
130
|
return true;
|
|
@@ -242,6 +273,8 @@ class PlaywrightPlugin {
|
|
|
242
273
|
configDir: path.join(pluginRootDir, 'config'),
|
|
243
274
|
runDir: path.join(pluginRootDir, 'run'),
|
|
244
275
|
extensionProdversion: '132.0.0.0',
|
|
276
|
+
navigatorPlatform: platformFromUserAgent(DEFAULT_FINGERPRINT_PROFILE.userAgent),
|
|
277
|
+
disableWebRTC: false,
|
|
245
278
|
composeDir: path.join(__dirname, 'playwright-assets'),
|
|
246
279
|
ports: {
|
|
247
280
|
contHeadless: 8931,
|
|
@@ -273,6 +306,8 @@ class PlaywrightPlugin {
|
|
|
273
306
|
asStringArray(this.globalConfig.enabledScenes, [...defaultConfig.enabledScenes])
|
|
274
307
|
);
|
|
275
308
|
merged.containerRuntime = this.resolveContainerRuntime(merged.containerRuntime);
|
|
309
|
+
merged.navigatorPlatform = String(merged.navigatorPlatform || defaultConfig.navigatorPlatform).trim() || defaultConfig.navigatorPlatform;
|
|
310
|
+
merged.disableWebRTC = asBoolean(merged.disableWebRTC, defaultConfig.disableWebRTC);
|
|
276
311
|
|
|
277
312
|
if (merged.enabledScenes.length === 0) {
|
|
278
313
|
throw new Error('playwright.enabledScenes 不能为空');
|
|
@@ -377,6 +412,72 @@ class PlaywrightPlugin {
|
|
|
377
412
|
return !fs.existsSync(this.sceneConfigPath(sceneName));
|
|
378
413
|
}
|
|
379
414
|
|
|
415
|
+
sceneInitScriptPath(sceneName) {
|
|
416
|
+
const configFile = path.basename(this.sceneConfigPath(sceneName), '.json');
|
|
417
|
+
return path.join(this.config.configDir, `${configFile}.init.js`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
legacySceneInitScriptPath(sceneName) {
|
|
421
|
+
return path.join(this.config.configDir, `${sceneName}.init.js`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
buildInitScriptContent() {
|
|
425
|
+
const lines = [
|
|
426
|
+
"'use strict';",
|
|
427
|
+
'(function () {',
|
|
428
|
+
` const platformValue = ${JSON.stringify(this.config.navigatorPlatform)};`,
|
|
429
|
+
' try {',
|
|
430
|
+
' const navProto = Object.getPrototypeOf(navigator);',
|
|
431
|
+
" Object.defineProperty(navProto, 'platform', {",
|
|
432
|
+
' configurable: true,',
|
|
433
|
+
' get: () => platformValue',
|
|
434
|
+
' });',
|
|
435
|
+
' } catch (_) {}'
|
|
436
|
+
];
|
|
437
|
+
|
|
438
|
+
if (this.config.disableWebRTC) {
|
|
439
|
+
lines.push(
|
|
440
|
+
' try {',
|
|
441
|
+
' const scope = globalThis;',
|
|
442
|
+
" const blocked = ['RTCPeerConnection', 'webkitRTCPeerConnection', 'RTCIceCandidate', 'RTCRtpSender', 'RTCRtpReceiver', 'RTCRtpTransceiver', 'RTCDataChannel'];",
|
|
443
|
+
' for (const name of blocked) {',
|
|
444
|
+
" Object.defineProperty(scope, name, { configurable: true, writable: true, value: undefined });",
|
|
445
|
+
' }',
|
|
446
|
+
' if (navigator.mediaDevices) {',
|
|
447
|
+
' const errorFactory = () => {',
|
|
448
|
+
' try {',
|
|
449
|
+
" return new DOMException('WebRTC is disabled', 'NotAllowedError');",
|
|
450
|
+
' } catch (_) {',
|
|
451
|
+
" const error = new Error('WebRTC is disabled');",
|
|
452
|
+
" error.name = 'NotAllowedError';",
|
|
453
|
+
' return error;',
|
|
454
|
+
' }',
|
|
455
|
+
' };',
|
|
456
|
+
" Object.defineProperty(navigator.mediaDevices, 'getUserMedia', {",
|
|
457
|
+
' configurable: true,',
|
|
458
|
+
' writable: true,',
|
|
459
|
+
' value: async () => { throw errorFactory(); }',
|
|
460
|
+
' });',
|
|
461
|
+
' }',
|
|
462
|
+
' } catch (_) {}'
|
|
463
|
+
);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
lines.push('})();', '');
|
|
467
|
+
return lines.join('\n');
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
ensureSceneInitScript(sceneName) {
|
|
471
|
+
const filePath = this.sceneInitScriptPath(sceneName);
|
|
472
|
+
const content = this.buildInitScriptContent();
|
|
473
|
+
fs.writeFileSync(filePath, content, 'utf8');
|
|
474
|
+
const legacyFilePath = this.legacySceneInitScriptPath(sceneName);
|
|
475
|
+
if (legacyFilePath !== filePath) {
|
|
476
|
+
fs.rmSync(legacyFilePath, { force: true });
|
|
477
|
+
}
|
|
478
|
+
return filePath;
|
|
479
|
+
}
|
|
480
|
+
|
|
380
481
|
defaultBrowserName(sceneName) {
|
|
381
482
|
const cfg = this.buildSceneConfig(sceneName);
|
|
382
483
|
const browserName = cfg && cfg.browser && cfg.browser.browserName;
|
|
@@ -553,6 +654,7 @@ class PlaywrightPlugin {
|
|
|
553
654
|
const def = SCENE_DEFS[sceneName];
|
|
554
655
|
const port = this.scenePort(sceneName);
|
|
555
656
|
const extensionPaths = asStringArray(options.extensionPaths, []);
|
|
657
|
+
const initScript = asStringArray(options.initScript, []);
|
|
556
658
|
const baseLaunchArgs = [
|
|
557
659
|
`--user-agent=${DEFAULT_FINGERPRINT_PROFILE.userAgent}`,
|
|
558
660
|
`--lang=${DEFAULT_FINGERPRINT_PROFILE.locale}`,
|
|
@@ -569,6 +671,9 @@ class PlaywrightPlugin {
|
|
|
569
671
|
if (extensionPaths.length > 0) {
|
|
570
672
|
launchOptions.args.push(...this.buildExtensionLaunchArgs(extensionPaths));
|
|
571
673
|
}
|
|
674
|
+
if (this.config.disableWebRTC) {
|
|
675
|
+
launchOptions.args.push(...DISABLE_WEBRTC_LAUNCH_ARGS);
|
|
676
|
+
}
|
|
572
677
|
|
|
573
678
|
return {
|
|
574
679
|
server: {
|
|
@@ -584,6 +689,7 @@ class PlaywrightPlugin {
|
|
|
584
689
|
browser: {
|
|
585
690
|
chromiumSandbox: true,
|
|
586
691
|
browserName: 'chromium',
|
|
692
|
+
initScript,
|
|
587
693
|
launchOptions,
|
|
588
694
|
contextOptions: {
|
|
589
695
|
userAgent: DEFAULT_FINGERPRINT_PROFILE.userAgent,
|
|
@@ -607,7 +713,13 @@ class PlaywrightPlugin {
|
|
|
607
713
|
|
|
608
714
|
ensureSceneConfig(sceneName, options = {}) {
|
|
609
715
|
fs.mkdirSync(this.config.configDir, { recursive: true });
|
|
610
|
-
const
|
|
716
|
+
const initScriptPath = this.ensureSceneInitScript(sceneName);
|
|
717
|
+
const configuredInitScript = asStringArray(options.initScript, []);
|
|
718
|
+
const initScript = configuredInitScript.length > 0 ? configuredInitScript : [initScriptPath];
|
|
719
|
+
const payload = this.buildSceneConfig(sceneName, {
|
|
720
|
+
...options,
|
|
721
|
+
initScript
|
|
722
|
+
});
|
|
611
723
|
const filePath = this.sceneConfigPath(sceneName);
|
|
612
724
|
fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 4)}\n`, 'utf8');
|
|
613
725
|
return filePath;
|
|
@@ -717,21 +829,31 @@ class PlaywrightPlugin {
|
|
|
717
829
|
}
|
|
718
830
|
|
|
719
831
|
const incomingExtensionPaths = asStringArray(options.extensionPaths, []);
|
|
720
|
-
|
|
832
|
+
const hostInitScriptPath = this.sceneInitScriptPath(sceneName);
|
|
833
|
+
const containerInitScriptPath = path.posix.join('/app/config', path.basename(hostInitScriptPath));
|
|
834
|
+
let configOptions = {
|
|
835
|
+
...options,
|
|
836
|
+
extensionPaths: incomingExtensionPaths,
|
|
837
|
+
initScript: [containerInitScriptPath]
|
|
838
|
+
};
|
|
721
839
|
const composeFiles = [this.containerComposePath(sceneName)];
|
|
840
|
+
const volumeMounts = [`${hostInitScriptPath}:${containerInitScriptPath}:ro`];
|
|
722
841
|
|
|
723
842
|
if (incomingExtensionPaths.length > 0) {
|
|
724
843
|
const mapped = this.buildContainerExtensionMounts(incomingExtensionPaths);
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
this.ensureContainerComposeOverride(sceneName, []);
|
|
844
|
+
volumeMounts.push(...mapped.volumeMounts);
|
|
845
|
+
configOptions = {
|
|
846
|
+
...options,
|
|
847
|
+
extensionPaths: mapped.containerPaths,
|
|
848
|
+
initScript: [containerInitScriptPath]
|
|
849
|
+
};
|
|
732
850
|
}
|
|
733
|
-
|
|
734
851
|
const cfgPath = this.ensureSceneConfig(sceneName, configOptions);
|
|
852
|
+
const overridePath = this.ensureContainerComposeOverride(sceneName, volumeMounts);
|
|
853
|
+
if (overridePath) {
|
|
854
|
+
composeFiles.push(overridePath);
|
|
855
|
+
}
|
|
856
|
+
|
|
735
857
|
const env = this.containerEnv(sceneName, cfgPath, { requireVncPassword: true });
|
|
736
858
|
const def = SCENE_DEFS[sceneName];
|
|
737
859
|
|