This repository has been archived on 2026-06-15. You can view files and clone it, but cannot push or open issues or pull requests.
Files
MediaLore/src/lib/movies.ts
2026-04-11 18:33:03 -04:00

114 lines
3.3 KiB
TypeScript

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,
}
})
}