diff --git a/src/routes/(authed)/tools/adv-upload/+page.svelte b/src/routes/(authed)/tools/adv-upload/+page.svelte index adc7dde..ed74032 100644 --- a/src/routes/(authed)/tools/adv-upload/+page.svelte +++ b/src/routes/(authed)/tools/adv-upload/+page.svelte @@ -25,7 +25,6 @@ const UPLOAD_PROXY_ENDPOINT = '/api/adv-upload'; const LIST_PROXY_ENDPOINT = '/api/adv-list'; const FILE_PROXY_ENDPOINT = '/api/adv-file'; - const THUMB_PROXY_ENDPOINT = '/api/adv-thumb'; const ALLOWED_EXTENSIONS = ['.mp4']; // ───────────────────────────────────────────────────────────────────────── @@ -220,11 +219,6 @@ const { [slot.name]: _drop, ...rest } = videoBlobUrls; videoBlobUrls = rest; } - if (thumbUrls[slot.name]) { - URL.revokeObjectURL(thumbUrls[slot.name]); - const { [slot.name]: _t, ...restThumb } = thumbUrls; - thumbUrls = restThumb; - } refreshServerIndex(); return true; } catch (error) { @@ -351,8 +345,6 @@ let serverFilesByName = $state>({}); // The server video currently open in the preview lightbox (or null). let viewingName = $state(null); - // name -> object URL of the small JPEG poster (thumbnail) for a slot's video. - let thumbUrls = $state>({}); function formatBytes(bytes: number): string { if (!bytes) return '0 MB'; @@ -372,47 +364,11 @@ const map: Record = {}; for (const f of (data.files ?? []) as ServerVideo[]) map[f.name] = f.size; serverFilesByName = map; - // Fetch a tiny JPEG poster for each slot video so the grid shows a - // thumbnail (backend grabs one frame — no full-video download). - autoLoadSlotThumbs(); } catch (error) { logger.error('[Adv] refresh server index error:', error); } } - // Fetch the small JPEG poster for one slot video → object URL for an . - async function loadSlotThumb(name: string) { - if (thumbUrls[name]) return; - try { - const url = `${THUMB_PROXY_ENDPOINT}?country=${encodeURIComponent(selectedCountry)}&filename=${encodeURIComponent(name)}`; - const res = await fetch(url, { headers: await authHeaders() }); - if (!res.ok) return; - const blob = await res.blob(); - thumbUrls = { ...thumbUrls, [name]: URL.createObjectURL(blob) }; - } catch (error) { - logger.error('[Adv] load thumb error:', error); - } - } - - // Load posters for the slot videos on the server (posters are tiny, so a - // larger concurrency pool is fine). - async function autoLoadSlotThumbs() { - const slotNames = new Set(slots.map((s) => s.name)); - const names = Object.keys(serverFilesByName).filter( - (n) => slotNames.has(n) && !thumbUrls[n] - ); - if (names.length === 0) return; - - const CONCURRENCY = 5; - let next = 0; - async function worker() { - while (next < names.length) { - await loadSlotThumb(names[next++]); - } - } - await Promise.all(Array.from({ length: Math.min(CONCURRENCY, names.length) }, worker)); - } - // Open the current server video for a slot in the lightbox. async function viewCurrent(name: string) { await playServerVideo(name); // loads the blob if not already @@ -502,7 +458,6 @@ return () => { for (const s of slots) if (s.preview) URL.revokeObjectURL(s.preview); for (const url of Object.values(videoBlobUrls)) URL.revokeObjectURL(url); - for (const url of Object.values(thumbUrls)) URL.revokeObjectURL(url); }; }); @@ -656,32 +611,6 @@ {/if} - {:else if thumbUrls[slot.name]} - - {:else} {/if} diff --git a/src/routes/api/adv-thumb/+server.ts b/src/routes/api/adv-thumb/+server.ts deleted file mode 100644 index b7e6b69..0000000 --- a/src/routes/api/adv-thumb/+server.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { logger } from '$lib/core/utils/logger'; -import { error } from '@sveltejs/kit'; -import type { RequestHandler } from './$types'; -import { env } from '$env/dynamic/public'; -import { verifyAuthToken } from '$lib/server/auth'; - -// Adv videos are served by the same taobin_image service as menu images. -const ADV_API_BASE = env.PUBLIC_POST_IMAGE; - -// Return a small JPEG poster (single frame) for an adv video so the slot grid can -// show a thumbnail without downloading the whole mp4. The frontend fetches this -// with a Bearer token and turns the response into a Blob/object URL for an . -export const GET: RequestHandler = async ({ request, url }) => { - try { - await verifyAuthToken(request); - - const country = url.searchParams.get('country'); - const filename = url.searchParams.get('filename'); - if (!country || !filename) { - throw error(400, 'Missing country or filename'); - } - - // POST (not GET): the kong gateway only routes POST to the taobin-image service. - const endpoint = `${ADV_API_BASE}/adv/thumb/${encodeURIComponent(country)}/${encodeURIComponent(filename)}`; - const response = await fetch(endpoint, { method: 'POST' }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({ detail: response.statusText })); - throw error(response.status, errorData.detail || 'Failed to fetch thumbnail'); - } - - return new Response(response.body, { - status: 200, - headers: { - 'Content-Type': response.headers.get('Content-Type') || 'image/jpeg', - 'Cache-Control': 'no-store' - } - }); - } catch (err) { - logger.error('[Adv Thumb Proxy] Error:', err); - if (err && typeof err === 'object' && 'status' in err) { - throw err; - } - throw error(500, err instanceof Error ? err.message : 'Internal server error'); - } -};