add thumbnail generation

This commit is contained in:
2026-03-25 16:59:09 -04:00
parent 90528c4768
commit bf54b45fa1
9 changed files with 317 additions and 39 deletions

View File

@@ -0,0 +1,62 @@
import { NextRequest, NextResponse } from 'next/server'
import fs from 'fs'
import path from 'path'
import { getLibrary, resolveLibraryRoot, resolveAndJail } from '@/lib/libraries'
import { getThumbnailPath } from '@/lib/thumbnails'
const VIDEO_EXTENSIONS = new Set(['.mp4', '.mov', '.mkv', '.avi', '.webm', '.m4v'])
const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp', '.tiff', '.tif'])
function getMediaType(filePath: string): 'image' | 'video' | null {
const ext = path.extname(filePath).toLowerCase()
if (IMAGE_EXTENSIONS.has(ext)) return 'image'
if (VIDEO_EXTENSIONS.has(ext)) return 'video'
return null
}
export async function GET(request: NextRequest) {
const { searchParams } = request.nextUrl
const libraryId = searchParams.get('libraryId')
const subpath = searchParams.get('path')
if (!libraryId || !subpath) {
return NextResponse.json({ error: 'Missing libraryId or path' }, { status: 400 })
}
const library = getLibrary(libraryId)
if (!library) {
return NextResponse.json({ error: 'Library not found' }, { status: 404 })
}
const root = resolveLibraryRoot(library)
let filePath: string
try {
filePath = resolveAndJail(root, subpath)
} catch {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
}
const mediaType = getMediaType(filePath)
if (!mediaType) {
return NextResponse.json({ error: 'Thumbnails are only supported for image and video files' }, { status: 400 })
}
try {
const thumbnailPath = await getThumbnailPath(filePath, libraryId, mediaType)
const stat = fs.statSync(thumbnailPath)
const stream = fs.createReadStream(thumbnailPath)
return new NextResponse(stream as unknown as ReadableStream, {
status: 200,
headers: {
'Content-Type': 'image/jpeg',
'Content-Length': String(stat.size),
'Cache-Control': 'public, max-age=86400',
},
})
} catch (err) {
console.error(`Thumbnail generation failed for ${filePath}:`, err)
return new NextResponse(null, { status: 404 })
}
}