fix(harvest): four verified stages — pill wait, menu wait, jiggle-hover flyout, download-event confirmation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2dfa752df3
commit
37a2ae8cb2
@ -29,10 +29,13 @@ async function ensure(tabId) {
|
||||
const ENTER = { key: 'Enter', code: 'Enter', windowsVirtualKeyCode: 13, nativeVirtualKeyCode: 13 };
|
||||
const ESC = { key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27, nativeVirtualKeyCode: 27 };
|
||||
|
||||
// While a harvest pass runs, route every browser download into flowrinse/harvest/.
|
||||
// While a harvest pass runs, route every browser download into flowrinse/harvest/
|
||||
// and count them — the content script uses the count to VERIFY each 1K click fired.
|
||||
let harvestRouting = false;
|
||||
let harvestCount = 0;
|
||||
chrome.downloads.onDeterminingFilename.addListener((item, suggest) => {
|
||||
if (!harvestRouting) return;
|
||||
harvestCount++;
|
||||
suggest({ filename: `flowrinse/harvest/${(item.filename || 'asset').split(/[\\/]/).pop()}`, conflictAction: 'uniquify' });
|
||||
});
|
||||
|
||||
@ -75,6 +78,8 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||
} else if (msg.type === 'harvestRouting') {
|
||||
harvestRouting = !!msg.on;
|
||||
sendResponse({ ok: true });
|
||||
} else if (msg.type === 'harvestStats') {
|
||||
sendResponse({ ok: true, count: harvestCount });
|
||||
} else if (msg.type === 'hardReload') {
|
||||
// Cache-bypass reload — the by-hand fix for a wedged Flow session.
|
||||
if (attached.has(tabId)) { chrome.debugger.detach({ tabId }, () => {}); attached.delete(tabId); }
|
||||
|
||||
59
content.js
59
content.js
@ -569,27 +569,50 @@ async function executeHarvest(task) {
|
||||
}
|
||||
idle = 0;
|
||||
const href = tile.href.split('#')[0];
|
||||
tile.scrollIntoView({ block: 'center' }); await sleep(600);
|
||||
await bg({ type: 'hoverAt', ...center(tile) }); await sleep(600);
|
||||
const dots = findTileMenuButton(tile);
|
||||
if (!dots) { missed++; seen.add(href); log('info', `harvest: no ⋮ on a tile — ${gridCensus()}`); continue; }
|
||||
await bg({ type: 'clickAt', ...center(dots) }); await sleep(800);
|
||||
const dl = findMenuItem(/^\s*download\b/i);
|
||||
if (!dl) { missed++; seen.add(href); log('info', `harvest: menu open but no Download item — ${gridCensus()}`); await bg({ type: 'pressEscape' }); await sleep(400); continue; }
|
||||
// "Download" is a submenu parent: HOVER unfurls the nested 1K/2K/4K flyout
|
||||
// (clicking it closes the menu — the bug the first pass hit).
|
||||
await bg({ type: 'hoverAt', ...center(dl) }); await sleep(800);
|
||||
let oneK = findMenuItem(/\b1k\b|original/i);
|
||||
if (!oneK) { // some builds expand on click instead — try once
|
||||
await bg({ type: 'clickAt', ...center(dl) }); await sleep(800);
|
||||
oneK = findMenuItem(/\b1k\b|original/i);
|
||||
tile.scrollIntoView({ block: 'center' }); await sleep(700);
|
||||
|
||||
// Stage 1/4: hover the tile until the ♡ ↻ ⋮ pill renders
|
||||
await bg({ type: 'hoverAt', ...center(tile) });
|
||||
const dots = await waitFor(() => findTileMenuButton(tile), 3000, 300);
|
||||
if (!dots) { missed++; seen.add(href); log('info', `harvest: control pill never appeared — ${gridCensus()}`); continue; }
|
||||
|
||||
// Stage 2/4: click ⋮ ("More"), wait for the menu with Download in it
|
||||
await bg({ type: 'clickAt', ...center(dots) });
|
||||
const dl = await waitFor(() => findMenuItem(/^\s*download\b/i), 3000, 300);
|
||||
if (!dl) { missed++; seen.add(href); log('info', `harvest: ⋮ clicked but no Download item — ${gridCensus()}`); await bg({ type: 'pressEscape' }); await sleep(400); continue; }
|
||||
|
||||
// Stage 3/4: HOVER Download — the 1K/2K/4K flyout unfurls beside it (which side
|
||||
// depends on where the tile sits on screen; irrelevant, coords are re-queried
|
||||
// fresh). Jiggle the pointer a few px and retry — one mouseMoved sometimes
|
||||
// isn't enough for the hover intent to register.
|
||||
let oneK = null;
|
||||
for (let j = 0; j < 3 && !oneK; j++) {
|
||||
const c = center(dl);
|
||||
await bg({ type: 'hoverAt', x: c.x - 4 + j * 4, y: c.y });
|
||||
oneK = await waitFor(() => findMenuItem(/\b1k\b|original size/i), 1600, 250);
|
||||
}
|
||||
if (!oneK) { missed++; seen.add(href); log('info', 'harvest: Download hovered but no 1K flyout appeared.'); await bg({ type: 'pressEscape' }); await sleep(400); continue; }
|
||||
if (!oneK) { // last resort: some builds expand on click
|
||||
await bg({ type: 'clickAt', ...center(dl) });
|
||||
oneK = await waitFor(() => findMenuItem(/\b1k\b|original size/i), 1600, 250);
|
||||
}
|
||||
if (!oneK) { missed++; seen.add(href); log('info', 'harvest: no 1K flyout after hover + click-expand.'); await bg({ type: 'pressEscape' }); await sleep(400); continue; }
|
||||
|
||||
// Stage 4/4: walk the pointer INTO the flyout (keeps it open), then click 1K,
|
||||
// then VERIFY a download event actually fired before counting it.
|
||||
const dlBefore = ((await bg({ type: 'harvestStats' })) || {}).count || 0;
|
||||
await bg({ type: 'hoverAt', ...center(oneK) }); await sleep(350);
|
||||
await bg({ type: 'clickAt', ...center(oneK) });
|
||||
got++; seen.add(href);
|
||||
let fired = false;
|
||||
for (let j = 0; j < 12 && !fired; j++) {
|
||||
await sleep(500);
|
||||
fired = (((await bg({ type: 'harvestStats' })) || {}).count || 0) > dlBefore;
|
||||
}
|
||||
if (fired) { got++; if (got % 10 === 0) log('info', `harvest: ${got} downloaded so far…`); }
|
||||
else { missed++; log('info', 'harvest: clicked 1K but no download event fired.'); }
|
||||
seen.add(href);
|
||||
chrome.storage.local.set({ harvested_urls_v2: [...seen].slice(-3000) });
|
||||
if (got % 10 === 0) log('info', `harvest: ${got} downloaded so far…`);
|
||||
await sleep(1800 + Math.random() * 1500); // pace — don't machine-gun the account
|
||||
await bg({ type: 'pressEscape' }); await sleep(300); // ensure menus are closed before the next tile
|
||||
await sleep(1200 + Math.random() * 1200); // pace — don't machine-gun the account
|
||||
}
|
||||
log('success', `Harvest pass done: ${got} downloaded, ${missed} missed, ${skipped} skipped → Downloads/flowrinse/harvest/`);
|
||||
await reportCompletion(task.id, 'success', null, [`downloaded:${got}`, `missed:${missed}`]);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user