add more logs
This commit is contained in:
@@ -14,11 +14,13 @@ export function getDb(): Database.Database {
|
|||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[db] opening database at ${DB_PATH}`);
|
||||||
const db = new Database(DB_PATH);
|
const db = new Database(DB_PATH);
|
||||||
db.pragma('journal_mode = WAL');
|
db.pragma('journal_mode = WAL');
|
||||||
db.pragma('foreign_keys = ON');
|
db.pragma('foreign_keys = ON');
|
||||||
initSchema(db);
|
initSchema(db);
|
||||||
_db = db;
|
_db = db;
|
||||||
|
console.log('[db] database ready');
|
||||||
return _db;
|
return _db;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,8 +83,10 @@ function initSchema(db: Database.Database): void {
|
|||||||
|
|
||||||
const cols = db.prepare('PRAGMA table_info(games)').all() as Array<{ name: string }>;
|
const cols = db.prepare('PRAGMA table_info(games)').all() as Array<{ name: string }>;
|
||||||
if (!cols.some((c) => c.name === 'series_id')) {
|
if (!cols.some((c) => c.name === 'series_id')) {
|
||||||
|
console.log('[db] migrating: adding series_id column to games');
|
||||||
db.exec(
|
db.exec(
|
||||||
'ALTER TABLE games ADD COLUMN series_id INTEGER DEFAULT NULL REFERENCES series(id) ON DELETE SET NULL'
|
'ALTER TABLE games ADD COLUMN series_id INTEGER DEFAULT NULL REFERENCES series(id) ON DELETE SET NULL'
|
||||||
);
|
);
|
||||||
|
console.log('[db] migration complete');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,11 +30,13 @@ function scanGameFolder(
|
|||||||
seriesId: number | null
|
seriesId: number | null
|
||||||
): void {
|
): void {
|
||||||
const slug = toSlug(title);
|
const slug = toSlug(title);
|
||||||
|
console.log(`[scanner] game: "${title}" → slug="${slug}" series_id=${seriesId} path=${folderPath}`);
|
||||||
|
|
||||||
let entries: fs.Dirent[];
|
let entries: fs.Dirent[];
|
||||||
try {
|
try {
|
||||||
entries = fs.readdirSync(folderPath, { withFileTypes: true });
|
entries = fs.readdirSync(folderPath, { withFileTypes: true });
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
console.error(`[scanner] failed to read game folder "${folderPath}":`, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +47,7 @@ function scanGameFolder(
|
|||||||
(e) => e.isFile() && /^widecover\.(png|jpg|jpeg|webp)$/i.test(e.name)
|
(e) => e.isFile() && /^widecover\.(png|jpg|jpeg|webp)$/i.test(e.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
db.prepare(
|
db.prepare(
|
||||||
`INSERT INTO games (slug, title, library, folder_path, has_cover, has_wide, series_id)
|
`INSERT INTO games (slug, title, library, folder_path, has_cover, has_wide, series_id)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
@@ -57,6 +60,10 @@ function scanGameFolder(
|
|||||||
series_id = excluded.series_id,
|
series_id = excluded.series_id,
|
||||||
last_scanned_at = datetime('now')`
|
last_scanned_at = datetime('now')`
|
||||||
).run(slug, title, library, folderPath, hasCover ? 1 : 0, hasWide ? 1 : 0, seriesId);
|
).run(slug, title, library, folderPath, hasCover ? 1 : 0, hasWide ? 1 : 0, seriesId);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[scanner] DB insert failed for game "${title}" (slug="${slug}"):`, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const game = db.prepare('SELECT id FROM games WHERE slug = ?').get(slug) as { id: number };
|
const game = db.prepare('SELECT id FROM games WHERE slug = ?').get(slug) as { id: number };
|
||||||
|
|
||||||
@@ -96,19 +103,27 @@ function scanGameFolder(
|
|||||||
|
|
||||||
export function scanLibraries(db: Database.Database): void {
|
export function scanLibraries(db: Database.Database): void {
|
||||||
const GAMES_PATH = env.GAMES_PATH ?? path.join(process.cwd(), 'games');
|
const GAMES_PATH = env.GAMES_PATH ?? path.join(process.cwd(), 'games');
|
||||||
|
console.log(`[scanner] starting scan, GAMES_PATH=${GAMES_PATH}`);
|
||||||
|
|
||||||
for (const library of ['public', 'private'] as const) {
|
for (const library of ['public', 'private'] as const) {
|
||||||
const libPath = path.join(GAMES_PATH, library);
|
const libPath = path.join(GAMES_PATH, library);
|
||||||
if (!fs.existsSync(libPath)) continue;
|
if (!fs.existsSync(libPath)) {
|
||||||
|
console.log(`[scanner] library path not found, skipping: ${libPath}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let topFolders: fs.Dirent[];
|
let topFolders: fs.Dirent[];
|
||||||
try {
|
try {
|
||||||
topFolders = fs
|
topFolders = fs
|
||||||
.readdirSync(libPath, { withFileTypes: true })
|
.readdirSync(libPath, { withFileTypes: true })
|
||||||
.filter((d) => d.isDirectory());
|
.filter((d) => d.isDirectory());
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
console.error(`[scanner] failed to read library "${libPath}":`, err);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[scanner] ${library}: found ${topFolders.length} top-level folder(s)`);
|
||||||
|
|
||||||
for (const folder of topFolders) {
|
for (const folder of topFolders) {
|
||||||
const title = folder.name;
|
const title = folder.name;
|
||||||
const folderPath = path.join(libPath, folder.name);
|
const folderPath = path.join(libPath, folder.name);
|
||||||
@@ -116,16 +131,20 @@ export function scanLibraries(db: Database.Database): void {
|
|||||||
let entries: fs.Dirent[];
|
let entries: fs.Dirent[];
|
||||||
try {
|
try {
|
||||||
entries = fs.readdirSync(folderPath, { withFileTypes: true });
|
entries = fs.readdirSync(folderPath, { withFileTypes: true });
|
||||||
} catch {
|
} catch (err) {
|
||||||
|
console.error(`[scanner] failed to read folder "${folderPath}":`, err);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isSeriesFolder(entries)) {
|
if (isSeriesFolder(entries)) {
|
||||||
const seriesSlug = toSlug(title);
|
const seriesSlug = toSlug(title);
|
||||||
|
console.log(`[scanner] series: "${title}" → slug="${seriesSlug}"`);
|
||||||
|
|
||||||
const hasCover = entries.some(
|
const hasCover = entries.some(
|
||||||
(e) => e.isFile() && /^cover\.(png|jpg|jpeg|webp)$/i.test(e.name)
|
(e) => e.isFile() && /^cover\.(png|jpg|jpeg|webp)$/i.test(e.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
db.prepare(
|
db.prepare(
|
||||||
`INSERT INTO series (slug, title, library, folder_path, has_cover)
|
`INSERT INTO series (slug, title, library, folder_path, has_cover)
|
||||||
VALUES (?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?)
|
||||||
@@ -136,19 +155,24 @@ export function scanLibraries(db: Database.Database): void {
|
|||||||
has_cover = excluded.has_cover,
|
has_cover = excluded.has_cover,
|
||||||
last_scanned_at = datetime('now')`
|
last_scanned_at = datetime('now')`
|
||||||
).run(seriesSlug, title, library, folderPath, hasCover ? 1 : 0);
|
).run(seriesSlug, title, library, folderPath, hasCover ? 1 : 0);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[scanner] DB insert failed for series "${title}":`, err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const seriesRow = db
|
const seriesRow = db
|
||||||
.prepare('SELECT id FROM series WHERE slug = ?')
|
.prepare('SELECT id FROM series WHERE slug = ?')
|
||||||
.get(seriesSlug) as { id: number };
|
.get(seriesSlug) as { id: number };
|
||||||
|
|
||||||
for (const entry of entries) {
|
const gameSubfolders = entries.filter(
|
||||||
if (
|
(e) =>
|
||||||
!entry.isDirectory() ||
|
e.isDirectory() &&
|
||||||
entry.name.toLowerCase().endsWith('.app') ||
|
!e.name.toLowerCase().endsWith('.app') &&
|
||||||
entry.name.toLowerCase() === 'screenshots'
|
e.name.toLowerCase() !== 'screenshots'
|
||||||
)
|
);
|
||||||
continue;
|
console.log(`[scanner] → ${gameSubfolders.length} game(s) in series`);
|
||||||
|
|
||||||
|
for (const entry of gameSubfolders) {
|
||||||
const gameFolderPath = path.join(folderPath, entry.name);
|
const gameFolderPath = path.join(folderPath, entry.name);
|
||||||
scanGameFolder(db, gameFolderPath, entry.name, library, seriesRow.id);
|
scanGameFolder(db, gameFolderPath, entry.name, library, seriesRow.id);
|
||||||
}
|
}
|
||||||
@@ -163,6 +187,7 @@ export function scanLibraries(db: Database.Database): void {
|
|||||||
.all(library) as Array<{ id: number; folder_path: string }>;
|
.all(library) as Array<{ id: number; folder_path: string }>;
|
||||||
for (const row of existingGames) {
|
for (const row of existingGames) {
|
||||||
if (!fs.existsSync(row.folder_path)) {
|
if (!fs.existsSync(row.folder_path)) {
|
||||||
|
console.log(`[scanner] removing stale game: ${row.folder_path}`);
|
||||||
db.prepare('DELETE FROM games WHERE id = ?').run(row.id);
|
db.prepare('DELETE FROM games WHERE id = ?').run(row.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,8 +198,13 @@ export function scanLibraries(db: Database.Database): void {
|
|||||||
.all(library) as Array<{ id: number; folder_path: string }>;
|
.all(library) as Array<{ id: number; folder_path: string }>;
|
||||||
for (const row of existingSeries) {
|
for (const row of existingSeries) {
|
||||||
if (!fs.existsSync(row.folder_path)) {
|
if (!fs.existsSync(row.folder_path)) {
|
||||||
|
console.log(`[scanner] removing stale series: ${row.folder_path}`);
|
||||||
db.prepare('DELETE FROM series WHERE id = ?').run(row.id);
|
db.prepare('DELETE FROM series WHERE id = ?').run(row.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gameCount = (db.prepare('SELECT COUNT(*) as n FROM games').get() as { n: number }).n;
|
||||||
|
const seriesCount = (db.prepare('SELECT COUNT(*) as n FROM series').get() as { n: number }).n;
|
||||||
|
console.log(`[scanner] done — ${gameCount} game(s), ${seriesCount} series in DB`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user