mirror of
https://github.com/markuryy/shark.git
synced 2025-12-15 04:41:01 +00:00
feat(db): add tracks table and lyric scan caching
This commit is contained in:
@@ -24,6 +24,19 @@ export interface DbAlbum {
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
export interface DbTrack {
|
||||
id: number;
|
||||
path: string;
|
||||
title: string;
|
||||
artist: string;
|
||||
album: string;
|
||||
duration: number;
|
||||
format: string;
|
||||
has_lyrics: number;
|
||||
last_scanned: number | null;
|
||||
created_at: number;
|
||||
}
|
||||
|
||||
let db: Database | null = null;
|
||||
|
||||
/**
|
||||
@@ -231,3 +244,82 @@ export async function getLibraryStats(): Promise<{
|
||||
trackCount: trackResult[0]?.total || 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tracks without lyrics (has_lyrics = 0)
|
||||
*/
|
||||
export async function getTracksWithoutLyrics(): Promise<DbTrack[]> {
|
||||
const database = await initDatabase();
|
||||
const tracks = await database.select<DbTrack[]>(
|
||||
'SELECT * FROM tracks WHERE has_lyrics = 0 ORDER BY artist COLLATE NOCASE, album COLLATE NOCASE, title COLLATE NOCASE'
|
||||
);
|
||||
return tracks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert a track (insert or update)
|
||||
*/
|
||||
export async function upsertTrack(track: {
|
||||
path: string;
|
||||
title: string;
|
||||
artist: string;
|
||||
album: string;
|
||||
duration: number;
|
||||
format: string;
|
||||
has_lyrics: boolean;
|
||||
}): Promise<void> {
|
||||
const database = await initDatabase();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
|
||||
await database.execute(
|
||||
`INSERT INTO tracks (path, title, artist, album, duration, format, has_lyrics, last_scanned)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
ON CONFLICT(path) DO UPDATE SET
|
||||
title = $2,
|
||||
artist = $3,
|
||||
album = $4,
|
||||
duration = $5,
|
||||
format = $6,
|
||||
has_lyrics = $7,
|
||||
last_scanned = $8`,
|
||||
[
|
||||
track.path,
|
||||
track.title,
|
||||
track.artist,
|
||||
track.album,
|
||||
track.duration,
|
||||
track.format,
|
||||
track.has_lyrics ? 1 : 0,
|
||||
now
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last scan timestamp for lyrics
|
||||
*/
|
||||
export async function getLyricsScanTimestamp(): Promise<number | null> {
|
||||
const database = await initDatabase();
|
||||
const result = await database.select<{ last_scanned: number | null }[]>(
|
||||
'SELECT MAX(last_scanned) as last_scanned FROM tracks'
|
||||
);
|
||||
return result[0]?.last_scanned || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete tracks that are no longer in the provided paths
|
||||
*/
|
||||
export async function deleteTracksNotInPaths(paths: string[]): Promise<void> {
|
||||
if (paths.length === 0) {
|
||||
const database = await initDatabase();
|
||||
await database.execute('DELETE FROM tracks');
|
||||
return;
|
||||
}
|
||||
|
||||
const database = await initDatabase();
|
||||
const placeholders = paths.map((_, i) => `$${i + 1}`).join(',');
|
||||
await database.execute(
|
||||
`DELETE FROM tracks WHERE path NOT IN (${placeholders})`,
|
||||
paths
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user