"Remove from library" deletes the audio_files row and records the path as excluded, so the next scan does not import it again. Without the exclusion the operation undoes itself on the next scan, which is worse than not having it at all; the file on disk is never touched, which is the promise the confirmation copy will make. The soft scan compares the number of audio files on disk against the number of rows, so both walks now skip excluded paths — otherwise the two counts disagree forever and every launch queues a full scan of the whole library. A full rescan clears the exclusions, which is the only way back for a path removed by mistake until there is a UI for it.
350 lines
12 KiB
SQL
350 lines
12 KiB
SQL
-- name: CreateAudioFile :one
|
|
INSERT INTO audio_files (file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
RETURNING *;
|
|
|
|
-- name: CreateAudioFileWithGroupKey :one
|
|
INSERT INTO audio_files (
|
|
file_path, length_milliseconds, file_type_id, recording_id,
|
|
sample_rate, bit_depth, channels, bitrate, file_size, basename,
|
|
library_id, group_key, tag_status, modified_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
RETURNING *;
|
|
|
|
-- name: GetAudioFileGroupKey :one
|
|
SELECT group_key FROM audio_files
|
|
WHERE id = ? LIMIT 1;
|
|
|
|
-- name: SetAudioFileGroupKey :exec
|
|
UPDATE audio_files SET group_key = ? WHERE id = ?;
|
|
|
|
-- name: GetAudioFile :one
|
|
SELECT * FROM audio_files
|
|
WHERE id = ? LIMIT 1;
|
|
|
|
-- name: GetAudioFileByPath :one
|
|
SELECT * FROM audio_files
|
|
WHERE file_path = ? LIMIT 1;
|
|
|
|
-- name: UpdateAudioFile :exec
|
|
UPDATE audio_files
|
|
SET file_path = ?, length_milliseconds = ?, file_type_id = ?, recording_id = ?, sample_rate = ?, bit_depth = ?, channels = ?, bitrate = ?, file_size = ?, basename = ?
|
|
WHERE id = ?;
|
|
|
|
-- name: UpdateAudioFileRecording :exec
|
|
UPDATE audio_files
|
|
SET recording_id = ?, sample_rate = ?, bit_depth = ?, channels = ?, bitrate = ?, file_size = ?, length_milliseconds = ?, modified_at = ?
|
|
WHERE id = ?;
|
|
|
|
-- name: UpdateAudioFileStat :exec
|
|
-- Records the on-disk mtime/size without re-reading tags. Used to
|
|
-- backfill the staleness baseline for files the scan skipped, and to
|
|
-- re-baseline after YellowJacket's own tag writer rewrites a file.
|
|
UPDATE audio_files
|
|
SET modified_at = ?, file_size = ?
|
|
WHERE id = ?;
|
|
|
|
-- name: GetLibraryMaxModifiedAt :one
|
|
-- Newest recorded mtime in a library, for the startup soft scan. 0 when
|
|
-- the library is empty or no row has a baseline yet.
|
|
SELECT CAST(COALESCE(MAX(modified_at), 0) AS INTEGER) FROM audio_files
|
|
WHERE library_id = ?;
|
|
|
|
-- name: DeleteAudioFile :exec
|
|
DELETE FROM audio_files
|
|
WHERE id = ?;
|
|
|
|
-- name: CountAudioFiles :one
|
|
SELECT count(*) FROM audio_files;
|
|
|
|
-- name: GetRandomAudioFilePath :one
|
|
SELECT file_path FROM audio_files
|
|
ORDER BY RANDOM()
|
|
LIMIT 1;
|
|
|
|
-- name: GetAllAudioFiles :many
|
|
SELECT * FROM audio_files;
|
|
|
|
-- name: GetAllAudioFilePaths :many
|
|
SELECT id, file_path FROM audio_files;
|
|
|
|
-- name: GetAudioFilesNeedingMetadata :many
|
|
SELECT * FROM audio_files
|
|
WHERE recording_id = 0;
|
|
|
|
-- name: GetAllAudioFilesWithArtist :many
|
|
SELECT
|
|
af.id,
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
af.file_type_id,
|
|
af.recording_id,
|
|
COALESCE(ac.text, '') AS artist_name,
|
|
COALESCE(r.name, '') AS title
|
|
FROM audio_files af
|
|
JOIN recordings r ON af.recording_id = r.id
|
|
JOIN artist_credit ac ON r.artist_credit_id = ac.id;
|
|
|
|
-- name: GetTrackMetadataByPath :one
|
|
SELECT
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
COALESCE(r.name, '') AS title,
|
|
COALESCE(ac.text, '') AS artist,
|
|
COALESCE(rg.name, '') AS album,
|
|
COALESCE(ca.file_path, '') AS cover_art_path,
|
|
COALESCE(a.mbid, '') AS artist_mbid,
|
|
COALESCE(rg.mbid, '') AS release_group_mbid,
|
|
COALESCE(r.mbid, '') AS recording_mbid
|
|
FROM audio_files af
|
|
LEFT JOIN recordings r ON af.recording_id = r.id
|
|
LEFT JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
|
LEFT JOIN artist_credit_artist aca ON aca.credit_id = ac.id
|
|
LEFT JOIN artists a ON a.id = aca.artist_id
|
|
LEFT JOIN release_group_recordings rgr ON r.id = rgr.recording_id
|
|
LEFT JOIN release_groups rg ON rgr.release_group_id = rg.id
|
|
LEFT JOIN cover_art ca ON rg.cover_art_id = ca.id
|
|
WHERE af.file_path = ?
|
|
LIMIT 1;
|
|
|
|
-- name: GetAllTracksWithFullMetadata :many
|
|
SELECT
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
COALESCE(r.name, '') AS title,
|
|
COALESCE(ac.text, '') AS artist_name,
|
|
r.track_number,
|
|
r.disc_number,
|
|
COALESCE(rg.name, '') AS album,
|
|
CAST(COALESCE(
|
|
(SELECT GROUP_CONCAT(g.name, '||')
|
|
FROM recording_genres rg_sub
|
|
JOIN genres g ON rg_sub.genre_id = g.id
|
|
WHERE rg_sub.recording_id = r.id),
|
|
''
|
|
) AS TEXT) AS genre,
|
|
COALESCE(r.year, 0) AS year,
|
|
COALESCE(r.composer, '') AS composer,
|
|
COALESCE(ft.extension, '') AS file_type,
|
|
af.sample_rate,
|
|
af.bit_depth,
|
|
af.channels,
|
|
af.bitrate,
|
|
af.file_size,
|
|
af.play_count,
|
|
af.last_played,
|
|
COALESCE(ca.file_path, '') AS cover_art_path,
|
|
COALESCE(a.mbid, '') AS artist_mbid,
|
|
COALESCE(rg.mbid, '') AS release_group_mbid,
|
|
COALESCE(r.mbid, '') AS recording_mbid
|
|
FROM audio_files af
|
|
JOIN recordings r ON af.recording_id = r.id
|
|
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
|
LEFT JOIN artist_credit_artist aca ON aca.credit_id = ac.id
|
|
LEFT JOIN artists a ON a.id = aca.artist_id
|
|
LEFT JOIN release_group_recordings rgr ON r.id = rgr.recording_id
|
|
LEFT JOIN release_groups rg ON rgr.release_group_id = rg.id
|
|
LEFT JOIN cover_art ca ON rg.cover_art_id = ca.id
|
|
LEFT JOIN file_types ft ON af.file_type_id = ft.id;
|
|
|
|
-- name: SearchAudioFilesByBasename :many
|
|
SELECT
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
COALESCE(r.name, '') AS title,
|
|
COALESCE(ac.text, '') AS artist,
|
|
COALESCE(rg.name, '') AS album
|
|
FROM audio_files af
|
|
LEFT JOIN recordings r ON af.recording_id = r.id
|
|
LEFT JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
|
LEFT JOIN (
|
|
SELECT recording_id, MIN(release_group_id) AS release_group_id
|
|
FROM release_group_recordings
|
|
GROUP BY recording_id
|
|
) rgr ON r.id = rgr.recording_id
|
|
LEFT JOIN release_groups rg ON rgr.release_group_id = rg.id
|
|
WHERE af.basename = ?
|
|
LIMIT ?;
|
|
|
|
-- name: LookupTrackMetaByPaths :many
|
|
SELECT id, file_path, title, artist_name, album, cover_art_path, artist_mbid, release_group_mbid, recording_mbid
|
|
FROM track_metadata
|
|
WHERE file_path IN (sqlc.slice('paths'));
|
|
|
|
-- name: GetAudioFilesByLibrary :many
|
|
SELECT * FROM audio_files WHERE library_id = ?;
|
|
|
|
-- name: CountAudioFilesByLibrary :one
|
|
SELECT COUNT(*) AS count FROM audio_files WHERE library_id = ?;
|
|
|
|
-- name: DeleteAllAudioFiles :exec
|
|
DELETE FROM audio_files;
|
|
|
|
-- name: GetAllTracksWithFullMetadataByLibrary :many
|
|
SELECT
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
COALESCE(r.name, '') AS title,
|
|
COALESCE(ac.text, '') AS artist_name,
|
|
r.track_number,
|
|
r.disc_number,
|
|
COALESCE(rg.name, '') AS album,
|
|
CAST(COALESCE(
|
|
(SELECT GROUP_CONCAT(g.name, '||')
|
|
FROM recording_genres rg_sub
|
|
JOIN genres g ON rg_sub.genre_id = g.id
|
|
WHERE rg_sub.recording_id = r.id),
|
|
''
|
|
) AS TEXT) AS genre,
|
|
COALESCE(r.year, 0) AS year,
|
|
COALESCE(r.composer, '') AS composer,
|
|
COALESCE(ft.extension, '') AS file_type,
|
|
af.sample_rate,
|
|
af.bit_depth,
|
|
af.channels,
|
|
af.bitrate,
|
|
af.file_size,
|
|
af.play_count,
|
|
af.last_played,
|
|
COALESCE(ca.file_path, '') AS cover_art_path,
|
|
COALESCE(a.mbid, '') AS artist_mbid,
|
|
COALESCE(rg.mbid, '') AS release_group_mbid,
|
|
COALESCE(r.mbid, '') AS recording_mbid
|
|
FROM audio_files af
|
|
JOIN recordings r ON af.recording_id = r.id
|
|
JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
|
LEFT JOIN artist_credit_artist aca ON aca.credit_id = ac.id
|
|
LEFT JOIN artists a ON a.id = aca.artist_id
|
|
LEFT JOIN release_group_recordings rgr ON r.id = rgr.recording_id
|
|
LEFT JOIN release_groups rg ON rgr.release_group_id = rg.id
|
|
LEFT JOIN cover_art ca ON rg.cover_art_id = ca.id
|
|
LEFT JOIN file_types ft ON af.file_type_id = ft.id
|
|
WHERE af.library_id = ?;
|
|
|
|
-- name: GetAudioFilesByReleaseGroup :many
|
|
SELECT
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
COALESCE(r.name, '') AS title,
|
|
COALESCE(ac.text, '') AS artist_name,
|
|
rgr.track_number,
|
|
rgr.disc_number,
|
|
COALESCE(rg.name, '') AS album,
|
|
CAST(COALESCE(
|
|
(SELECT GROUP_CONCAT(g.name, '||')
|
|
FROM recording_genres rg_sub
|
|
JOIN genres g ON rg_sub.genre_id = g.id
|
|
WHERE rg_sub.recording_id = r.id),
|
|
''
|
|
) AS TEXT) AS genre,
|
|
COALESCE(r.year, 0) AS year,
|
|
COALESCE(r.composer, '') AS composer,
|
|
COALESCE(ft.extension, '') AS file_type,
|
|
af.sample_rate,
|
|
af.bit_depth,
|
|
af.channels,
|
|
af.bitrate,
|
|
af.file_size,
|
|
COALESCE(a.mbid, '') AS artist_mbid,
|
|
COALESCE(rg.mbid, '') AS release_group_mbid,
|
|
COALESCE(r.mbid, '') AS recording_mbid
|
|
FROM release_group_recordings rgr
|
|
JOIN recordings r ON rgr.recording_id = r.id
|
|
JOIN audio_files af ON af.recording_id = r.id
|
|
LEFT JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
|
LEFT JOIN artist_credit_artist aca ON aca.credit_id = ac.id
|
|
LEFT JOIN artists a ON a.id = aca.artist_id
|
|
LEFT JOIN release_groups rg ON rgr.release_group_id = rg.id
|
|
LEFT JOIN file_types ft ON af.file_type_id = ft.id
|
|
WHERE rgr.release_group_id = ?
|
|
ORDER BY rgr.disc_number, rgr.track_number;
|
|
|
|
-- name: GetAudioFilesByReleaseGroupByLibrary :many
|
|
SELECT
|
|
af.file_path,
|
|
af.length_milliseconds,
|
|
COALESCE(r.name, '') AS title,
|
|
COALESCE(ac.text, '') AS artist_name,
|
|
rgr.track_number,
|
|
rgr.disc_number,
|
|
COALESCE(rg.name, '') AS album,
|
|
CAST(COALESCE(
|
|
(SELECT GROUP_CONCAT(g.name, '||')
|
|
FROM recording_genres rg_sub
|
|
JOIN genres g ON rg_sub.genre_id = g.id
|
|
WHERE rg_sub.recording_id = r.id),
|
|
''
|
|
) AS TEXT) AS genre,
|
|
COALESCE(r.year, 0) AS year,
|
|
COALESCE(r.composer, '') AS composer,
|
|
COALESCE(ft.extension, '') AS file_type,
|
|
af.sample_rate,
|
|
af.bit_depth,
|
|
af.channels,
|
|
af.bitrate,
|
|
af.file_size,
|
|
COALESCE(a.mbid, '') AS artist_mbid,
|
|
COALESCE(rg.mbid, '') AS release_group_mbid,
|
|
COALESCE(r.mbid, '') AS recording_mbid
|
|
FROM release_group_recordings rgr
|
|
JOIN recordings r ON rgr.recording_id = r.id
|
|
JOIN audio_files af ON af.recording_id = r.id
|
|
LEFT JOIN artist_credit ac ON r.artist_credit_id = ac.id
|
|
LEFT JOIN artist_credit_artist aca ON aca.credit_id = ac.id
|
|
LEFT JOIN artists a ON a.id = aca.artist_id
|
|
LEFT JOIN release_groups rg ON rgr.release_group_id = rg.id
|
|
LEFT JOIN file_types ft ON af.file_type_id = ft.id
|
|
WHERE rgr.release_group_id = ? AND af.library_id = ?
|
|
ORDER BY rgr.disc_number, rgr.track_number;
|
|
|
|
-- "Play this artist" and "play these albums" wanted file paths and asked
|
|
-- for whole track rows to get them, one round trip per album (perf.m2).
|
|
-- These answer the same question in one query and carry only what the
|
|
-- caller uses; the release group id comes back so the caller can keep
|
|
-- its own album ordering.
|
|
|
|
-- name: GetFilePathsByReleaseGroups :many
|
|
SELECT rgr.release_group_id, af.file_path
|
|
FROM release_group_recordings rgr
|
|
JOIN recordings r ON rgr.recording_id = r.id
|
|
JOIN audio_files af ON af.recording_id = r.id
|
|
WHERE rgr.release_group_id IN (sqlc.slice('release_group_ids'))
|
|
ORDER BY rgr.disc_number, rgr.track_number;
|
|
|
|
-- name: GetFilePathsByReleaseGroupsByLibrary :many
|
|
SELECT rgr.release_group_id, af.file_path
|
|
FROM release_group_recordings rgr
|
|
JOIN recordings r ON rgr.recording_id = r.id
|
|
JOIN audio_files af ON af.recording_id = r.id
|
|
WHERE rgr.release_group_id IN (sqlc.slice('release_group_ids'))
|
|
AND af.library_id = ?
|
|
ORDER BY rgr.disc_number, rgr.track_number;
|
|
|
|
-- Same shape again, keyed on recording MBID, for the catalog side.
|
|
-- An Explore album page knows which of its tracks the user owns only
|
|
-- as a set of recording MBIDs -- that is exactly how the backend
|
|
-- decides `inLibrary` (markReleasesInLibrary -> CheckMBIDs) -- and
|
|
-- MBTrack.LocalID is declared but never written by anything, so there
|
|
-- is no id to ask by. Grouped by MBID because a recording can have
|
|
-- more than one file (the duplicate fixtures are precisely that) and
|
|
-- because the caller owns the order: the tracklist's, not the
|
|
-- database's.
|
|
|
|
-- name: GetFilePathsByRecordingMBIDs :many
|
|
SELECT r.mbid AS recording_mbid, af.file_path
|
|
FROM recordings r
|
|
JOIN audio_files af ON af.recording_id = r.id
|
|
WHERE r.mbid IN (sqlc.slice('mbids'))
|
|
ORDER BY af.file_path;
|
|
|
|
-- name: GetFilePathsByRecordingMBIDsByLibrary :many
|
|
SELECT r.mbid AS recording_mbid, af.file_path
|
|
FROM recordings r
|
|
JOIN audio_files af ON af.recording_id = r.id
|
|
WHERE r.mbid IN (sqlc.slice('mbids'))
|
|
AND af.library_id = ?
|
|
ORDER BY af.file_path;
|
|
|
|
-- name: GetAudioFilesByPaths :many
|
|
SELECT id, library_id, file_path, group_key FROM audio_files
|
|
WHERE file_path IN (sqlc.slice('paths'));
|