This commit is contained in:
1 parent
8efd032cc6
commit
df38dccbf1
735 files changed
+128321
-120870
No files matched your search
+140
-122
@@ -2,51 +2,51 @@ import { resolveGordonBuildUrl } from "@/lib/services/import/core/gordon";
|
||||
import { siteSettings } from "@/lib/services/site-settings";
|
||||
|
||||
export interface OfficialColor {
|
||||
id: number;
|
||||
index: number;
|
||||
club: number;
|
||||
selectable: boolean;
|
||||
hexCode: string;
|
||||
id: number;
|
||||
index: number;
|
||||
club: number;
|
||||
selectable: boolean;
|
||||
hexCode: string;
|
||||
}
|
||||
export interface OfficialPalette {
|
||||
id: number;
|
||||
colors: OfficialColor[];
|
||||
id: number;
|
||||
colors: OfficialColor[];
|
||||
}
|
||||
export interface OfficialPart {
|
||||
id: number;
|
||||
type: string;
|
||||
colorable: boolean;
|
||||
index: number;
|
||||
colorindex: number;
|
||||
id: number;
|
||||
type: string;
|
||||
colorable: boolean;
|
||||
index: number;
|
||||
colorindex: number;
|
||||
}
|
||||
export interface OfficialSet {
|
||||
id: number;
|
||||
gender: string;
|
||||
club: number;
|
||||
colorable: boolean;
|
||||
selectable: boolean;
|
||||
preselectable: boolean;
|
||||
sellable: boolean;
|
||||
parts: OfficialPart[];
|
||||
hiddenLayers?: { partType: string }[];
|
||||
id: number;
|
||||
gender: string;
|
||||
club: number;
|
||||
colorable: boolean;
|
||||
selectable: boolean;
|
||||
preselectable: boolean;
|
||||
sellable: boolean;
|
||||
parts: OfficialPart[];
|
||||
hiddenLayers?: { partType: string }[];
|
||||
}
|
||||
export interface OfficialSetType {
|
||||
type: string;
|
||||
paletteId: number;
|
||||
mandM0: boolean;
|
||||
mandF0: boolean;
|
||||
mandM1: boolean;
|
||||
mandF1: boolean;
|
||||
sets: OfficialSet[];
|
||||
type: string;
|
||||
paletteId: number;
|
||||
mandM0: boolean;
|
||||
mandF0: boolean;
|
||||
mandM1: boolean;
|
||||
mandF1: boolean;
|
||||
sets: OfficialSet[];
|
||||
}
|
||||
export interface OfficialFigureData {
|
||||
palettes: OfficialPalette[];
|
||||
setTypes: OfficialSetType[];
|
||||
palettes: OfficialPalette[];
|
||||
setTypes: OfficialSetType[];
|
||||
}
|
||||
export interface OfficialLibrary {
|
||||
id: string;
|
||||
revision: number;
|
||||
parts: { id: number; type: string }[];
|
||||
id: string;
|
||||
revision: number;
|
||||
parts: { id: number; type: string }[];
|
||||
}
|
||||
|
||||
const CACHE_TTL = 30 * 60 * 1000;
|
||||
@@ -55,107 +55,125 @@ let fdCache: { data: OfficialFigureData; ts: number } | null = null;
|
||||
let fmCache: { libs: OfficialLibrary[]; ts: number } | null = null;
|
||||
|
||||
export function __clearFigureOfficialCache(): void {
|
||||
fdCache = null;
|
||||
fmCache = null;
|
||||
fdCache = null;
|
||||
fmCache = null;
|
||||
}
|
||||
|
||||
const attr = (tag: string, name: string): string | undefined =>
|
||||
new RegExp(`\\b${name}="([^"]*)"`).exec(tag)?.[1];
|
||||
new RegExp(`\\b${name}="([^"]*)"`).exec(tag)?.[1];
|
||||
const bool = (v: string | undefined): boolean => v === "1" || v === "true";
|
||||
const num = (v: string | undefined): number => {
|
||||
const n = Number(v);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
const n = Number(v);
|
||||
return Number.isFinite(n) ? n : 0;
|
||||
};
|
||||
|
||||
export async function fetchOfficialFigureData(): Promise<OfficialFigureData> {
|
||||
const now = Date.now();
|
||||
if (fdCache && now - fdCache.ts < CACHE_TTL) return fdCache.data;
|
||||
const url = await siteSettings.get("figuredata_source_url", DEFAULT_FIGUREDATA_URL);
|
||||
const res = await fetch(url ?? DEFAULT_FIGUREDATA_URL, { signal: AbortSignal.timeout(30000) });
|
||||
if (!res.ok) throw new Error(`figuredata fetch failed: ${res.status}`);
|
||||
const xml = await res.text();
|
||||
const now = Date.now();
|
||||
if (fdCache && now - fdCache.ts < CACHE_TTL) return fdCache.data;
|
||||
const url = await siteSettings.get(
|
||||
"figuredata_source_url",
|
||||
DEFAULT_FIGUREDATA_URL,
|
||||
);
|
||||
const res = await fetch(url ?? DEFAULT_FIGUREDATA_URL, {
|
||||
signal: AbortSignal.timeout(30000),
|
||||
});
|
||||
if (!res.ok) throw new Error(`figuredata fetch failed: ${res.status}`);
|
||||
const xml = await res.text();
|
||||
|
||||
const palettes: OfficialPalette[] = [];
|
||||
for (const pm of xml.matchAll(/<palette\b([^>]*)>([\s\S]*?)<\/palette>/g)) {
|
||||
const id = num(attr(pm[1], "id"));
|
||||
const colors: OfficialColor[] = [];
|
||||
for (const cm of pm[2].matchAll(/<color\b([^>]*)>([^<]*)<\/color>/g)) {
|
||||
colors.push({
|
||||
id: num(attr(cm[1], "id")),
|
||||
index: num(attr(cm[1], "index")),
|
||||
club: num(attr(cm[1], "club")),
|
||||
selectable: bool(attr(cm[1], "selectable")),
|
||||
hexCode: cm[2].trim(),
|
||||
});
|
||||
}
|
||||
palettes.push({ id, colors });
|
||||
}
|
||||
const palettes: OfficialPalette[] = [];
|
||||
for (const pm of xml.matchAll(/<palette\b([^>]*)>([\s\S]*?)<\/palette>/g)) {
|
||||
const id = num(attr(pm[1], "id"));
|
||||
const colors: OfficialColor[] = [];
|
||||
for (const cm of pm[2].matchAll(/<color\b([^>]*)>([^<]*)<\/color>/g)) {
|
||||
colors.push({
|
||||
id: num(attr(cm[1], "id")),
|
||||
index: num(attr(cm[1], "index")),
|
||||
club: num(attr(cm[1], "club")),
|
||||
selectable: bool(attr(cm[1], "selectable")),
|
||||
hexCode: cm[2].trim(),
|
||||
});
|
||||
}
|
||||
palettes.push({ id, colors });
|
||||
}
|
||||
|
||||
const setTypes: OfficialSetType[] = [];
|
||||
for (const sm of xml.matchAll(/<settype\b([^>]*)>([\s\S]*?)<\/settype>/g)) {
|
||||
const a = sm[1];
|
||||
const sets: OfficialSet[] = [];
|
||||
for (const setM of sm[2].matchAll(/<set\b([^>]*)>([\s\S]*?)<\/set>/g)) {
|
||||
const sa = setM[1];
|
||||
const parts: OfficialPart[] = [];
|
||||
for (const pm of setM[2].matchAll(/<part\b([^>]*)\/?>/g)) {
|
||||
parts.push({
|
||||
id: num(attr(pm[1], "id")),
|
||||
type: attr(pm[1], "type") ?? "",
|
||||
colorable: bool(attr(pm[1], "colorable")),
|
||||
index: num(attr(pm[1], "index")),
|
||||
colorindex: num(attr(pm[1], "colorindex")),
|
||||
});
|
||||
}
|
||||
const hiddenLayers: { partType: string }[] = [];
|
||||
for (const hm of setM[2].matchAll(/<layer\b([^>]*)\/?>/g)) {
|
||||
const pt = attr(hm[1], "parttype");
|
||||
if (pt) hiddenLayers.push({ partType: pt });
|
||||
}
|
||||
sets.push({
|
||||
id: num(attr(sa, "id")),
|
||||
gender: attr(sa, "gender") ?? "U",
|
||||
club: num(attr(sa, "club")),
|
||||
colorable: bool(attr(sa, "colorable")),
|
||||
selectable: bool(attr(sa, "selectable")),
|
||||
preselectable: bool(attr(sa, "preselectable")),
|
||||
sellable: bool(attr(sa, "sellable")),
|
||||
parts,
|
||||
hiddenLayers: hiddenLayers.length ? hiddenLayers : undefined,
|
||||
});
|
||||
}
|
||||
setTypes.push({
|
||||
type: attr(a, "type") ?? "",
|
||||
paletteId: num(attr(a, "paletteid")),
|
||||
mandM0: bool(attr(a, "mand_m_0")),
|
||||
mandF0: bool(attr(a, "mand_f_0")),
|
||||
mandM1: bool(attr(a, "mand_m_1")),
|
||||
mandF1: bool(attr(a, "mand_f_1")),
|
||||
sets,
|
||||
});
|
||||
}
|
||||
const setTypes: OfficialSetType[] = [];
|
||||
for (const sm of xml.matchAll(/<settype\b([^>]*)>([\s\S]*?)<\/settype>/g)) {
|
||||
const a = sm[1];
|
||||
const sets: OfficialSet[] = [];
|
||||
for (const setM of sm[2].matchAll(/<set\b([^>]*)>([\s\S]*?)<\/set>/g)) {
|
||||
const sa = setM[1];
|
||||
const parts: OfficialPart[] = [];
|
||||
for (const pm of setM[2].matchAll(/<part\b([^>]*)\/?>/g)) {
|
||||
parts.push({
|
||||
id: num(attr(pm[1], "id")),
|
||||
type: attr(pm[1], "type") ?? "",
|
||||
colorable: bool(attr(pm[1], "colorable")),
|
||||
index: num(attr(pm[1], "index")),
|
||||
colorindex: num(attr(pm[1], "colorindex")),
|
||||
});
|
||||
}
|
||||
const hiddenLayers: { partType: string }[] = [];
|
||||
for (const hm of setM[2].matchAll(/<layer\b([^>]*)\/?>/g)) {
|
||||
const pt = attr(hm[1], "parttype");
|
||||
if (pt) hiddenLayers.push({ partType: pt });
|
||||
}
|
||||
sets.push({
|
||||
id: num(attr(sa, "id")),
|
||||
gender: attr(sa, "gender") ?? "U",
|
||||
club: num(attr(sa, "club")),
|
||||
colorable: bool(attr(sa, "colorable")),
|
||||
selectable: bool(attr(sa, "selectable")),
|
||||
preselectable: bool(attr(sa, "preselectable")),
|
||||
sellable: bool(attr(sa, "sellable")),
|
||||
parts,
|
||||
hiddenLayers: hiddenLayers.length ? hiddenLayers : undefined,
|
||||
});
|
||||
}
|
||||
setTypes.push({
|
||||
type: attr(a, "type") ?? "",
|
||||
paletteId: num(attr(a, "paletteid")),
|
||||
mandM0: bool(attr(a, "mand_m_0")),
|
||||
mandF0: bool(attr(a, "mand_f_0")),
|
||||
mandM1: bool(attr(a, "mand_m_1")),
|
||||
mandF1: bool(attr(a, "mand_f_1")),
|
||||
sets,
|
||||
});
|
||||
}
|
||||
|
||||
const data = { palettes, setTypes };
|
||||
fdCache = { data, ts: now };
|
||||
return data;
|
||||
const data = { palettes, setTypes };
|
||||
fdCache = { data, ts: now };
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function fetchOfficialFigureMap(): Promise<OfficialLibrary[]> {
|
||||
const now = Date.now();
|
||||
if (fmCache && now - fmCache.ts < CACHE_TTL) return fmCache.libs;
|
||||
const override = ((await siteSettings.get("figure_swf_base_url", "")) ?? "").trim();
|
||||
const base = override ? `${override.replace(/\/+$/, "")}/` : await resolveGordonBuildUrl();
|
||||
const res = await fetch(`${base}figuremap.xml`, { signal: AbortSignal.timeout(30000) });
|
||||
if (!res.ok) throw new Error(`figuremap fetch failed: ${res.status}`);
|
||||
const xml = await res.text();
|
||||
const libs: OfficialLibrary[] = [];
|
||||
for (const lm of xml.matchAll(/<lib\b([^>]*)>([\s\S]*?)<\/lib>/g)) {
|
||||
const parts: { id: number; type: string }[] = [];
|
||||
for (const pm of lm[2].matchAll(/<part\b([^>]*)\/?>/g)) {
|
||||
parts.push({ id: num(attr(pm[1], "id")), type: attr(pm[1], "type") ?? "" });
|
||||
}
|
||||
libs.push({ id: attr(lm[1], "id") ?? "", revision: num(attr(lm[1], "revision")), parts });
|
||||
}
|
||||
fmCache = { libs, ts: now };
|
||||
return libs;
|
||||
const now = Date.now();
|
||||
if (fmCache && now - fmCache.ts < CACHE_TTL) return fmCache.libs;
|
||||
const override = (
|
||||
(await siteSettings.get("figure_swf_base_url", "")) ?? ""
|
||||
).trim();
|
||||
const base = override
|
||||
? `${override.replace(/\/+$/, "")}/`
|
||||
: await resolveGordonBuildUrl();
|
||||
const res = await fetch(`${base}figuremap.xml`, {
|
||||
signal: AbortSignal.timeout(30000),
|
||||
});
|
||||
if (!res.ok) throw new Error(`figuremap fetch failed: ${res.status}`);
|
||||
const xml = await res.text();
|
||||
const libs: OfficialLibrary[] = [];
|
||||
for (const lm of xml.matchAll(/<lib\b([^>]*)>([\s\S]*?)<\/lib>/g)) {
|
||||
const parts: { id: number; type: string }[] = [];
|
||||
for (const pm of lm[2].matchAll(/<part\b([^>]*)\/?>/g)) {
|
||||
parts.push({
|
||||
id: num(attr(pm[1], "id")),
|
||||
type: attr(pm[1], "type") ?? "",
|
||||
});
|
||||
}
|
||||
libs.push({
|
||||
id: attr(lm[1], "id") ?? "",
|
||||
revision: num(attr(lm[1], "revision")),
|
||||
parts,
|
||||
});
|
||||
}
|
||||
fmCache = { libs, ts: now };
|
||||
return libs;
|
||||
}
|
||||
Reference in new issue
Block a user