From 5c5c1c2abec05b28df9d0cafd003f4d73c2d4c3f Mon Sep 17 00:00:00 2001 From: "Jill \"oatmealine\" Monoids" Date: Sat, 17 Feb 2024 14:06:33 +0300 Subject: [PATCH] add metadata sanity check for duplicates, improve id fetching --- ids.js | 2 ++ index.js | 70 ++++++++++++++++++++++++++++++++++++++++++++++++-------- nlw.js | 2 +- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/ids.js b/ids.js index 07cd8cb..b9c68ff 100644 --- a/ids.js +++ b/ids.js @@ -10,6 +10,8 @@ const fruityLevels = { 'Dark matter (Real)': 'Dark matter', 'Versus... MR. BEAST!!!': 'Vers', // ??? 'El Dorado': 'El Dorado ', // ???? + 'Photovoltaic ': 'Photovoltaic', + 'Disconnect ': 'Disconnect', }; // horrid. thank you ids diff --git a/index.js b/index.js index f43db7a..eb00175 100644 --- a/index.js +++ b/index.js @@ -55,31 +55,60 @@ async function fetchSheets() { await loadupMetadataQueue(); } -async function fetchLevelData(name, creator) { +async function fetchLevelData(name, creator, loose = false) { console.log('looking up metadata for', name, 'by', creator); - const { statusCode, headers, trailers, body } = await request(`https://history.geometrydash.eu/api/v1/search/level/advanced/?query=${name}&filter=cache_demon=true`); + + const params = new URLSearchParams(loose ? { + 'query': name, + 'filter': 'cache_demon=true', + } : { + 'filter': `cache_demon=true AND cache_level_name='${name}'`, + }); + + const { statusCode, headers, trailers, body } = await request(`https://history.geometrydash.eu/api/v1/search/level/advanced/?${params.toString()}`); const data = await body.json(); + if (data.hits.length === 0 && !loose) { + return await fetchLevelData(name, creator, true); + } else if (data.hits.length === 0) { + return undefined; + } + if (data.hits.length === 1) return data.hits[0]; const exact = data.hits.filter(h => h.cache_level_name.toLowerCase() === name.toLowerCase()); if (exact.length === 1) return exact[0]; - const creatorHits = data.hits.filter(h => creator.toLowerCase().toLowerCase().includes(h.cache_username)); + const creatorHits = data.hits.filter(h => creator.toLowerCase().includes(h.cache_username.toLowerCase())); if (creatorHits.length === 1) return creatorHits[0]; return data.hits[0]; } const metadataFetchQueue = new PQueue({ concurrency: 10, interval: 500, intervalCap: 2 }); -metadataFetchQueue.on('empty', () => { console.log('metadata fetch queue empty!'); }); +metadataFetchQueue.on('empty', async () => { + console.log('metadata fetch queue empty, idling'); + await metadataSanityCheck(); +}); + +// hopefully will prevent cross-sheet duplicate fighting +// eg. the same level in multiple sheets being marked as duplicate metadata +function normalizeCreatorName(name) { + return name.replace('&', 'and').trim().toLowerCase(); +} + +function getMetadata(level) { + return levels.metadata.find(m => + level.name === m.name && + // normalized on the righthand side to prevent completely invalidating old caches + normalizeCreatorName(level.creator) === normalizeCreatorName(m.creator) + ); +} async function loadupMetadataQueue() { const list = [...levels.nlw.regular, ...levels.nlw.platformer, ...levels.nlw.pending, ...levels.ids.regular]; - const noMetadata = list.filter(l => - levels.metadata.findIndex(m => m.name === l.name && m.creator === l.creator) === -1 - ); + const noMetadata = list.filter(l => getMetadata(l) === undefined); if (noMetadata.length === 0) { console.log('no metadata to fetch!'); return @@ -89,6 +118,12 @@ async function loadupMetadataQueue() { metadataFetchQueue.addAll( noMetadata.map(level => (async () => { + // to prevent race conditions, let's check if the metadata exists already and cancel if so + if (getMetadata(level)) { + console.log(`metadata for ${level.name} by ${level.creator} already found, lol`); + return; + } + const data = await fetchLevelData(level.name, level.creator); if (!data) { console.error('failed to find metadata!'); @@ -99,7 +134,7 @@ async function loadupMetadataQueue() { levels.metadata.push({ name: level.name, - creator: level.creator, + creator: normalizeCreatorName(level.creator), id: data.online_id, }); @@ -108,6 +143,23 @@ async function loadupMetadataQueue() { ); } +async function metadataSanityCheck() { + const duplicates = levels.metadata.filter((l1, index) => levels.metadata.findIndex(l2 => l1.id === l2.id) !== index); + + if (duplicates.length > 0) { + console.log('WARNING - duplicate IDs found in metadata table!:'); + const ids = new Set(duplicates.map(l => l.id)); + const badLevels = [...ids].flatMap(id => levels.metadata.filter(l => l.id === id)); + for (const level of badLevels) { + console.log(`${level.id} ${level.name} by ${level.creator}`); + } + console.log('clearing out, will be refreshed next metadata fetch cycle'); + levels.metadata = levels.metadata.filter(l => !ids.has(l.id)); + await saveCache(); + //await loadupMetadataQueue(); + } +} + await loadCache(); //await loadupMetadataQueue(); @@ -137,7 +189,7 @@ app.get('/list', (req, res) => { return res.status(400); } - res.json(list.map(l => ({ ...(levels.metadata.find(m => l.name === m.name && l.creator === m.creator) || {}), ...l }))); + res.json(list.map(l => ({ ...(getMetadata(l) || {}), ...l }))); }); app.get('/ids', (req, res) => { diff --git a/nlw.js b/nlw.js index 75cddfd..01ac84a 100644 --- a/nlw.js +++ b/nlw.js @@ -17,7 +17,7 @@ const fruityLevels = { 'Missing Benefi s': 'Missing Benefits', 'troll levle': 'troll level', 'gardening map': 'gardening map ', - 'Flying Maze': 'Floating Maze', + 'Flying Maze': 'Floating Outskirts', }; const brokenColors = {