2026-05-27 15:59:59 -04:00
|
|
|
import { discoverMissingMtgSets, discoverMissingPokemonSets } from './discover.js';
|
|
|
|
|
import { importMtgSet } from './mtg.js';
|
|
|
|
|
import { importPokemonSet } from './pokemon.js';
|
|
|
|
|
|
|
|
|
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
|
|
|
|
|
|
|
|
const DEFAULT_MAX_SETS_PER_RUN = 3;
|
|
|
|
|
const DEFAULT_DELAY_MS = 1500;
|
|
|
|
|
|
2026-05-27 22:49:58 -04:00
|
|
|
/**
|
|
|
|
|
* Merge missing MTG + Pokémon sets and pick the newest releases first.
|
|
|
|
|
*/
|
|
|
|
|
export function buildCatalogSyncQueue(missingMtg, missingPokemon, maxSetsPerRun = DEFAULT_MAX_SETS_PER_RUN) {
|
|
|
|
|
const queue = [
|
|
|
|
|
...missingMtg.map((set) => ({
|
|
|
|
|
game: 'mtg',
|
|
|
|
|
setCode: set.code,
|
|
|
|
|
name: set.name,
|
|
|
|
|
releasedAt: set.releasedAt,
|
|
|
|
|
})),
|
|
|
|
|
...missingPokemon.map((set) => ({
|
|
|
|
|
game: 'pokemon',
|
|
|
|
|
setCode: set.id,
|
|
|
|
|
name: set.name,
|
|
|
|
|
releasedAt: set.releasedAt,
|
|
|
|
|
})),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
queue.sort((a, b) => {
|
|
|
|
|
const dateA = a.releasedAt || '';
|
|
|
|
|
const dateB = b.releasedAt || '';
|
|
|
|
|
if (!dateA && !dateB) return 0;
|
|
|
|
|
if (!dateA) return 1;
|
|
|
|
|
if (!dateB) return -1;
|
|
|
|
|
return dateB.localeCompare(dateA);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return queue.slice(0, maxSetsPerRun);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-27 15:59:59 -04:00
|
|
|
/**
|
|
|
|
|
* Discover missing MTG + Pokémon sets and import up to maxSetsPerRun, paced for upstream APIs.
|
|
|
|
|
*/
|
|
|
|
|
export async function runCatalogSync(options = {}) {
|
|
|
|
|
const maxSetsPerRun = options.maxSetsPerRun ?? DEFAULT_MAX_SETS_PER_RUN;
|
|
|
|
|
const delayBetweenSetsMs = options.delayBetweenSetsMs ?? DEFAULT_DELAY_MS;
|
|
|
|
|
|
|
|
|
|
const [missingMtg, missingPokemon] = await Promise.all([
|
|
|
|
|
discoverMissingMtgSets(),
|
|
|
|
|
discoverMissingPokemonSets(),
|
|
|
|
|
]);
|
|
|
|
|
|
2026-05-27 22:49:58 -04:00
|
|
|
const queue = buildCatalogSyncQueue(missingMtg, missingPokemon, maxSetsPerRun);
|
2026-05-27 15:59:59 -04:00
|
|
|
|
|
|
|
|
const summary = {
|
|
|
|
|
imported: 0,
|
|
|
|
|
skipped: 0,
|
|
|
|
|
errors: [],
|
|
|
|
|
setsProcessed: [],
|
|
|
|
|
pendingMtgSets: missingMtg.length,
|
|
|
|
|
pendingPokemonSets: missingPokemon.length,
|
|
|
|
|
lorcana: 'skipped — manual Lorcana set map update required',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (let index = 0; index < queue.length; index += 1) {
|
|
|
|
|
const item = queue[index];
|
|
|
|
|
try {
|
|
|
|
|
const result =
|
|
|
|
|
item.game === 'mtg'
|
|
|
|
|
? await importMtgSet(item.setCode)
|
|
|
|
|
: await importPokemonSet(item.setCode);
|
|
|
|
|
|
|
|
|
|
summary.setsProcessed.push({
|
|
|
|
|
game: item.game,
|
|
|
|
|
setCode: item.setCode,
|
|
|
|
|
name: item.name,
|
|
|
|
|
...result,
|
|
|
|
|
});
|
|
|
|
|
summary.imported += result.imported;
|
|
|
|
|
summary.skipped += result.skipped;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error(`[runCatalogSync] Failed ${item.game}/${item.setCode}:`, error);
|
|
|
|
|
summary.errors.push({
|
|
|
|
|
game: item.game,
|
|
|
|
|
setCode: item.setCode,
|
|
|
|
|
message: error.message,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (index < queue.length - 1) {
|
|
|
|
|
await delay(delayBetweenSetsMs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('[runCatalogSync]', JSON.stringify(summary));
|
|
|
|
|
return summary;
|
|
|
|
|
}
|