@skrillex1224/android-toolkit 1.0.8 → 1.0.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/dist/browser.js +1 -1
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +77 -74
- package/dist/index.cjs.map +3 -3
- package/dist/index.js +77 -74
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/browser.js
CHANGED
|
@@ -25,7 +25,7 @@ var Code = Object.freeze({
|
|
|
25
25
|
ContentUnavailable: 30010003,
|
|
26
26
|
SourceExtractionFailed: 30010004,
|
|
27
27
|
AutomationFailed: 30010005,
|
|
28
|
-
|
|
28
|
+
AppRuntimeUnavailable: 30010008,
|
|
29
29
|
AppNotInstalled: 30010009
|
|
30
30
|
});
|
|
31
31
|
var Status = Object.freeze({
|
package/dist/browser.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/constants.js", "../src/errors.js", "../entrys/browser.js"],
|
|
4
|
-
"sourcesContent": ["export const Code = Object.freeze({\n Success: 0,\n UnknownError: -1,\n NotLogin: 30000001,\n Timeout: 30000003,\n InitialTimeout: 30000004,\n OverallTimeout: 30000005,\n InvalidRequest: 30010001,\n AdbUnavailable: 30010002,\n ContentUnavailable: 30010003,\n SourceExtractionFailed: 30010004,\n AutomationFailed: 30010005,\n
|
|
4
|
+
"sourcesContent": ["export const Code = Object.freeze({\n Success: 0,\n UnknownError: -1,\n NotLogin: 30000001,\n Timeout: 30000003,\n InitialTimeout: 30000004,\n OverallTimeout: 30000005,\n InvalidRequest: 30010001,\n AdbUnavailable: 30010002,\n ContentUnavailable: 30010003,\n SourceExtractionFailed: 30010004,\n AutomationFailed: 30010005,\n AppRuntimeUnavailable: 30010008,\n AppNotInstalled: 30010009,\n});\n\nexport const Status = Object.freeze({\n Success: 'SUCCESS',\n Failed: 'FAILED',\n});\n\nexport const UnicodeIme = Object.freeze({\n packageName: 'io.appium.settings',\n component: 'io.appium.settings/.UnicodeIME',\n inputAction: 'ADB_INPUT_TEXT',\n});\n\nexport const AppiumSettings = Object.freeze({\n packageName: 'io.appium.settings',\n clipboardReceiver: 'io.appium.settings/.receivers.ClipboardReceiver',\n clipboardGetAction: 'io.appium.settings.clipboard.get',\n});\n\nconst normalizeShare = (share) => {\n const source = share && typeof share === 'object' ? share : {};\n return {\n mode: 'clipboard',\n prefix: String(source.prefix || '').trim(),\n xurl: Array.isArray(source.xurl) ? source.xurl : [],\n };\n};\n\nconst createActorInfo = (info) => {\n const key = String(info.key || '').trim();\n return Object.freeze({\n key,\n name: String(info.name || key),\n icon: String(info.icon || `https://static.heartbitai.com/general/actors/${key}.png`),\n share: Object.freeze(normalizeShare(info.share)),\n });\n};\n\nexport const ActorInfo = Object.freeze({\n 'doubao.android': createActorInfo({\n key: 'doubao.android',\n name: '\u8C46\u5305 Android',\n share: {\n prefix: 'https://www.doubao.com/thread/',\n xurl: [],\n },\n }),\n});\n", "import { Code } from './constants.js';\n\nexport class CrawlerError extends Error {\n constructor(input = {}, options = {}) {\n const payload = typeof input === 'string' ? { message: input } : input;\n super(payload.message || 'Android crawler error', options);\n this.name = 'CrawlerError';\n this.code = payload.code || Code.UnknownError;\n this.context = payload.context || {};\n }\n\n static isCrawlerError(error) {\n return error instanceof CrawlerError || error?.name === 'CrawlerError';\n }\n\n static from(error, fallback = {}) {\n if (CrawlerError.isCrawlerError(error)) return error;\n return new CrawlerError({\n message: error?.message || String(error),\n code: fallback.code || Code.UnknownError,\n context: fallback.context || {},\n });\n }\n}\n\nexport function serializeError(error) {\n if (!error) return { message: '' };\n return {\n name: error.name || 'Error',\n message: error.message || String(error),\n stack: error.stack || '',\n code: error.code || '',\n context: error.context || undefined,\n };\n}\n", "import * as Constants from '../src/constants.js';\nimport * as Errors from '../src/errors.js';\n\nconst ToolkitMode = Object.freeze({\n default: 'default',\n});\n\nexport const useAndroidToolKit = () => {\n return {\n Constants,\n Errors,\n };\n};\n\nuseAndroidToolKit.Mode = ToolkitMode;\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -57,7 +57,7 @@ var Code = Object.freeze({
|
|
|
57
57
|
ContentUnavailable: 30010003,
|
|
58
58
|
SourceExtractionFailed: 30010004,
|
|
59
59
|
AutomationFailed: 30010005,
|
|
60
|
-
|
|
60
|
+
AppRuntimeUnavailable: 30010008,
|
|
61
61
|
AppNotInstalled: 30010009
|
|
62
62
|
});
|
|
63
63
|
var Status = Object.freeze({
|
|
@@ -282,7 +282,7 @@ function safeJson(value) {
|
|
|
282
282
|
|
|
283
283
|
// src/device.js
|
|
284
284
|
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
285
|
-
var
|
|
285
|
+
var Device2 = {
|
|
286
286
|
adbExec,
|
|
287
287
|
adbShell,
|
|
288
288
|
forceStopApp,
|
|
@@ -663,7 +663,7 @@ function createApifyKit(options = {}) {
|
|
|
663
663
|
if (failActor) {
|
|
664
664
|
let base64 = "";
|
|
665
665
|
try {
|
|
666
|
-
base64 = await
|
|
666
|
+
base64 = await Device2.screenshotBase64(ctx);
|
|
667
667
|
} catch (error) {
|
|
668
668
|
base64 = `\u622A\u56FE\u5931\u8D25: ${error?.message || String(error)}`;
|
|
669
669
|
}
|
|
@@ -781,7 +781,7 @@ var DeviceView = {
|
|
|
781
781
|
normalizeSelector
|
|
782
782
|
};
|
|
783
783
|
async function snapshot(ctx, options = {}) {
|
|
784
|
-
const xml = options.xml || await
|
|
784
|
+
const xml = options.xml || await Device2.dumpUiXml(ctx);
|
|
785
785
|
const parsed = parser.parse(xml);
|
|
786
786
|
const roots = toArray(parsed?.hierarchy?.node).map((node) => normalizeNode(node, null, 0));
|
|
787
787
|
const flat = [];
|
|
@@ -965,6 +965,12 @@ function selectorLabel(selector) {
|
|
|
965
965
|
}
|
|
966
966
|
|
|
967
967
|
// src/device-input.js
|
|
968
|
+
var SCROLL_MAX_SWIPES = 24;
|
|
969
|
+
var SCROLL_STABLE_ROUNDS = 1;
|
|
970
|
+
var SCROLL_SWIPE_RATIO = 0.72;
|
|
971
|
+
var SCROLL_DURATION_MS = 360;
|
|
972
|
+
var SCROLL_SETTLE_MS = 500;
|
|
973
|
+
var SCROLL_HASH_SOURCE = "screenshot";
|
|
968
974
|
var DeviceInput = {
|
|
969
975
|
click,
|
|
970
976
|
tap: click,
|
|
@@ -978,12 +984,12 @@ var DeviceInput = {
|
|
|
978
984
|
};
|
|
979
985
|
async function click(ctx, selectorOrPoint, options = {}) {
|
|
980
986
|
if (isPoint(selectorOrPoint)) {
|
|
981
|
-
await
|
|
987
|
+
await Device2.tapAbsolute(ctx, selectorOrPoint.x, selectorOrPoint.y);
|
|
982
988
|
return { point: selectorOrPoint };
|
|
983
989
|
}
|
|
984
990
|
if (isBounds(selectorOrPoint)) {
|
|
985
991
|
const point2 = centerOf(selectorOrPoint);
|
|
986
|
-
await
|
|
992
|
+
await Device2.tapAbsolute(ctx, point2.x, point2.y);
|
|
987
993
|
return { point: point2 };
|
|
988
994
|
}
|
|
989
995
|
const target = await DeviceView.find(ctx, selectorOrPoint, options);
|
|
@@ -1002,7 +1008,7 @@ async function click(ctx, selectorOrPoint, options = {}) {
|
|
|
1002
1008
|
actual: simplifyNode(actual),
|
|
1003
1009
|
point
|
|
1004
1010
|
});
|
|
1005
|
-
await
|
|
1011
|
+
await Device2.tapAbsolute(ctx, point.x, point.y);
|
|
1006
1012
|
await sleep(Number(options.settleMs || 250));
|
|
1007
1013
|
return { target, actual, point };
|
|
1008
1014
|
}
|
|
@@ -1013,7 +1019,7 @@ async function fill(ctx, selector, text2, options = {}) {
|
|
|
1013
1019
|
settleMs: Number(options.focusSettleMs || 350)
|
|
1014
1020
|
});
|
|
1015
1021
|
try {
|
|
1016
|
-
await
|
|
1022
|
+
await Device2.typeText(ctx, value);
|
|
1017
1023
|
} catch (error) {
|
|
1018
1024
|
throw new CrawlerError({
|
|
1019
1025
|
message: `automation_failed: View \u5199\u5165\u6587\u672C\u5931\u8D25 ${JSON.stringify(selector)}`,
|
|
@@ -1030,10 +1036,10 @@ async function fill(ctx, selector, text2, options = {}) {
|
|
|
1030
1036
|
return { target: clicked.actual || clicked.target, chars: value.length };
|
|
1031
1037
|
}
|
|
1032
1038
|
async function press(ctx, key) {
|
|
1033
|
-
await
|
|
1039
|
+
await Device2.pressKey(ctx, key);
|
|
1034
1040
|
}
|
|
1035
1041
|
async function pressEnter2(ctx) {
|
|
1036
|
-
await
|
|
1042
|
+
await Device2.pressEnter(ctx);
|
|
1037
1043
|
}
|
|
1038
1044
|
async function scroll(ctx, selector, direction, options = {}) {
|
|
1039
1045
|
const node = await DeviceView.find(ctx, selector, options);
|
|
@@ -1041,8 +1047,8 @@ async function scroll(ctx, selector, direction, options = {}) {
|
|
|
1041
1047
|
}
|
|
1042
1048
|
async function scrollBounds(ctx, bounds2, direction, options = {}) {
|
|
1043
1049
|
const box = bounds2;
|
|
1044
|
-
const ratio = Number(options.swipeRatio ||
|
|
1045
|
-
const durationMs = Number(options.durationMs ||
|
|
1050
|
+
const ratio = Number(options.swipeRatio || SCROLL_SWIPE_RATIO);
|
|
1051
|
+
const durationMs = Number(options.durationMs || SCROLL_DURATION_MS);
|
|
1046
1052
|
const x = box.centerX;
|
|
1047
1053
|
const distance = Math.max(40, box.height * ratio);
|
|
1048
1054
|
const down = String(direction || "").toLowerCase() === "down";
|
|
@@ -1054,8 +1060,8 @@ async function scrollBounds(ctx, bounds2, direction, options = {}) {
|
|
|
1054
1060
|
x,
|
|
1055
1061
|
y: down ? Math.min(box.bottom - 10, from.y + distance) : Math.max(box.top + 10, from.y - distance)
|
|
1056
1062
|
};
|
|
1057
|
-
await
|
|
1058
|
-
await sleep(Number(options.settleMs ||
|
|
1063
|
+
await Device2.swipe(ctx, from, to, durationMs);
|
|
1064
|
+
await sleep(Number(options.settleMs || SCROLL_SETTLE_MS));
|
|
1059
1065
|
}
|
|
1060
1066
|
async function scrollToEnd(ctx, selector, options = {}) {
|
|
1061
1067
|
return scrollUntilStable(ctx, selector, "up", options);
|
|
@@ -1064,56 +1070,30 @@ async function scrollToTop(ctx, selector, options = {}) {
|
|
|
1064
1070
|
return scrollUntilStable(ctx, selector, "down", options);
|
|
1065
1071
|
}
|
|
1066
1072
|
async function scrollUntilStable(ctx, selector, direction, options = {}) {
|
|
1067
|
-
const maxSwipes = Math.max(1, Number(options.maxSwipes ||
|
|
1068
|
-
const stableRoundsTarget = Math.max(1, Number(options.stableRounds ||
|
|
1069
|
-
const hashSource = String(options.stability || options.hashSource || "view");
|
|
1070
|
-
if (hashSource === "screenshot") {
|
|
1071
|
-
return scrollUntilScreenshotStable(ctx, selector, direction, options, maxSwipes, stableRoundsTarget, hashSource);
|
|
1072
|
-
}
|
|
1073
|
-
let lastHash = "";
|
|
1074
|
-
let stableRounds = 0;
|
|
1075
|
-
for (let index = 0; index < maxSwipes; index += 1) {
|
|
1076
|
-
const currentHash = await scrollStateHash(ctx, selector, options, hashSource);
|
|
1077
|
-
if (currentHash === lastHash) {
|
|
1078
|
-
stableRounds += 1;
|
|
1079
|
-
if (stableRounds >= stableRoundsTarget) {
|
|
1080
|
-
return { swipes: index, stableRounds, hash: currentHash, hashSource, capped: false };
|
|
1081
|
-
}
|
|
1082
|
-
} else {
|
|
1083
|
-
stableRounds = 0;
|
|
1084
|
-
lastHash = currentHash;
|
|
1085
|
-
}
|
|
1086
|
-
await scroll(ctx, selector, direction, options);
|
|
1087
|
-
}
|
|
1088
|
-
return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource, capped: true };
|
|
1089
|
-
}
|
|
1090
|
-
async function scrollUntilScreenshotStable(ctx, selector, direction, options, maxSwipes, stableRoundsTarget, hashSource) {
|
|
1073
|
+
const maxSwipes = Math.max(1, Number(options.maxSwipes || SCROLL_MAX_SWIPES));
|
|
1074
|
+
const stableRoundsTarget = Math.max(1, Number(options.stableRounds || SCROLL_STABLE_ROUNDS));
|
|
1091
1075
|
const node = await DeviceView.find(ctx, selector, options);
|
|
1092
1076
|
const bounds2 = node.bounds;
|
|
1093
|
-
let lastHash = await scrollStateHash(ctx
|
|
1077
|
+
let lastHash = await scrollStateHash(ctx);
|
|
1094
1078
|
let stableRounds = 0;
|
|
1095
1079
|
for (let index = 0; index < maxSwipes; index += 1) {
|
|
1096
1080
|
await scrollBounds(ctx, bounds2, direction, options);
|
|
1097
|
-
const currentHash = await scrollStateHash(ctx
|
|
1081
|
+
const currentHash = await scrollStateHash(ctx);
|
|
1098
1082
|
if (currentHash === lastHash) {
|
|
1099
1083
|
stableRounds += 1;
|
|
1100
1084
|
if (stableRounds >= stableRoundsTarget) {
|
|
1101
|
-
return { swipes: index + 1, stableRounds, hash: currentHash, hashSource, capped: false };
|
|
1085
|
+
return { swipes: index + 1, stableRounds, hash: currentHash, hashSource: SCROLL_HASH_SOURCE, capped: false };
|
|
1102
1086
|
}
|
|
1103
1087
|
} else {
|
|
1104
1088
|
stableRounds = 0;
|
|
1105
1089
|
lastHash = currentHash;
|
|
1106
1090
|
}
|
|
1107
1091
|
}
|
|
1108
|
-
return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource, capped: true };
|
|
1092
|
+
return { swipes: maxSwipes, stableRounds, hash: lastHash, hashSource: SCROLL_HASH_SOURCE, capped: true };
|
|
1109
1093
|
}
|
|
1110
|
-
async function scrollStateHash(ctx
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
return (0, import_node_crypto2.createHash)("sha256").update(png).digest("hex");
|
|
1114
|
-
}
|
|
1115
|
-
const node = await DeviceView.find(ctx, selector, options);
|
|
1116
|
-
return DeviceView.hashNode(node);
|
|
1094
|
+
async function scrollStateHash(ctx) {
|
|
1095
|
+
const png = await Device2.screenshotPng(ctx);
|
|
1096
|
+
return (0, import_node_crypto2.createHash)("sha256").update(png).digest("hex");
|
|
1117
1097
|
}
|
|
1118
1098
|
function nearestClickable(node) {
|
|
1119
1099
|
let current = node;
|
|
@@ -1190,8 +1170,8 @@ async function query(ctx, options = {}) {
|
|
|
1190
1170
|
const dbPaths = await resolveDeviceDbPaths(ctx, config);
|
|
1191
1171
|
if (dbPaths.length === 0) {
|
|
1192
1172
|
throw new CrawlerError({
|
|
1193
|
-
message: "
|
|
1194
|
-
code: Code.
|
|
1173
|
+
message: "app_runtime_unavailable: sqlite database not found",
|
|
1174
|
+
code: Code.AppRuntimeUnavailable,
|
|
1195
1175
|
context: { dbDir: config.dbDir, dbPath: config.dbPath, dbNamePrefix: config.dbNamePrefix }
|
|
1196
1176
|
});
|
|
1197
1177
|
}
|
|
@@ -1240,8 +1220,8 @@ async function resolveDeviceDbPaths(ctx, config) {
|
|
|
1240
1220
|
maxBuffer: 2 * 1024 * 1024
|
|
1241
1221
|
}).catch((error) => {
|
|
1242
1222
|
throw new CrawlerError({
|
|
1243
|
-
message: `
|
|
1244
|
-
code: Code.
|
|
1223
|
+
message: `app_runtime_unavailable: sqlite dbDir unavailable ${error?.message || String(error)}`,
|
|
1224
|
+
code: Code.AppRuntimeUnavailable,
|
|
1245
1225
|
context: { dbDir: config.dbDir }
|
|
1246
1226
|
});
|
|
1247
1227
|
});
|
|
@@ -1266,8 +1246,8 @@ async function pullDatabaseSnapshot(ctx, dbPath, localDir) {
|
|
|
1266
1246
|
await adbSuShell(ctx, copyCommand, { timeoutMs: 3e4, maxBuffer: 4 * 1024 * 1024 });
|
|
1267
1247
|
await adbPull(adbPath, serial, remoteDb, localDb).catch((error) => {
|
|
1268
1248
|
throw new CrawlerError({
|
|
1269
|
-
message: `
|
|
1270
|
-
code: Code.
|
|
1249
|
+
message: `app_runtime_unavailable: sqlite snapshot pull failed ${error?.message || String(error)}`,
|
|
1250
|
+
code: Code.AppRuntimeUnavailable,
|
|
1271
1251
|
context: { dbPath }
|
|
1272
1252
|
});
|
|
1273
1253
|
});
|
|
@@ -1280,7 +1260,7 @@ async function pullDatabaseSnapshot(ctx, dbPath, localDir) {
|
|
|
1280
1260
|
}
|
|
1281
1261
|
}
|
|
1282
1262
|
async function adbSuShell(ctx, command, options = {}) {
|
|
1283
|
-
return
|
|
1263
|
+
return Device2.adbShell(ctx, [`su -c ${shellQuote(command)}`], options);
|
|
1284
1264
|
}
|
|
1285
1265
|
async function queryLocalSQLite(dbPath, config) {
|
|
1286
1266
|
const payloadPath = `${dbPath}.query.json`;
|
|
@@ -1717,6 +1697,11 @@ var DEFAULT_COLUMNS = 3;
|
|
|
1717
1697
|
var DEFAULT_SETTLE_MS = 550;
|
|
1718
1698
|
var DEFAULT_TIMEOUT_MS = 5e4;
|
|
1719
1699
|
var DEFAULT_POLL_INTERVAL_MS = 800;
|
|
1700
|
+
var RESET_TO_TOP_MAX_SWIPES = 16;
|
|
1701
|
+
var RESET_TO_TOP_STABLE_ROUNDS = 1;
|
|
1702
|
+
var CAPTURE_SCROLL_STEP_RATIO = 0.56;
|
|
1703
|
+
var CAPTURE_SCROLL_DURATION_MS = 360;
|
|
1704
|
+
var SPRITE_GAP = 16;
|
|
1720
1705
|
var Share = {
|
|
1721
1706
|
captureScreen,
|
|
1722
1707
|
captureLink
|
|
@@ -1726,18 +1711,34 @@ async function captureScreen(ctx, options = {}) {
|
|
|
1726
1711
|
const frameBuffers = [];
|
|
1727
1712
|
const seen = /* @__PURE__ */ new Set();
|
|
1728
1713
|
const targetSelector = options.selector || { id: "message_list" };
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1714
|
+
const resetToTop = options.resetToTop !== false;
|
|
1715
|
+
let topResult = null;
|
|
1716
|
+
if (resetToTop) {
|
|
1717
|
+
topResult = await DeviceInput.scrollToTop(ctx, targetSelector, {
|
|
1718
|
+
maxSwipes: RESET_TO_TOP_MAX_SWIPES,
|
|
1719
|
+
stableRounds: RESET_TO_TOP_STABLE_ROUNDS,
|
|
1720
|
+
settleMs: 400
|
|
1721
|
+
});
|
|
1722
|
+
Logger.info("captureScreen scrollToTop", {
|
|
1723
|
+
swipes: topResult?.swipes,
|
|
1724
|
+
stableRounds: topResult?.stableRounds,
|
|
1725
|
+
hashSource: topResult?.hashSource,
|
|
1726
|
+
capped: topResult?.capped === true
|
|
1740
1727
|
});
|
|
1728
|
+
if (topResult?.capped === true) {
|
|
1729
|
+
throw new CrawlerError({
|
|
1730
|
+
message: `source_extraction_failed: \u622A\u56FE\u524D\u672A\u80FD\u786E\u8BA4\u56DE\u5230\u9876\u90E8 swipes=${topResult.swipes} stableRounds=${topResult.stableRounds}`,
|
|
1731
|
+
code: Code.SourceExtractionFailed,
|
|
1732
|
+
context: {
|
|
1733
|
+
selector: targetSelector,
|
|
1734
|
+
swipes: topResult.swipes,
|
|
1735
|
+
stableRounds: topResult.stableRounds,
|
|
1736
|
+
hashSource: topResult.hashSource
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
for (let index = 0; index < DEFAULT_CAPTURE_COUNT; index += 1) {
|
|
1741
1742
|
await sleep(DEFAULT_SETTLE_MS);
|
|
1742
1743
|
const png = await Device.screenshotPng(ctx);
|
|
1743
1744
|
const hash = (0, import_node_crypto3.createHash)("sha256").update(png).digest("hex");
|
|
@@ -1746,18 +1747,21 @@ async function captureScreen(ctx, options = {}) {
|
|
|
1746
1747
|
frameBuffers.push(png);
|
|
1747
1748
|
if (index >= DEFAULT_CAPTURE_COUNT - 1) break;
|
|
1748
1749
|
await DeviceInput.scroll(ctx, targetSelector, "up", {
|
|
1749
|
-
swipeRatio:
|
|
1750
|
-
durationMs:
|
|
1750
|
+
swipeRatio: CAPTURE_SCROLL_STEP_RATIO,
|
|
1751
|
+
durationMs: CAPTURE_SCROLL_DURATION_MS,
|
|
1751
1752
|
settleMs: DEFAULT_SETTLE_MS
|
|
1752
1753
|
}).catch(() => {
|
|
1753
1754
|
});
|
|
1754
1755
|
}
|
|
1755
|
-
const sprite = await composeSprite(frameBuffers, { columns: DEFAULT_COLUMNS, gap:
|
|
1756
|
+
const sprite = await composeSprite(frameBuffers, { columns: DEFAULT_COLUMNS, gap: SPRITE_GAP });
|
|
1756
1757
|
const compression = resolveImageCompression(options);
|
|
1757
1758
|
const base64 = await compressImageBufferToBase64(sprite, compression);
|
|
1758
1759
|
Logger.success("Share.captureScreen", {
|
|
1759
1760
|
duration: Logger.duration(startedAt),
|
|
1760
1761
|
frameCount: frameBuffers.length,
|
|
1762
|
+
scrollStepRatio: CAPTURE_SCROLL_STEP_RATIO,
|
|
1763
|
+
resetToTop,
|
|
1764
|
+
topCapped: topResult?.capped === true,
|
|
1761
1765
|
base64Bytes: Math.ceil(base64.length)
|
|
1762
1766
|
});
|
|
1763
1767
|
return `data:image/jpeg;base64,${base64}`;
|
|
@@ -1772,7 +1776,6 @@ async function captureLink(ctx, options = {}) {
|
|
|
1772
1776
|
});
|
|
1773
1777
|
}
|
|
1774
1778
|
const timeoutMs = Number(options.timeoutMs || DEFAULT_TIMEOUT_MS);
|
|
1775
|
-
const pollIntervalMs = Number(options.pollIntervalMs || DEFAULT_POLL_INTERVAL_MS);
|
|
1776
1779
|
const deadline = Date.now() + timeoutMs;
|
|
1777
1780
|
const prefix = String(share.prefix || "").trim();
|
|
1778
1781
|
Logger.start("Share.captureLink", { actor: actorInfo.key, prefix, timeoutMs });
|
|
@@ -1794,7 +1797,7 @@ async function captureLink(ctx, options = {}) {
|
|
|
1794
1797
|
if (link && link === beforeLink) {
|
|
1795
1798
|
lastEvent = { ...event, rejected: "same_as_baseline", beforeLink };
|
|
1796
1799
|
}
|
|
1797
|
-
await sleep(
|
|
1800
|
+
await sleep(DEFAULT_POLL_INTERVAL_MS);
|
|
1798
1801
|
}
|
|
1799
1802
|
throw new CrawlerError({
|
|
1800
1803
|
message: "source_extraction_failed: \u672A\u6355\u83B7\u5206\u4EAB\u94FE\u63A5",
|
|
@@ -1890,7 +1893,7 @@ async function run(handler, options = {}) {
|
|
|
1890
1893
|
const kit = {
|
|
1891
1894
|
Launch,
|
|
1892
1895
|
ApifyKit: apifyKit,
|
|
1893
|
-
Device,
|
|
1896
|
+
Device: Device2,
|
|
1894
1897
|
DeviceInput,
|
|
1895
1898
|
DeviceView,
|
|
1896
1899
|
DeviceSQLite,
|
|
@@ -1941,7 +1944,7 @@ var useAndroidToolKit = () => {
|
|
|
1941
1944
|
DeviceInput,
|
|
1942
1945
|
DeviceView,
|
|
1943
1946
|
DeviceSQLite,
|
|
1944
|
-
Device,
|
|
1947
|
+
Device: Device2,
|
|
1945
1948
|
Mutation,
|
|
1946
1949
|
Share,
|
|
1947
1950
|
Constants: constants_exports,
|