Fix missed removals from async ownership state rendering
Add/Remove button state loads asynchronously after page render, so a fixed delay could scan before all buttons updated and skip real candidates (confirmed via Strum's VCV Modules brand page). Add a button-state stabilization wait and multi-pass per-page scanning to catch late-rendering items.
This commit is contained in:
@@ -46,6 +46,30 @@ function pageUrl(page, mode) {
|
||||
return base.toString();
|
||||
}
|
||||
|
||||
async function waitForStableActionButtons(page, { stableForMs = 800, maxWaitMs = 8000 } = {}) {
|
||||
function signature() {
|
||||
const buttons = Array.from(document.querySelectorAll('button.library-add, button.library-remove'));
|
||||
return buttons
|
||||
.map((b) => `${b.className.includes('library-add') ? 'add' : 'remove'}:${b.className.includes('hidden') ? 'h' : 'v'}`)
|
||||
.join('|');
|
||||
}
|
||||
|
||||
const start = Date.now();
|
||||
let lastSig = await page.evaluate(signature);
|
||||
let stableSince = Date.now();
|
||||
|
||||
while (Date.now() - start < maxWaitMs) {
|
||||
await page.waitForTimeout(200);
|
||||
const sig = await page.evaluate(signature);
|
||||
if (sig === lastSig) {
|
||||
if (Date.now() - stableSince >= stableForMs) return;
|
||||
} else {
|
||||
lastSig = sig;
|
||||
stableSince = Date.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function scanAndRemoveOnPage(page, dryRun) {
|
||||
return page.evaluate(({ dryRunArg }) => {
|
||||
function isVisible(el) {
|
||||
@@ -114,6 +138,27 @@ async function scanAndRemoveOnPage(page, dryRun) {
|
||||
}, { dryRunArg: dryRun });
|
||||
}
|
||||
|
||||
async function scanAndRemoveWithRetries(page, dryRun) {
|
||||
let combined = await scanAndRemoveOnPage(page, dryRun);
|
||||
|
||||
// Extra passes catch modules whose ownership state renders after the initial stabilization wait.
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
await waitForStableActionButtons(page, { stableForMs: 600, maxWaitMs: 4000 });
|
||||
const again = await scanAndRemoveOnPage(page, dryRun);
|
||||
if (again.visibleRemoveButtons === 0 && again.removed === 0) break;
|
||||
|
||||
combined = {
|
||||
url: again.url,
|
||||
maxPage: again.maxPage,
|
||||
visibleRemoveButtons: combined.visibleRemoveButtons + again.visibleRemoveButtons,
|
||||
removed: combined.removed + again.removed,
|
||||
keptHardwareClone: combined.keptHardwareClone + again.keptHardwareClone
|
||||
};
|
||||
}
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
function reportPage(pageNumber, info, dryRun) {
|
||||
const modeText = dryRun ? 'dry-run' : 'apply';
|
||||
const unchanged = info.removed === 0 ? 'yes' : 'no';
|
||||
@@ -205,7 +250,8 @@ async function main() {
|
||||
throw new Error('Not logged in on VCV Library after login detection. Aborting to avoid false no-op run.');
|
||||
}
|
||||
|
||||
const first = await scanAndRemoveOnPage(page, opts.dryRun);
|
||||
await waitForStableActionButtons(page);
|
||||
const first = await scanAndRemoveWithRetries(page, opts.dryRun);
|
||||
const lastPage = opts.maxPages
|
||||
? Math.min(opts.startPage + opts.maxPages - 1, first.maxPage)
|
||||
: first.maxPage;
|
||||
@@ -235,7 +281,8 @@ async function main() {
|
||||
const url = pageUrl(p, opts.mode);
|
||||
console.log(`Navigating page ${p}/${lastPage}: ${url}`);
|
||||
await navigateWithRetry(page, url, opts.navTimeoutMs, `Page ${p} navigation`);
|
||||
const info = await scanAndRemoveOnPage(page, opts.dryRun);
|
||||
await waitForStableActionButtons(page);
|
||||
const info = await scanAndRemoveWithRetries(page, opts.dryRun);
|
||||
addStats(info);
|
||||
reportPage(p, info, opts.dryRun);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user