206 lines
7.6 KiB
JavaScript
206 lines
7.6 KiB
JavaScript
// Gachabase Data Fetcher, Catalog Sync & Image Engine for GaC-srtool
|
|
// Connects with hsr.gachabase.net to dynamically update characters, lightcones,
|
|
// and high-res assets whenever the game updates or new patches/betas drop.
|
|
|
|
export const GACHABASE_BASE_URL = 'https://hsr.gachabase.net';
|
|
export const GACHABASE_CDN_URL = 'https://cdn.gachabase.net';
|
|
|
|
// Pre-mapped known beta / newest assets from Gachabase CDN
|
|
export const KNOWN_GACHABASE_ASSETS = {
|
|
avatars: {
|
|
1501: 'https://cdn.gachabase.net/hsr/assets/09ee6e2618ac2a0dab2aec8d73b0bb01.png', // Sparxie
|
|
1502: 'https://cdn.gachabase.net/hsr/assets/8ca2530c42791593b72d54a850fce97e.png', // Yao Guang
|
|
1505: 'https://cdn.gachabase.net/hsr/assets/723a90e789fd354fb96b450a90c73e30.png', // Evanescia
|
|
1506: 'https://cdn.gachabase.net/hsr/assets/0cbfb275bfef439b16ba6ff9f848eb3a.png', // Silver Wolf Collab
|
|
1511: 'https://cdn.gachabase.net/hsr/assets/2be49dbbe7cfe8f77187dcfea51d9863.png', // Aeon ★ Aha
|
|
1513: 'https://cdn.gachabase.net/hsr/assets/2fdf5d4fe6eb998064438ffd373ce95f.png', // Aventurine • Waveflair
|
|
},
|
|
avatarSplash: {
|
|
1511: 'https://cdn.gachabase.net/hsr/assets/f8b5630e85022ab8b2761e4e3f614b42.png', // Aha Splash Art (2048x1914)
|
|
1502: 'https://cdn.gachabase.net/hsr/assets/8ca2530c42791593b72d54a850fce97e.png', // Yao Guang
|
|
1501: 'https://cdn.gachabase.net/hsr/assets/09ee6e2618ac2a0dab2aec8d73b0bb01.png', // Sparxie
|
|
},
|
|
lightcones: {
|
|
23065: 'https://cdn.gachabase.net/hsr/assets/500cfc18698932759756f6b3ba4db417.png', // Upon the First Echo of "Aha" Full Art
|
|
},
|
|
lightconeThumbs: {
|
|
23065: 'https://cdn.gachabase.net/hsr/assets/c794417a81e656bfdf1212202230f80f.png', // Aha LC Thumbnail
|
|
}
|
|
};
|
|
|
|
// Global in-memory image cache populated dynamically from Gachabase catalogs
|
|
export const GACHABASE_IMAGE_CACHE = {
|
|
avatars: { ...KNOWN_GACHABASE_ASSETS.avatars },
|
|
avatarSplash: { ...KNOWN_GACHABASE_ASSETS.avatarSplash },
|
|
lightcones: { ...KNOWN_GACHABASE_ASSETS.lightcones },
|
|
lightconeThumbs: { ...KNOWN_GACHABASE_ASSETS.lightconeThumbs }
|
|
};
|
|
|
|
/**
|
|
* Image resolver for avatars
|
|
* Prioritizes local asset, falls back to Gachabase CDN
|
|
*/
|
|
export function getAvatarIconUrl(avatarId) {
|
|
const id = parseInt(avatarId, 10);
|
|
if (GACHABASE_IMAGE_CACHE.avatars[id]) {
|
|
return GACHABASE_IMAGE_CACHE.avatars[id];
|
|
}
|
|
if (KNOWN_GACHABASE_ASSETS.avatars[id]) {
|
|
return KNOWN_GACHABASE_ASSETS.avatars[id];
|
|
}
|
|
return `./assets/avatars/${id}.png`;
|
|
}
|
|
|
|
/**
|
|
* Image resolver for lightcones
|
|
*/
|
|
export function getLightconeArtUrl(lcId) {
|
|
const id = parseInt(lcId, 10);
|
|
if (GACHABASE_IMAGE_CACHE.lightcones[id]) {
|
|
return GACHABASE_IMAGE_CACHE.lightcones[id];
|
|
}
|
|
if (KNOWN_GACHABASE_ASSETS.lightcones[id]) {
|
|
return KNOWN_GACHABASE_ASSETS.lightcones[id];
|
|
}
|
|
if (id === 23065) {
|
|
return './assets/lightcones/23065.png';
|
|
}
|
|
return `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/light_cone_preview/${id}.png`;
|
|
}
|
|
|
|
/**
|
|
* Image resolver for lightcone thumbnails
|
|
*/
|
|
export function getLightconeThumbUrl(lcId) {
|
|
const id = parseInt(lcId, 10);
|
|
if (GACHABASE_IMAGE_CACHE.lightconeThumbs[id]) {
|
|
return GACHABASE_IMAGE_CACHE.lightconeThumbs[id];
|
|
}
|
|
if (KNOWN_GACHABASE_ASSETS.lightconeThumbs[id]) {
|
|
return KNOWN_GACHABASE_ASSETS.lightconeThumbs[id];
|
|
}
|
|
return getLightconeArtUrl(lcId);
|
|
}
|
|
|
|
/**
|
|
* Robust error handler for Avatar Full Portrait / Splash Art
|
|
* Falls back through:
|
|
* 0. jsDelivr StarRailRes character_portrait (2048x2048 splash)
|
|
* 1. jsDelivr StarRailRes character_drawing (transparent full body art)
|
|
* 2. Raw GitHub character_portrait
|
|
* 3. Raw GitHub character_drawing
|
|
* 4. Gachabase splash cache
|
|
* 5. jsDelivr avatar icon (visual fallback rather than blank)
|
|
*/
|
|
export function handleAvatarPortraitError(img, avatarId) {
|
|
if (!img) return;
|
|
const id = parseInt(avatarId, 10);
|
|
const step = parseInt(img.getAttribute('data-err-step') || '0', 10);
|
|
|
|
if (step === 0) {
|
|
img.setAttribute('data-err-step', '1');
|
|
const portraitUrl = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/character_portrait/${id}.png`;
|
|
if (img.src !== portraitUrl) {
|
|
img.src = portraitUrl;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (step <= 1) {
|
|
img.setAttribute('data-err-step', '2');
|
|
const drawingUrl = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/character_drawing/${id}.png`;
|
|
if (img.src !== drawingUrl) {
|
|
img.src = drawingUrl;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (step <= 2) {
|
|
img.setAttribute('data-err-step', '3');
|
|
const rawPortrait = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/image/character_portrait/${id}.png`;
|
|
if (img.src !== rawPortrait) {
|
|
img.src = rawPortrait;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (step <= 3) {
|
|
img.setAttribute('data-err-step', '4');
|
|
const gachaSplash = GACHABASE_IMAGE_CACHE.avatarSplash?.[id] || KNOWN_GACHABASE_ASSETS.avatarSplash?.[id];
|
|
if (gachaSplash && img.src !== gachaSplash) {
|
|
img.src = gachaSplash;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Step 4+: Visual fallback to avatar icon rather than ever leaving the portrait black/blank
|
|
img.setAttribute('data-err-step', '5');
|
|
const iconUrl = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/avatar/${id}.png`;
|
|
if (img.src !== iconUrl) {
|
|
img.src = iconUrl;
|
|
img.style.objectFit = 'contain';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Robust error handler for Avatar images
|
|
* Falls back through: jsDelivr StarRailRes -> Raw GitHub -> Gachabase CDN -> Letter Placeholder
|
|
*/
|
|
export function handleAvatarImgError(img, avatarId) {
|
|
if (!img) return;
|
|
const id = parseInt(avatarId, 10);
|
|
const step = parseInt(img.getAttribute('data-err-step') || '0', 10);
|
|
|
|
if (step === 0) {
|
|
img.setAttribute('data-err-step', '1');
|
|
img.src = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/icon/avatar/${id}.png`;
|
|
} else if (step === 1) {
|
|
img.setAttribute('data-err-step', '2');
|
|
img.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/icon/avatar/${id}.png`;
|
|
} else if (step === 2) {
|
|
img.setAttribute('data-err-step', '3');
|
|
const gachaUrl = GACHABASE_IMAGE_CACHE.avatars[id] || KNOWN_GACHABASE_ASSETS.avatars[id];
|
|
if (gachaUrl && img.src !== gachaUrl) {
|
|
img.src = gachaUrl;
|
|
} else {
|
|
img.style.display = 'none';
|
|
if (img.nextElementSibling && img.nextElementSibling.classList.contains('avatar-icon-placeholder')) {
|
|
img.nextElementSibling.style.display = 'flex';
|
|
}
|
|
}
|
|
} else {
|
|
img.style.display = 'none';
|
|
if (img.nextElementSibling && img.nextElementSibling.classList.contains('avatar-icon-placeholder')) {
|
|
img.nextElementSibling.style.display = 'flex';
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Robust error handler for Lightcone images
|
|
* Falls back through: jsDelivr StarRailRes -> Raw GitHub -> Gachabase CDN -> Default Logo
|
|
*/
|
|
export function handleLcImgError(img, lcId) {
|
|
if (!img) return;
|
|
const id = parseInt(lcId, 10);
|
|
const step = parseInt(img.getAttribute('data-err-step') || '0', 10);
|
|
|
|
if (step === 0) {
|
|
img.setAttribute('data-err-step', '1');
|
|
img.src = `https://cdn.jsdelivr.net/gh/Mar-7th/StarRailRes@master/image/light_cone_preview/${id}.png`;
|
|
} else if (step === 1) {
|
|
img.setAttribute('data-err-step', '2');
|
|
img.src = `https://raw.githubusercontent.com/Mar-7th/StarRailRes/master/image/light_cone_preview/${id}.png`;
|
|
} else if (step === 2) {
|
|
img.setAttribute('data-err-step', '3');
|
|
const gachaUrl = GACHABASE_IMAGE_CACHE.lightcones[id] || KNOWN_GACHABASE_ASSETS.lightcones[id] || GACHABASE_IMAGE_CACHE.lightconeThumbs[id] || KNOWN_GACHABASE_ASSETS.lightconeThumbs[id];
|
|
if (gachaUrl && img.src !== gachaUrl) {
|
|
img.src = gachaUrl;
|
|
} else {
|
|
img.src = './assets/logo.png';
|
|
}
|
|
} else {
|
|
img.src = './assets/logo.png';
|
|
}
|
|
}
|