perf: enable source-specific asset downloads and make nitro/icon URLs optional
CI / check (push) Successful in 26s
CI / release (push) Skipped
CI / deploy (push) Successful in 48s

Import improvements:
- importSingleFurni now accepts sourceSwfBaseUrl, nitroBaseUrl, iconBaseUrl
  params to try source-specific asset downloads before falling back to
  the official Habbo CDN (images.habbo.com)
- Source-specific URL cascade:
  1. Try sourceSwfBaseUrl/hof_furni/{rev}/{name}.swf
  2. Try sourceNitroBaseUrl/{name}.nitro (pre-made nitro bundles)
  3. Fallback to images.habbo.com/dcr/hof_furni/{rev}/{name}.swf
- Same fallback chain for icon downloads
- Clone sources can now have empty nitroBaseUrl/iconBaseUrl (retro
  hotels without nitro support)
- Added VirtualCity source (verified SWF downloads via virtualc.nl/dcr)
- Added sourceSwfBaseUrl field to CloneSource interface
- Both batch and single import routes pass source params through
- Clone POST route accepts sourceSwfBaseUrl, makes nitro/icon optional
- Nitro fallback download tries .nitro files before SWF conversion
This commit is contained in:
openhands committed 2026-08-06 20:19:54 +02:00
1 parent 4c93dc38c6
commit a1775fbf1e
5 files changed
+156 -40

No files matched your search

+25
View File
@@ -8,6 +8,16 @@ export interface CloneSource {
nitroBaseUrl: string;
iconBaseUrl: string;
hotel: string;
/**
* Optional per-source base URL for downloading SWF files.
* When set, the import service will try this URL before falling back
* to the official Habbo CDN (images.habbo.com).
*
* Retro hotels often have their own dcr/hof_furni structure,
* e.g. "https://virtualcity.nl/dcr" →
* "https://virtualcity.nl/dcr/hof_furni/{revision}/{classname}.swf"
*/
sourceSwfBaseUrl?: string;
}
const KEY = "clone_sources";
@@ -124,6 +134,19 @@ export const DEFAULT_SOURCES: CloneSource[] = [
nitroBaseUrl: "https://www.habblet.city/nitro/bundled/furniture",
iconBaseUrl: "https://www.habblet.city/nitro/icons",
hotel: "com",
// Retro hotels may use their own SWF server (Habbo official CDN still has legacy files)
// This field enables per-source SWF/nitro/icon download base URL fallback
// The import service will try: sourceSwfBaseUrl > images.habbo.com
sourceSwfBaseUrl: "https://www.habblet.city/dcr",
},
{
id: "default-virtualcity",
name: "VirtualCity",
furnidataUrl: "https://virtualc.nl/gamedata/furnidata_json/1",
nitroBaseUrl: "https://virtualc.nl/nitro/bundled/furniture",
iconBaseUrl: "https://virtualc.nl/nitro/icons",
hotel: "com",
sourceSwfBaseUrl: "https://virtualc.nl/dcr",
},
];
@@ -149,6 +172,7 @@ export async function upsertSource(
src: Omit<CloneSource, "id"> & { id?: string },
): Promise<CloneSource> {
const sources = await listSources();
const swfBase = src.sourceSwfBaseUrl?.trim().replace(/\/+$/, "");
const entry: CloneSource = {
id: src.id || randomUUID(),
name: src.name.trim(),
@@ -156,6 +180,7 @@ export async function upsertSource(
nitroBaseUrl: src.nitroBaseUrl.trim().replace(/\/+$/, ""),
iconBaseUrl: src.iconBaseUrl.trim().replace(/\/+$/, ""),
hotel: (src.hotel ?? "").trim(),
sourceSwfBaseUrl: swfBase || undefined,
};
const idx = sources.findIndex((s) => s.id === entry.id);
if (idx >= 0) sources[idx] = entry;