fix: identify badge items by .gif presence in album1584 directory
Badge items like HC_Badge, BADGE_SHREK_03 have item_names that don't start
with 'badge_' and interaction_type='default'. Now loads known badge codes
from <gamedataRoot>/album1584/*.gif files and uses that set to exclude
badge items from .nitro and icon audit checks. Also removes incorrect
startsWith('badge_') check that would wrongly skip badge display cases
which DO have .nitro files.
This commit is contained in:
1 parent
750973de38
commit
b1a1e5125c
3 files changed
+67
-9
No files matched your search
@@ -1,9 +1,13 @@
|
|||||||
import { readdir } from "node:fs/promises";
|
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";
|
||||||
import { listSources } from "@/lib/services/clone-sources";
|
import { listSources } from "@/lib/services/clone-sources";
|
||||||
import { getFurniAssetDirs } from "@/lib/services/furni-asset-dirs";
|
import {
|
||||||
|
getFurniAssetDirs,
|
||||||
|
getGamedataRoot,
|
||||||
|
} from "@/lib/services/furni-asset-dirs";
|
||||||
import {
|
import {
|
||||||
applyCatalogSql,
|
applyCatalogSql,
|
||||||
generateCatalogSql,
|
generateCatalogSql,
|
||||||
@@ -140,12 +144,26 @@ export async function runCatalogAudit(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [iconFiles, nitroFiles] = await Promise.all([
|
const [iconFiles, nitroFiles, badgeGifFiles] = await Promise.all([
|
||||||
readdir(iconDir).catch(() => [] as string[]),
|
readdir(iconDir).catch(() => [] as string[]),
|
||||||
readdir(nitroDir).catch(() => [] as string[]),
|
readdir(nitroDir).catch(() => [] as string[]),
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
const gamedataRoot = await getGamedataRoot();
|
||||||
|
if (!gamedataRoot) return [] as string[];
|
||||||
|
return await readdir(path.join(gamedataRoot, "album1584")).catch(
|
||||||
|
() => [] as string[],
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return [] as string[];
|
||||||
|
}
|
||||||
|
})(),
|
||||||
]);
|
]);
|
||||||
const iconSet = new Set(iconFiles);
|
const iconSet = new Set(iconFiles);
|
||||||
const nitroSet = new Set(nitroFiles);
|
const nitroSet = new Set(nitroFiles);
|
||||||
|
const badgeSet = new Set(
|
||||||
|
badgeGifFiles.filter((f) => f.endsWith(".gif")).map((f) => f.slice(0, -4)),
|
||||||
|
);
|
||||||
|
|
||||||
onEvent?.({ type: "progress", message: "Loading items_base…" });
|
onEvent?.({ type: "progress", message: "Loading items_base…" });
|
||||||
|
|
||||||
@@ -242,7 +260,7 @@ export async function runCatalogAudit(
|
|||||||
});
|
});
|
||||||
// Badge items use .gif files, not .nitro bundles.
|
// Badge items use .gif files, not .nitro bundles.
|
||||||
if (
|
if (
|
||||||
!item.item_name.startsWith("badge_") &&
|
!badgeSet.has(item.item_name) &&
|
||||||
nitroSet.has(`${item.item_name}.nitro`)
|
nitroSet.has(`${item.item_name}.nitro`)
|
||||||
) {
|
) {
|
||||||
missingCatalogEntries.push({
|
missingCatalogEntries.push({
|
||||||
@@ -314,7 +332,7 @@ export async function runCatalogAudit(
|
|||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
const item = items[i];
|
const item = items[i];
|
||||||
// Badge items use .gif files, not .nitro bundles.
|
// Badge items use .gif files, not .nitro bundles.
|
||||||
if (item.item_name.startsWith("badge_")) continue;
|
if (badgeSet.has(item.item_name)) continue;
|
||||||
if (!nitroSet.has(`${item.item_name}.nitro`)) {
|
if (!nitroSet.has(`${item.item_name}.nitro`)) {
|
||||||
missingNitroClassnames.push(item.item_name);
|
missingNitroClassnames.push(item.item_name);
|
||||||
issues.push({
|
issues.push({
|
||||||
@@ -340,11 +358,10 @@ 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];
|
||||||
// Badge items use .gif icons stored as badge_<code>_icon.png,
|
// Badge items use .gif icons (stored in album1584), not .png icon files.
|
||||||
// not <code>_icon.png like regular furni.
|
if (badgeSet.has(item.item_name)) continue;
|
||||||
const iconFileName = `${item.item_name}_icon.png`;
|
const iconFileName = `${item.item_name}_icon.png`;
|
||||||
const badgeIconFileName = `badge_${item.item_name}_icon.png`;
|
if (!iconSet.has(iconFileName)) {
|
||||||
if (!iconSet.has(iconFileName) && !iconSet.has(badgeIconFileName)) {
|
|
||||||
missingIconClassnames.push(item.item_name);
|
missingIconClassnames.push(item.item_name);
|
||||||
issues.push({
|
issues.push({
|
||||||
type: "missing_icon",
|
type: "missing_icon",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
type FurniAssetWriteTargets,
|
type FurniAssetWriteTargets,
|
||||||
getFurniAssetWriteTargets,
|
getFurniAssetWriteTargets,
|
||||||
|
getGamedataRoot,
|
||||||
} from "@/lib/services/furni-asset-dirs";
|
} from "@/lib/services/furni-asset-dirs";
|
||||||
import { downloadFile } from "@/lib/services/import/core/download";
|
import { downloadFile } from "@/lib/services/import/core/download";
|
||||||
import { ensureDirectories } from "./furni-import";
|
import { ensureDirectories } from "./furni-import";
|
||||||
@@ -140,6 +141,23 @@ export async function repairMissingIcons(
|
|||||||
...targets.mirrorDirs.map((d) => d.nitroDir),
|
...targets.mirrorDirs.map((d) => d.nitroDir),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Load known badge codes from the album directory so we can skip badge
|
||||||
|
// items — they use .gif files, not .png icons.
|
||||||
|
let badgeSet: Set<string> | null = null;
|
||||||
|
try {
|
||||||
|
const gamedataRoot = await getGamedataRoot();
|
||||||
|
if (gamedataRoot) {
|
||||||
|
const badgeFiles = await fs
|
||||||
|
.readdir(path.join(gamedataRoot, "album1584"))
|
||||||
|
.catch(() => [] as string[]);
|
||||||
|
badgeSet = new Set(
|
||||||
|
badgeFiles.filter((f) => f.endsWith(".gif")).map((f) => f.slice(0, -4)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
badgeSet = null;
|
||||||
|
}
|
||||||
|
|
||||||
let items: Array<{ id: number; item_name: string }>;
|
let items: Array<{ id: number; item_name: string }>;
|
||||||
try {
|
try {
|
||||||
const [rows] = (await db.execute(sql`
|
const [rows] = (await db.execute(sql`
|
||||||
@@ -158,6 +176,11 @@ export async function repairMissingIcons(
|
|||||||
onEvent?.({ type: "started", total });
|
onEvent?.({ type: "started", total });
|
||||||
|
|
||||||
const processItem = async (classname: string) => {
|
const processItem = async (classname: string) => {
|
||||||
|
// Badge items that only exist as .gif files (in album1584) don't have
|
||||||
|
// .png icon files to repair. Skip them.
|
||||||
|
if (badgeSet?.has(classname)) {
|
||||||
|
return { status: "skipped" } as const;
|
||||||
|
}
|
||||||
const fileName = `${classname}_icon.png`;
|
const fileName = `${classname}_icon.png`;
|
||||||
|
|
||||||
// Badge items store icons as badge_<code>_icon.png (the classname already
|
// Badge items store icons as badge_<code>_icon.png (the classname already
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
type FurniAssetWriteTargets,
|
type FurniAssetWriteTargets,
|
||||||
getFurniAssetWriteTargets,
|
getFurniAssetWriteTargets,
|
||||||
|
getGamedataRoot,
|
||||||
} from "@/lib/services/furni-asset-dirs";
|
} from "@/lib/services/furni-asset-dirs";
|
||||||
import { downloadFile } from "@/lib/services/import/core/download";
|
import { downloadFile } from "@/lib/services/import/core/download";
|
||||||
import { parseNitroBundle } from "@/lib/services/swf/nitro-builder";
|
import { parseNitroBundle } from "@/lib/services/swf/nitro-builder";
|
||||||
@@ -136,6 +137,23 @@ export async function repairMissingNitros(
|
|||||||
...targets.mirrorDirs.map((d) => d.nitroDir),
|
...targets.mirrorDirs.map((d) => d.nitroDir),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Load known badge codes from the album directory so we can skip badge
|
||||||
|
// items — they use .gif files, not .nitro bundles.
|
||||||
|
let badgeSet: Set<string> | null = null;
|
||||||
|
try {
|
||||||
|
const gamedataRoot = await getGamedataRoot();
|
||||||
|
if (gamedataRoot) {
|
||||||
|
const badgeFiles = await fs
|
||||||
|
.readdir(path.join(gamedataRoot, "album1584"))
|
||||||
|
.catch(() => [] as string[]);
|
||||||
|
badgeSet = new Set(
|
||||||
|
badgeFiles.filter((f) => f.endsWith(".gif")).map((f) => f.slice(0, -4)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
badgeSet = null;
|
||||||
|
}
|
||||||
|
|
||||||
let items: Array<{ id: number; item_name: string }>;
|
let items: Array<{ id: number; item_name: string }>;
|
||||||
try {
|
try {
|
||||||
const [rows] = (await db.execute(sql`
|
const [rows] = (await db.execute(sql`
|
||||||
@@ -155,7 +173,7 @@ export async function repairMissingNitros(
|
|||||||
|
|
||||||
const processItem = async (classname: string) => {
|
const processItem = async (classname: string) => {
|
||||||
// Badge items use .gif files, not .nitro bundles.
|
// Badge items use .gif files, not .nitro bundles.
|
||||||
if (classname.startsWith("badge_")) {
|
if (badgeSet?.has(classname) ?? false) {
|
||||||
return { status: "skipped" } as const;
|
return { status: "skipped" } as const;
|
||||||
}
|
}
|
||||||
const fileName = `${classname}.nitro`;
|
const fileName = `${classname}.nitro`;
|
||||||
|
|||||||
Reference in new issue
Block a user