perf: speed up catalog audit by batching file checks and parallelizing source fetches
This commit is contained in:
1 parent
40a21ba767
commit
1cbb33a4e2
1 file changed
+48
-25
@@ -1,5 +1,4 @@
|
|||||||
import { existsSync } from "node:fs";
|
import { readdir } from "node:fs/promises";
|
||||||
import path from "node:path";
|
|
||||||
import { sql } from "drizzle-orm";
|
import { sql } from "drizzle-orm";
|
||||||
import { db } from "@/lib/db";
|
import { db } from "@/lib/db";
|
||||||
import { fetchSourceFurnidata } from "@/lib/services/clone-import";
|
import { fetchSourceFurnidata } from "@/lib/services/clone-import";
|
||||||
@@ -141,6 +140,13 @@ export async function runCatalogAudit(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [iconFiles, nitroFiles] = await Promise.all([
|
||||||
|
readdir(iconDir).catch(() => [] as string[]),
|
||||||
|
readdir(nitroDir).catch(() => [] as string[]),
|
||||||
|
]);
|
||||||
|
const iconSet = new Set(iconFiles);
|
||||||
|
const nitroSet = new Set(nitroFiles);
|
||||||
|
|
||||||
onEvent?.({ type: "progress", message: "Loading items_base…" });
|
onEvent?.({ type: "progress", message: "Loading items_base…" });
|
||||||
|
|
||||||
let items: Array<{
|
let items: Array<{
|
||||||
@@ -236,7 +242,7 @@ export async function runCatalogAudit(
|
|||||||
});
|
});
|
||||||
// Only include items that actually have a .nitro asset — a catalog
|
// Only include items that actually have a .nitro asset — a catalog
|
||||||
// entry for a furni without a bundle would be un-placeable in-game.
|
// entry for a furni without a bundle would be un-placeable in-game.
|
||||||
if (existsSync(path.join(nitroDir, `${item.item_name}.nitro`))) {
|
if (nitroSet.has(`${item.item_name}.nitro`)) {
|
||||||
missingCatalogEntries.push({
|
missingCatalogEntries.push({
|
||||||
classname: item.item_name,
|
classname: item.item_name,
|
||||||
itemId: item.id,
|
itemId: item.id,
|
||||||
@@ -305,8 +311,7 @@ export async function runCatalogAudit(
|
|||||||
const missingNitroClassnames: string[] = [];
|
const missingNitroClassnames: string[] = [];
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const nitroPath = path.join(nitroDir, `${item.item_name}.nitro`);
|
if (!nitroSet.has(`${item.item_name}.nitro`)) {
|
||||||
if (!existsSync(nitroPath)) {
|
|
||||||
missingNitroClassnames.push(item.item_name);
|
missingNitroClassnames.push(item.item_name);
|
||||||
issues.push({
|
issues.push({
|
||||||
type: "missing_nitro",
|
type: "missing_nitro",
|
||||||
@@ -331,8 +336,7 @@ export async function runCatalogAudit(
|
|||||||
const missingIconClassnames: string[] = [];
|
const missingIconClassnames: string[] = [];
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
const iconPath = path.join(iconDir, `${item.item_name}_icon.png`);
|
if (!iconSet.has(`${item.item_name}_icon.png`)) {
|
||||||
if (!existsSync(iconPath)) {
|
|
||||||
missingIconClassnames.push(item.item_name);
|
missingIconClassnames.push(item.item_name);
|
||||||
issues.push({
|
issues.push({
|
||||||
type: "missing_icon",
|
type: "missing_icon",
|
||||||
@@ -383,7 +387,7 @@ export async function runCatalogAudit(
|
|||||||
// and drop the ones the repair fixed from the issue list.
|
// and drop the ones the repair fixed from the issue list.
|
||||||
const stillMissing: string[] = [];
|
const stillMissing: string[] = [];
|
||||||
for (const classname of missingIconClassnames) {
|
for (const classname of missingIconClassnames) {
|
||||||
if (!existsSync(path.join(iconDir, `${classname}_icon.png`))) {
|
if (!iconSet.has(`${classname}_icon.png`)) {
|
||||||
stillMissing.push(classname);
|
stillMissing.push(classname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -438,7 +442,7 @@ export async function runCatalogAudit(
|
|||||||
// and drop the ones the repair fixed from the issue list.
|
// and drop the ones the repair fixed from the issue list.
|
||||||
const stillMissing: string[] = [];
|
const stillMissing: string[] = [];
|
||||||
for (const classname of missingNitroClassnames) {
|
for (const classname of missingNitroClassnames) {
|
||||||
if (!existsSync(path.join(nitroDir, `${classname}.nitro`))) {
|
if (!nitroSet.has(`${classname}.nitro`)) {
|
||||||
stillMissing.push(classname);
|
stillMissing.push(classname);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -601,27 +605,46 @@ export async function runCatalogAudit(
|
|||||||
onEvent?.({ type: "progress", message: "Comparing with clone sources…" });
|
onEvent?.({ type: "progress", message: "Comparing with clone sources…" });
|
||||||
|
|
||||||
const sources = await listSources();
|
const sources = await listSources();
|
||||||
for (const source of sources) {
|
const sourceResults = await Promise.all(
|
||||||
if (!source.furnidataUrl) continue;
|
sources
|
||||||
try {
|
.filter((s) => s.furnidataUrl)
|
||||||
const entries = await fetchSourceFurnidata(source.furnidataUrl);
|
.map(async (source) => {
|
||||||
for (const entry of entries) {
|
try {
|
||||||
if (!itemByName.has(entry.classname)) {
|
const entries = await fetchSourceFurnidata(source.furnidataUrl);
|
||||||
missingFromSources.push({
|
return {
|
||||||
sourceName: source.name,
|
source,
|
||||||
sourceId: source.id,
|
entries,
|
||||||
classname: entry.classname,
|
error: null,
|
||||||
itemName: entry.name,
|
} as const;
|
||||||
});
|
} catch {
|
||||||
|
return {
|
||||||
|
source,
|
||||||
|
entries: [] as Array<{ classname: string; name: string }>,
|
||||||
|
error: true,
|
||||||
|
} as const;
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
} catch {
|
);
|
||||||
|
|
||||||
|
for (const result of sourceResults) {
|
||||||
|
if (result.error) {
|
||||||
issues.push({
|
issues.push({
|
||||||
type: "source_fetch_failed",
|
type: "source_fetch_failed",
|
||||||
severity: "error",
|
severity: "error",
|
||||||
sourceName: source.name,
|
sourceName: result.source.name,
|
||||||
message: `Failed to fetch furnidata from "${source.name}"`,
|
message: `Failed to fetch furnidata from "${result.source.name}"`,
|
||||||
});
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const entry of result.entries) {
|
||||||
|
if (!itemByName.has(entry.classname)) {
|
||||||
|
missingFromSources.push({
|
||||||
|
sourceName: result.source.name,
|
||||||
|
sourceId: result.source.id,
|
||||||
|
classname: entry.classname,
|
||||||
|
itemName: entry.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user