From d2921764a1f9b868e391d325060f61a0828614cf Mon Sep 17 00:00:00 2001 From: James Date: Sun, 16 Aug 2026 17:28:09 -0400 Subject: [PATCH] feat: resolve images by anime id instead of title slug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit image-sync keys objects by record id now, so getSafeImageUrl no longer has to lowercase, underscore and escape a title to guess the key. The Autocomplete and show views already built `/weeb/` URLs by hand — which only start resolving with this change — so this brings the remaining two call sites in line. Co-Authored-By: Claude Opus 5 (1M context) --- src/services/image-utils.ts | 23 +++++++++-------------- src/views/index.tsx | 4 ++-- src/views/seasonal-linking/index.tsx | 2 +- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/services/image-utils.ts b/src/services/image-utils.ts index 3ff1365..009253e 100644 --- a/src/services/image-utils.ts +++ b/src/services/image-utils.ts @@ -2,21 +2,16 @@ export function getCdnUrl(): string { return 'https://cdn.weeb.vip'; } +/** + * Build a CDN URL for an image key. + * + * `src` is a record id — anime, character, staff. Images used to be keyed by a + * slug derived from the title, which meant lowercasing, underscoring and + * escaping it here to match how the sync services stored it. Ids need none of + * that. + */ export function getSafeImageUrl(src: string, path?: string): string { const cdnUrl = getCdnUrl(); - console.log("src", src, path); - // Replace %20 with + to match React SafeImage behavior exactly - const encodedSrc = src.toLowerCase().replace(/%20/g, "+"); - // Path handling - add trailing slash if path exists const pathPrefix = path ? `${path}/` : ""; - - // Return the properly encoded URL - only single encodeURIComponent like React - return `${cdnUrl}/${pathPrefix}${encodeURIComponent(escapeUri(encodedSrc))}`; -} - -function escapeUri(str: string): string { - return encodeURIComponent(str) - .replace(/[!'()*]/g, char => - '%' + char.charCodeAt(0).toString(16).toUpperCase() - ); + return `${cdnUrl}/${pathPrefix}${encodeURIComponent(src)}`; } diff --git a/src/views/index.tsx b/src/views/index.tsx index cc8fd49..30f4a50 100644 --- a/src/views/index.tsx +++ b/src/views/index.tsx @@ -179,11 +179,11 @@ function Index() {

Selected Anime

{selectedMALAnime.title_en} { currentTarget.onerror = null; currentTarget.src = "/assets/not found.jpg"; diff --git a/src/views/seasonal-linking/index.tsx b/src/views/seasonal-linking/index.tsx index c7a428e..85abd9d 100644 --- a/src/views/seasonal-linking/index.tsx +++ b/src/views/seasonal-linking/index.tsx @@ -144,7 +144,7 @@ function SeasonalLinking() {
{anime.titleEn} {