/** * Photo redundancy helpers. * * SkateSpots uploads images to Blossom, which is CONTENT-ADDRESSED: a blob's URL * is `/` (optionally with a file extension). That means a mirror of * the same file on another known host is derivable from the URL alone — no extra * bookkeeping needed — so a dead photo host never has to leave a blank spot. * * We only derive mirrors for Blossom-style hash paths; anything else is left * untouched (an external image URL has no meaningful alternative location). */ /** Hosts the app uploads to, and therefore the hosts we can mirror across. */ export const BLOSSOM_MIRROR_HOSTS = ['blossom.skate-spots.org', 'blossom.primal.net', 'blossom.band']; /** `<64-hex>` or `<64-hex>.` */ const HASH_RE = /^([0-9a-f]{64})(\.[a-z0-9]+)?$/i; /** * Ordered candidate URLs for an image: the original first, then any explicitly * supplied fallbacks, then same-hash mirrors on the other known Blossom hosts. */ export function imageCandidates( src: string | undefined | null, extra: string[] = [], ): string[] { const out: string[] = []; const add = (url?: string | null) => { if (typeof url === 'string' && url && !out.includes(url)) out.push(url); }; add(src); for (const url of extra) add(url); if (!src) return out; try { const parsed = new URL(src); const last = parsed.pathname.split('/').filter(Boolean).pop() ?? ''; const match = HASH_RE.exec(last); if (match) { for (const host of BLOSSOM_MIRROR_HOSTS) { if (host === parsed.hostname) continue; add(`${parsed.protocol}//${host}/${match[1]}${match[2] ?? ''}`); } } } catch { // Relative or malformed URL — nothing to mirror. } return out; }