浏览器自动化新增设备模拟功能:在浏览器命令中添加设备模拟选项

This commit is contained in:
fofolee 2025-01-23 20:06:32 +08:00
parent 179c0c567f
commit f28e53ea53
4 changed files with 416 additions and 10 deletions

View File

@ -0,0 +1,301 @@
const { initCDP, cleanupCDP } = require("./cdp");
const { searchTarget } = require("./tabs");
// 预定义的设备列表
const devices = {
// iOS 设备
"iPhone X": {
userAgent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
viewport: {
width: 375,
height: 812,
deviceScaleFactor: 3,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"iPhone 12 Pro": {
userAgent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
viewport: {
width: 390,
height: 844,
deviceScaleFactor: 3,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"iPhone 14 Pro Max": {
userAgent:
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
viewport: {
width: 430,
height: 932,
deviceScaleFactor: 3,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"iPad Pro": {
userAgent:
"Mozilla/5.0 (iPad; CPU OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
viewport: {
width: 1024,
height: 1366,
deviceScaleFactor: 2,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"iPad Mini": {
userAgent:
"Mozilla/5.0 (iPad; CPU OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1",
viewport: {
width: 768,
height: 1024,
deviceScaleFactor: 2,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
// Android 设备
"Pixel 5": {
userAgent:
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
viewport: {
width: 393,
height: 851,
deviceScaleFactor: 2.75,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"Pixel 7": {
userAgent:
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
viewport: {
width: 412,
height: 915,
deviceScaleFactor: 2.625,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"Samsung Galaxy S20 Ultra": {
userAgent:
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
viewport: {
width: 412,
height: 915,
deviceScaleFactor: 3.5,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"Samsung Galaxy Tab S7": {
userAgent:
"Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
viewport: {
width: 1600,
height: 2560,
deviceScaleFactor: 2,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"Xiaomi 12 Pro": {
userAgent:
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
viewport: {
width: 390,
height: 844,
deviceScaleFactor: 3,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
"HUAWEI Mate30 Pro": {
userAgent:
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36",
viewport: {
width: 392,
height: 835,
deviceScaleFactor: 3,
mobile: true,
hasTouch: true,
isLandscape: false,
},
},
// 桌面设备
Desktop: {
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
viewport: {
width: 1920,
height: 1080,
deviceScaleFactor: 1,
mobile: false,
hasTouch: false,
isLandscape: false,
},
},
"MacBook Pro 16": {
userAgent:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
viewport: {
width: 1728,
height: 1117,
deviceScaleFactor: 2,
mobile: false,
hasTouch: false,
isLandscape: false,
},
},
"4K Display": {
userAgent:
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
viewport: {
width: 3840,
height: 2160,
deviceScaleFactor: 2,
mobile: false,
hasTouch: false,
isLandscape: false,
},
},
};
// 设置设备模拟
const setDevice = async (tab, deviceName) => {
const target = await searchTarget(tab);
const { Network, Emulation } = await initCDP(target.id);
try {
const device = devices[deviceName];
if (!device) {
throw new Error(`未找到设备配置: ${deviceName}`);
}
// 设置 User Agent
await Network.setUserAgentOverride({
userAgent: device.userAgent,
});
// 设置视口
await Emulation.setDeviceMetricsOverride({
...device.viewport,
screenWidth: device.viewport.width,
screenHeight: device.viewport.height,
});
// 设置触摸事件模拟
if (device.viewport.hasTouch) {
await Emulation.setTouchEmulationEnabled({
enabled: true,
maxTouchPoints: 5,
});
}
} finally {
await cleanupCDP(target.id);
}
};
// 自定义设备模拟
const setCustomDevice = async (tab, options) => {
const target = await searchTarget(tab);
const { Network, Emulation } = await initCDP(target.id);
try {
const {
userAgent,
width = 1920,
height = 1080,
deviceScaleFactor = 1,
mobile = false,
hasTouch = false,
isLandscape = false,
} = options;
// 设置 User Agent
if (userAgent) {
await Network.setUserAgentOverride({
userAgent,
});
}
// 设置视口
await Emulation.setDeviceMetricsOverride({
width,
height,
deviceScaleFactor,
mobile,
isLandscape,
screenWidth: width,
screenHeight: height,
});
// 设置触摸事件模拟
if (hasTouch) {
await Emulation.setTouchEmulationEnabled({
enabled: true,
maxTouchPoints: 5,
});
}
} finally {
await cleanupCDP(target.id);
}
};
// 清除设备模拟
const clearDeviceEmulation = async (tab) => {
const target = await searchTarget(tab);
const { Network, Emulation } = await initCDP(target.id);
try {
// 先禁用触摸事件模拟
await Emulation.setTouchEmulationEnabled({
enabled: false,
});
// 清除 User Agent 覆盖
await Network.setUserAgentOverride({
userAgent: "",
});
// 重置设备指标
await Emulation.setDeviceMetricsOverride({
width: 0,
height: 0,
deviceScaleFactor: 0,
mobile: false,
screenWidth: 0,
screenHeight: 0,
});
// 清除设备指标覆盖
await Emulation.clearDeviceMetricsOverride();
} catch (error) {
console.error("清除设备模拟失败:", error);
} finally {
await cleanupCDP(target.id);
}
};
module.exports = {
setDevice,
setCustomDevice,
clearDeviceEmulation,
};

View File

@ -5,6 +5,7 @@ const tabs = require("./tabs");
const url = require("./url"); const url = require("./url");
const cookie = require("./cookie"); const cookie = require("./cookie");
const screenshot = require("./screenshot"); const screenshot = require("./screenshot");
const device = require("./device");
module.exports = { module.exports = {
...url, ...url,
@ -14,4 +15,5 @@ module.exports = {
...browserManager, ...browserManager,
...cookie, ...cookie,
...screenshot, ...screenshot,
...device,
}; };

View File

@ -1,4 +1,5 @@
import { newVarInputVal } from "js/composer/varInputValManager"; import { newVarInputVal } from "js/composer/varInputValManager";
import { deviceName, userAgent } from "js/options/httpOptions";
const tabConfig = { const tabConfig = {
component: "OptionEditor", component: "OptionEditor",
@ -6,7 +7,6 @@ const tabConfig = {
options: { options: {
by: { by: {
component: "QSelect", component: "QSelect",
label: "标签",
width: 3, width: 3,
options: [ options: [
{ label: "当前标签页", value: "active" }, { label: "当前标签页", value: "active" },
@ -19,7 +19,7 @@ const tabConfig = {
component: "VariableInput", component: "VariableInput",
icon: "tab", icon: "tab",
width: 9, width: 9,
placeholder: "当前标签页留空,其他支持模糊匹配", placeholder: "选择当前标签页留空URL/标题/ID支持模糊匹配",
}, },
}, },
defaultValue: { defaultValue: {
@ -625,5 +625,101 @@ export const browserCommands = {
}, },
], ],
}, },
{
value: "quickcomposer.browser.setDevice",
label: "设备模拟",
icon: "devices",
isAsync: true,
config: [tabConfig],
subCommands: [
{
value: "quickcomposer.browser.setDevice",
label: "使用预设设备",
icon: "smartphone",
config: [
{
label: "设备",
component: "QSelect",
icon: "devices",
width: 12,
options: [
...deviceName,
// 桌面设备
{ label: "Desktop", value: "Desktop" },
{ label: "MacBook Pro 16", value: "MacBook Pro 16" },
{ label: "4K Display", value: "4K Display" },
],
},
],
},
{
value: "quickcomposer.browser.setCustomDevice",
label: "自定义设备",
icon: "build",
config: [
{
label: "设备配置",
component: "OptionEditor",
icon: "settings",
width: 12,
options: {
width: {
label: "宽度",
component: "NumberInput",
width: 3,
defaultValue: 1920,
min: 0,
},
height: {
label: "高度",
component: "NumberInput",
width: 3,
defaultValue: 1080,
min: 0,
},
deviceScaleFactor: {
label: "设备像素比",
component: "NumberInput",
width: 3,
defaultValue: 1,
min: 1,
step: 0.1,
},
mobile: {
label: "移动设备",
component: "CheckButton",
width: 3,
},
hasTouch: {
label: "触摸屏",
component: "CheckButton",
width: 3,
},
isLandscape: {
label: "横屏",
component: "CheckButton",
width: 3,
},
userAgent: {
label: "User Agent",
component: "VariableInput",
icon: "code",
width: 6,
placeholder: "留空使用默认",
options: {
items: userAgent,
},
},
},
},
],
},
{
value: "quickcomposer.browser.clearDeviceEmulation",
label: "清除设备模拟",
icon: "clear",
},
],
},
], ],
}; };

View File

@ -67,15 +67,22 @@ export const commonHeaders = [
]; ];
export const deviceName = [ export const deviceName = [
{ label: "iPhone 11", value: "iPhone 11" },
{ label: "iPhone X", value: "iPhone X" }, { label: "iPhone X", value: "iPhone X" },
{ label: "iPad", value: "iPad" }, { label: "iPhone 12 Pro", value: "iPhone 12 Pro" },
{ label: "iPhone 6/7/8 Plus", value: "iPhone 6/7/8 Plus" }, { label: "iPhone 14 Pro Max", value: "iPhone 14 Pro Max" },
{ label: "iPhone 6/7/8", value: "iPhone 6/7/8" }, { label: "iPad Pro", value: "iPad Pro" },
{ label: "iPhone 5/SE", value: "iPhone 5/SE" }, { label: "iPad Mini", value: "iPad Mini" },
{ label: "HUAWEI Mate10", value: "HUAWEI Mate10" }, { label: "Pixel 5", value: "Pixel 5" },
{ label: "HUAWEI Mate20", value: "HUAWEI Mate20" }, { label: "Pixel 7", value: "Pixel 7" },
{ label: "HUAWEI Mate30", value: "HUAWEI Mate30" }, {
label: "Samsung Galaxy S20 Ultra",
value: "Samsung Galaxy S20 Ultra",
},
{
label: "Samsung Galaxy Tab S7",
value: "Samsung Galaxy Tab S7",
},
{ label: "Xiaomi 12 Pro", value: "Xiaomi 12 Pro" },
{ label: "HUAWEI Mate30 Pro", value: "HUAWEI Mate30 Pro" }, { label: "HUAWEI Mate30 Pro", value: "HUAWEI Mate30 Pro" },
]; ];