import api from './api'; export interface ZoneramaPhoto { id: string; page_url: string; image_1500?: string; title?: string; } export interface ZoneramaAlbumResp { album: { id?: string; title?: string; url?: string }; photos: ZoneramaPhoto[]; } export interface ZoneramaPickPayload { id: string; album_id?: string; album_url: string; page_url?: string; image_url: string; title?: string; } export async function getZoneramaAlbum(link: string, opts: { photo_limit?: number; rendered?: boolean } = {}): Promise { const params: any = { link }; if (typeof opts.photo_limit === 'number') params.photo_limit = String(opts.photo_limit); if (typeof opts.rendered === 'boolean') params.rendered = String(opts.rendered); const res = await api.get('/zonerama/album', { params }); return res.data as any; } export async function getZoneramaPicks(): Promise { const res = await api.get('/zonerama/picks'); return Array.isArray(res.data) ? res.data : [] as any; } export async function putZoneramaPick(payload: ZoneramaPickPayload): Promise<{ ok: boolean; count: number }> { const res = await api.post<{ ok: boolean; count: number }>('/admin/zonerama/pick', payload); return res.data; } export interface ZoneramaAlbumData { id: string; title: string; url: string; date: string; photos_count: number; views_count?: number; photos: ZoneramaPhoto[]; fetched_at?: string; } export async function saveAlbumToCache(albumLink: string, photoLimit: number = 50): Promise { const res = await api.post('/admin/zonerama/save-album', { link: albumLink, photo_limit: photoLimit }); return res.data; } // Helper to read the flat manifest produced by the prefetcher for fast grid rendering export async function getZoneramaManifest(): Promise> { const apiUrl = process.env.REACT_APP_API_URL || 'http://localhost:8080/api/v1'; const origin = new URL(apiUrl).origin; // New unified path for prefetched Zonerama items const url = `${origin}/cache/prefetch/zonerama_flat.json`; const res = await fetch(url, { cache: 'no-cache' }); if (!res.ok) return []; try { const json = await res.json(); if (Array.isArray(json)) return json as any; return []; } catch { return []; } } // More robust loader that tries multiple sources for prefetched items export async function getZoneramaManifestWithFallbacks(): Promise> { // 1) Try the main manifest const primary = await getZoneramaManifest(); if (primary && primary.length > 0) return primary; const apiUrl = process.env.REACT_APP_API_URL || 'http://localhost:8080/api/v1'; const origin = new URL(apiUrl).origin; // 1b) Backward-compat removed - old path no longer used to avoid 404 errors // 2) Try unified controller JSON (one JSON to control all prefetched albums) try { const ctrlUrl = `${origin}/cache/prefetch/zonerama_albums.json`; const resCtrl = await fetch(ctrlUrl, { cache: 'no-cache' }); if (resCtrl.ok) { const json = await resCtrl.json(); // Expect shape: { albums: [{ id, page_url, cover_local, cover_src, items: [...] }], items?: [...] } const items = Array.isArray(json?.items) ? json.items : []; const flatFromCtrl = items.map((it: any) => ({ id: String(it.id || it.photo_id || ''), album_id: String(it.album_id || ''), src: String(it.src || it.image_url || ''), local: String(it.local || it.local_url || it.image_local || ''), page_url: String(it.page_url || it.url || ''), })); if (flatFromCtrl.length > 0) return flatFromCtrl; } } catch { /* ignore */ } // 3) Fall back to picks endpoint if available (admin saves picks) try { const picks = await getZoneramaPicks(); const mapped = picks.map((p) => ({ id: String(p.id), album_id: String(p.album_id || ''), src: String(p.image_url), local: String(p.image_url), page_url: String(p.page_url || p.album_url || ''), })); return mapped; } catch { /* ignore */ } return []; }