diff --git a/viewer-frontend/app/api/photos/[id]/image/route.ts b/viewer-frontend/app/api/photos/[id]/image/route.ts
index 28e7e3f..e73e78e 100644
--- a/viewer-frontend/app/api/photos/[id]/image/route.ts
+++ b/viewer-frontend/app/api/photos/[id]/image/route.ts
@@ -4,7 +4,6 @@ import { readFile } from 'fs/promises';
import { createReadStream } from 'fs';
import { existsSync, statSync } from 'fs';
import path from 'path';
-import { getVideoThumbnail } from '@/lib/video-thumbnail';
// Conditionally import sharp - handle case where libvips is not installed
let sharp: any = null;
@@ -168,98 +167,35 @@ export async function GET(
// Handle video thumbnail request
if (thumbnail && mediaType === 'video') {
- // Check if video path is a URL (can't generate thumbnail for remote videos)
- const isVideoUrl = filePath.startsWith('http://') || filePath.startsWith('https://');
-
- if (isVideoUrl) {
- // For remote videos, we can't generate thumbnails locally
- // Return a placeholder
- console.warn(`Cannot generate thumbnail for remote video URL: ${filePath}`);
- try {
- if (!sharp) throw new Error('Sharp not available');
- const placeholderSvg = `
-
- `.trim();
-
- const placeholderBuffer = await sharp(Buffer.from(placeholderSvg))
- .png()
- .toBuffer();
-
- return new NextResponse(placeholderBuffer as unknown as BodyInit, {
- headers: {
- 'Content-Type': 'image/png',
- 'Cache-Control': 'public, max-age=60',
- },
- });
- } catch (error) {
- console.error('Error generating placeholder for remote video:', error);
- const minimalPng = Buffer.from(
- 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
- 'base64'
- );
- return new NextResponse(minimalPng as unknown as BodyInit, {
- headers: {
- 'Content-Type': 'image/png',
- 'Cache-Control': 'public, max-age=60',
- },
- });
- }
- }
-
+ const backendBaseUrl = process.env.BACKEND_BASE_URL || 'http://127.0.0.1:8000';
+
try {
- const thumbnailBuffer = await getVideoThumbnail(filePath);
- if (thumbnailBuffer && thumbnailBuffer.length > 0) {
- return new NextResponse(thumbnailBuffer as unknown as BodyInit, {
- headers: {
- 'Content-Type': 'image/jpeg',
- 'Cache-Control': 'public, max-age=31536000, immutable',
- },
- });
- }
- console.error(`Thumbnail generation returned empty buffer for video: ${filePath}`);
- } catch (error) {
- console.error(`Error generating thumbnail for video ${filePath}:`, error);
- }
-
- // Fallback: return a placeholder image (gray box with play icon)
- // Generate a PNG placeholder using sharp for better compatibility
- try {
- if (!sharp) throw new Error('Sharp not available');
- const placeholderSvg = `
-
- `.trim();
-
- const placeholderBuffer = await sharp(Buffer.from(placeholderSvg))
- .png()
- .toBuffer();
-
- return new NextResponse(placeholderBuffer as unknown as BodyInit, {
- headers: {
- 'Content-Type': 'image/png',
- 'Cache-Control': 'public, max-age=60', // Short cache for placeholder
- },
- });
- } catch (error) {
- console.error('Error generating placeholder:', error);
- // Ultimate fallback: return a minimal 1x1 PNG
- const minimalPng = Buffer.from(
- 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
- 'base64'
+ const backendResponse = await fetch(
+ `${backendBaseUrl}/api/v1/videos/${photoId}/thumbnail`,
+ { headers: { Accept: 'image/*' } }
);
- return new NextResponse(minimalPng as unknown as BodyInit, {
+
+ if (!backendResponse.ok) {
+ console.error(
+ `Backend thumbnail fetch failed for video ${photoId}: ` +
+ `${backendResponse.status} ${backendResponse.statusText}`
+ );
+ return new NextResponse('Failed to fetch video thumbnail', { status: 502 });
+ }
+
+ const arrayBuffer = await backendResponse.arrayBuffer();
+ const contentType = backendResponse.headers.get('content-type') || 'image/jpeg';
+
+ return new NextResponse(arrayBuffer as unknown as BodyInit, {
headers: {
- 'Content-Type': 'image/png',
- 'Cache-Control': 'public, max-age=60',
+ 'Content-Type': contentType,
+ // Keep caching modest: backend thumbnails can be regenerated/updated.
+ 'Cache-Control': 'public, max-age=86400',
},
});
+ } catch (error) {
+ console.error(`Error proxying backend thumbnail for video ${photoId}:`, error);
+ return new NextResponse('Failed to fetch video thumbnail', { status: 502 });
}
}