add project

This commit is contained in:
2026-05-17 14:51:37 -04:00
parent 2f1a0d6020
commit fe8dc317b2
40 changed files with 6137 additions and 0 deletions

98
src/lib/server/scanner.ts Normal file
View File

@@ -0,0 +1,98 @@
import fs from 'node:fs';
import path from 'node:path';
import type Database from 'better-sqlite3';
import { detectPlatform } from '$lib/platform';
import { toSlug, isImageFile } from '$lib/utils';
import { env } from '$env/dynamic/private';
export function scanLibraries(db: Database.Database): void {
const GAMES_PATH = env.GAMES_PATH ?? path.join(process.cwd(), 'games');
for (const library of ['public', 'private'] as const) {
const libPath = path.join(GAMES_PATH, library);
if (!fs.existsSync(libPath)) continue;
let gameFolders: fs.Dirent[];
try {
gameFolders = fs.readdirSync(libPath, { withFileTypes: true }).filter((d) => d.isDirectory());
} catch {
continue;
}
for (const folder of gameFolders) {
const title = folder.name;
const slug = toSlug(title);
const folderPath = path.join(libPath, folder.name);
let entries: fs.Dirent[];
try {
entries = fs.readdirSync(folderPath, { withFileTypes: true });
} catch {
continue;
}
const hasCover = entries.some(
(e) => e.isFile() && /^cover\.(png|jpg|jpeg|webp)$/i.test(e.name)
);
const hasWide = entries.some(
(e) => e.isFile() && /^widecover\.(png|jpg|jpeg|webp)$/i.test(e.name)
);
db.prepare(
`INSERT INTO games (slug, title, library, folder_path, has_cover, has_wide)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(slug) DO UPDATE SET
title = excluded.title,
library = excluded.library,
folder_path = excluded.folder_path,
has_cover = excluded.has_cover,
has_wide = excluded.has_wide,
last_scanned_at = datetime('now')`
).run(slug, title, library, folderPath, hasCover ? 1 : 0, hasWide ? 1 : 0);
const game = db.prepare('SELECT id FROM games WHERE slug = ?').get(slug) as { id: number };
db.prepare('DELETE FROM game_files WHERE game_id = ?').run(game.id);
for (const entry of entries) {
const platform = detectPlatform(entry.name, entry.isDirectory());
if (platform === null) continue;
const isDir = entry.isDirectory() ? 1 : 0;
let fileSize = 0;
if (!isDir) {
try {
fileSize = fs.statSync(path.join(folderPath, entry.name)).size;
} catch {
/* ignore */
}
}
db.prepare(
`INSERT INTO game_files (game_id, filename, rel_path, platform, is_dir, file_size)
VALUES (?, ?, ?, ?, ?, ?)`
).run(game.id, entry.name, entry.name, platform, isDir, fileSize);
}
db.prepare('DELETE FROM screenshots WHERE game_id = ?').run(game.id);
const ssDir = path.join(folderPath, 'screenshots');
if (fs.existsSync(ssDir)) {
const ssFiles = fs.readdirSync(ssDir).filter(isImageFile).sort();
ssFiles.forEach((filename, i) => {
db.prepare(
`INSERT INTO screenshots (game_id, filename, rel_path, sort_order)
VALUES (?, ?, ?, ?)`
).run(game.id, filename, `screenshots/${filename}`, i);
});
}
}
// Remove DB rows for game folders that no longer exist on disk
const existing = db
.prepare('SELECT id, folder_path FROM games WHERE library = ?')
.all(library) as Array<{ id: number; folder_path: string }>;
for (const row of existing) {
if (!fs.existsSync(row.folder_path)) {
db.prepare('DELETE FROM games WHERE id = ?').run(row.id);
}
}
}
}