import fs from 'fs' import path from 'path' import type { Movie } from '@/types' import { getDb } from './db' import { HIDDEN_FILES, VIDEO_EXTENSIONS, fileApiUrl, thumbnailApiUrl, findFile } from './media-utils' function findVideoFile(dir: string): string | null { let entries: string[] try { entries = fs.readdirSync(dir) } catch { return null } return entries.find( (e) => !HIDDEN_FILES.test(e) && VIDEO_EXTENSIONS.has(path.extname(e).toLowerCase()) ) ?? null } export function findNfoFile(dir: string, dirName: string): string | null { // Try {dirName}.nfo first, then movie.nfo, then any .nfo const candidates = [`${dirName}.nfo`, 'movie.nfo'] let entries: string[] try { entries = fs.readdirSync(dir) } catch { return null } for (const candidate of candidates) { const match = entries.find((e) => e.toLowerCase() === candidate.toLowerCase()) if (match) return match } return entries.find((e) => path.extname(e).toLowerCase() === '.nfo') ?? null } export function scanMoviesLibrary(libraryRoot: string, libraryId: string): Movie[] { let dirs: string[] try { dirs = fs .readdirSync(libraryRoot, { withFileTypes: true }) .filter((d) => d.isDirectory() && !HIDDEN_FILES.test(d.name)) .map((d) => d.name) } catch { return [] } const movies: Movie[] = [] for (const dirName of dirs) { const moviePath = path.join(libraryRoot, dirName) const videoFile = findVideoFile(moviePath) if (!videoFile) continue const posterFile = findFile(moviePath, /^(poster|cover|folder)$/i) const backdropFile = findFile(moviePath, /^(backdrop|fanart|background)$/i) const id = encodeURIComponent(dirName) const videoRelPath = path.join(dirName, videoFile) movies.push({ id, title: dirName, year: null, plot: null, rating: null, genres: [], runtime: null, posterUrl: posterFile ? thumbnailApiUrl(libraryId, path.join(dirName, posterFile)) : null, backdropUrl: backdropFile ? fileApiUrl(libraryId, path.join(dirName, backdropFile)) : null, videoPath: videoRelPath, }) } return movies.sort((a, b) => a.title.localeCompare(b.title)) } export function moviesFromDb(libraryId: string): Movie[] { const db = getDb() const rows = db .prepare(`SELECT * FROM media_items WHERE library_id = ? AND item_type = 'movie' ORDER BY title`) .all(libraryId) as Array<{ item_key: string title: string | null year: number | null plot: string | null genres: string | null metadata: string | null file_path: string | null }> return rows.map((row) => { const meta = row.metadata ? JSON.parse(row.metadata) : {} const idPart = row.item_key.split(':movie:')[1] ?? row.item_key return { id: idPart, item_key: row.item_key, title: row.title ?? decodeURIComponent(idPart), year: row.year ?? null, plot: row.plot ?? null, rating: meta.rating ?? null, genres: row.genres ? JSON.parse(row.genres) : [], runtime: meta.runtime ?? null, posterUrl: meta.posterUrl ?? null, backdropUrl: meta.backdropUrl ?? null, videoPath: row.file_path ?? '', manuallyEdited: meta.manuallyEdited === true, } }) }