fix(harvest): credit-safe size picker — original size for images AND videos, never the paid upscales
Video download flyout differs from images: 270p GIF / 720p Original Size / 1080p Upscaled / 4K Upscaled·50 credits. Old finder matched '1k' (image-only) and would have missed video originals. New findSizeOption() keys on 'original size' (common to both 1K and 720p rows) and hard-rejects any 'upscal'/'credit' row — the 4K video upscale costs 50 credits, so a stray click there spends real money. Proven against all flyout string variants (spaced + newline-collapsed). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
fc3e668504
commit
20941c007c
32
content.js
32
content.js
@ -551,6 +551,23 @@ function findMenuItem(re) {
|
|||||||
return leaf.length ? (leaf[0].closest('[role="menuitem"], li, button') || leaf[0]) : null;
|
return leaf.length ? (leaf[0].closest('[role="menuitem"], li, button') || leaf[0]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The FREE "original size" download option. Images: "1K Original size". Videos:
|
||||||
|
// "720p Original Size". Both contain "original size" — that's the safe key.
|
||||||
|
// SAFETY: never return an "Upscaled" / "…credits" row. The 4K video upscale costs
|
||||||
|
// 50 credits; the 1080p upscale isn't free either. A stray click there spends money.
|
||||||
|
function findSizeOption() {
|
||||||
|
const cands = [...document.querySelectorAll('[role="menuitem"], [role="menu"] button, div, span, li')]
|
||||||
|
.filter(visible)
|
||||||
|
.filter(e => {
|
||||||
|
const t = (e.textContent || '').trim();
|
||||||
|
return t.length < 40 && /original\s*size/i.test(t) && !/upscal|credit/i.test(t);
|
||||||
|
});
|
||||||
|
if (!cands.length) return null;
|
||||||
|
// smallest matching node = the option leaf, not a container wrapping the whole flyout
|
||||||
|
cands.sort((a, b) => (a.textContent || '').length - (b.textContent || '').length);
|
||||||
|
return cands[0].closest('[role="menuitem"], li, button') || cands[0];
|
||||||
|
}
|
||||||
|
|
||||||
let harvestAbort = false;
|
let harvestAbort = false;
|
||||||
|
|
||||||
// DL Mode from the popup: harvest what's on screen right now, no queue involved.
|
// DL Mode from the popup: harvest what's on screen right now, no queue involved.
|
||||||
@ -599,21 +616,22 @@ async function executeHarvest(task) {
|
|||||||
const dl = await waitFor(() => findMenuItem(/^\s*download\b/i), 3000, 300);
|
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; }
|
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
|
// Stage 3/4: HOVER Download — the size flyout unfurls beside it (side depends on
|
||||||
// depends on where the tile sits on screen; irrelevant, coords are re-queried
|
// where the tile sits; irrelevant, coords are re-queried fresh). Jiggle the
|
||||||
// fresh). Jiggle the pointer a few px and retry — one mouseMoved sometimes
|
// pointer a few px and retry — one mouseMoved sometimes isn't enough for the
|
||||||
// isn't enough for the hover intent to register.
|
// hover intent to register. We pick "original size" (1K image / 720p video) and
|
||||||
|
// NEVER the paid upscales — findSizeOption enforces that.
|
||||||
let oneK = null;
|
let oneK = null;
|
||||||
for (let j = 0; j < 3 && !oneK; j++) {
|
for (let j = 0; j < 3 && !oneK; j++) {
|
||||||
const c = center(dl);
|
const c = center(dl);
|
||||||
await bg({ type: 'hoverAt', x: c.x - 4 + j * 4, y: c.y });
|
await bg({ type: 'hoverAt', x: c.x - 4 + j * 4, y: c.y });
|
||||||
oneK = await waitFor(() => findMenuItem(/\b1k\b|original size/i), 1600, 250);
|
oneK = await waitFor(findSizeOption, 1600, 250);
|
||||||
}
|
}
|
||||||
if (!oneK) { // last resort: some builds expand on click
|
if (!oneK) { // last resort: some builds expand on click
|
||||||
await bg({ type: 'clickAt', ...center(dl) });
|
await bg({ type: 'clickAt', ...center(dl) });
|
||||||
oneK = await waitFor(() => findMenuItem(/\b1k\b|original size/i), 1600, 250);
|
oneK = await waitFor(findSizeOption, 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; }
|
if (!oneK) { missed++; seen.add(href); log('info', 'harvest: no "original size" option 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,
|
// 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.
|
// then VERIFY a download event actually fired before counting it.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user