RemoveFromLibrary deletes the audio_files rows the way the scan's own orphan cleanup does and records each path in excluded_paths. The exclusion is not an enhancement: without it the next scan finds the file, sees no row and imports it again, so the button undoes itself. The soft scan compares files on disk against rows in the database, so surveyAudioFiles and countAudioFiles both take the exclusion set — otherwise an excluded path makes the two disagree forever and queues a full scan on every launch. Deleting a row cascades to queue_tracks, so the removal calls the same CompactQueue hook RemoveLibrary does. Also lands the requested badge: library-status-indicator is a button again where it can act, utils/library-status.ts states once what owning and wanting mean, and the long-declared queued state finally has a producer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UDCbcCZQepnpSQYJ6SxxZm
1481 lines
39 KiB
Go
1481 lines
39 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.30.0
|
|
// source: audio_files.sql
|
|
|
|
package sqlcgen
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"strings"
|
|
)
|
|
|
|
const countAudioFiles = `-- name: CountAudioFiles :one
|
|
SELECT count(*) FROM audio_files
|
|
`
|
|
|
|
func (q *Queries) CountAudioFiles(ctx context.Context) (int64, error) {
|
|
row := q.db.QueryRowContext(ctx, countAudioFiles)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const countAudioFilesByLibrary = `-- name: CountAudioFilesByLibrary :one
|
|
SELECT COUNT(*) AS count FROM audio_files WHERE library_id = ?
|
|
`
|
|
|
|
func (q *Queries) CountAudioFilesByLibrary(ctx context.Context, libraryID int64) (int64, error) {
|
|
row := q.db.QueryRowContext(ctx, countAudioFilesByLibrary, libraryID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const createAudioFile = `-- 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 id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played, tag_status, group_key, modified_at
|
|
`
|
|
|
|
type CreateAudioFileParams struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
FileTypeID int64
|
|
RecordingID int64
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
Basename string
|
|
LibraryID int64
|
|
}
|
|
|
|
func (q *Queries) CreateAudioFile(ctx context.Context, arg CreateAudioFileParams) (AudioFile, error) {
|
|
row := q.db.QueryRowContext(ctx, createAudioFile,
|
|
arg.FilePath,
|
|
arg.LengthMilliseconds,
|
|
arg.FileTypeID,
|
|
arg.RecordingID,
|
|
arg.SampleRate,
|
|
arg.BitDepth,
|
|
arg.Channels,
|
|
arg.Bitrate,
|
|
arg.FileSize,
|
|
arg.Basename,
|
|
arg.LibraryID,
|
|
)
|
|
var i AudioFile
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Basename,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.TagStatus,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const createAudioFileWithGroupKey = `-- 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 id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played, tag_status, group_key, modified_at
|
|
`
|
|
|
|
type CreateAudioFileWithGroupKeyParams struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
FileTypeID int64
|
|
RecordingID int64
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
Basename string
|
|
LibraryID int64
|
|
GroupKey string
|
|
TagStatus string
|
|
ModifiedAt int64
|
|
}
|
|
|
|
func (q *Queries) CreateAudioFileWithGroupKey(ctx context.Context, arg CreateAudioFileWithGroupKeyParams) (AudioFile, error) {
|
|
row := q.db.QueryRowContext(ctx, createAudioFileWithGroupKey,
|
|
arg.FilePath,
|
|
arg.LengthMilliseconds,
|
|
arg.FileTypeID,
|
|
arg.RecordingID,
|
|
arg.SampleRate,
|
|
arg.BitDepth,
|
|
arg.Channels,
|
|
arg.Bitrate,
|
|
arg.FileSize,
|
|
arg.Basename,
|
|
arg.LibraryID,
|
|
arg.GroupKey,
|
|
arg.TagStatus,
|
|
arg.ModifiedAt,
|
|
)
|
|
var i AudioFile
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Basename,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.TagStatus,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const deleteAllAudioFiles = `-- name: DeleteAllAudioFiles :exec
|
|
DELETE FROM audio_files
|
|
`
|
|
|
|
func (q *Queries) DeleteAllAudioFiles(ctx context.Context) error {
|
|
_, err := q.db.ExecContext(ctx, deleteAllAudioFiles)
|
|
return err
|
|
}
|
|
|
|
const deleteAudioFile = `-- name: DeleteAudioFile :exec
|
|
DELETE FROM audio_files
|
|
WHERE id = ?
|
|
`
|
|
|
|
func (q *Queries) DeleteAudioFile(ctx context.Context, id int64) error {
|
|
_, err := q.db.ExecContext(ctx, deleteAudioFile, id)
|
|
return err
|
|
}
|
|
|
|
const getAllAudioFilePaths = `-- name: GetAllAudioFilePaths :many
|
|
SELECT id, file_path FROM audio_files
|
|
`
|
|
|
|
type GetAllAudioFilePathsRow struct {
|
|
ID int64
|
|
FilePath string
|
|
}
|
|
|
|
func (q *Queries) GetAllAudioFilePaths(ctx context.Context) ([]GetAllAudioFilePathsRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAllAudioFilePaths)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetAllAudioFilePathsRow
|
|
for rows.Next() {
|
|
var i GetAllAudioFilePathsRow
|
|
if err := rows.Scan(&i.ID, &i.FilePath); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAllAudioFiles = `-- name: GetAllAudioFiles :many
|
|
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played, tag_status, group_key, modified_at FROM audio_files
|
|
`
|
|
|
|
func (q *Queries) GetAllAudioFiles(ctx context.Context) ([]AudioFile, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAllAudioFiles)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []AudioFile
|
|
for rows.Next() {
|
|
var i AudioFile
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Basename,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.TagStatus,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAllAudioFilesWithArtist = `-- 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
|
|
`
|
|
|
|
type GetAllAudioFilesWithArtistRow struct {
|
|
ID int64
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
FileTypeID int64
|
|
RecordingID int64
|
|
ArtistName string
|
|
Title string
|
|
}
|
|
|
|
func (q *Queries) GetAllAudioFilesWithArtist(ctx context.Context) ([]GetAllAudioFilesWithArtistRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAllAudioFilesWithArtist)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetAllAudioFilesWithArtistRow
|
|
for rows.Next() {
|
|
var i GetAllAudioFilesWithArtistRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.ArtistName,
|
|
&i.Title,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAllTracksWithFullMetadata = `-- 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
|
|
`
|
|
|
|
type GetAllTracksWithFullMetadataRow struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
Title string
|
|
ArtistName string
|
|
TrackNumber sql.NullInt64
|
|
DiscNumber sql.NullInt64
|
|
Album string
|
|
Genre string
|
|
Year int64
|
|
Composer string
|
|
FileType string
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
PlayCount int64
|
|
LastPlayed sql.NullTime
|
|
CoverArtPath string
|
|
ArtistMbid string
|
|
ReleaseGroupMbid string
|
|
RecordingMbid string
|
|
}
|
|
|
|
func (q *Queries) GetAllTracksWithFullMetadata(ctx context.Context) ([]GetAllTracksWithFullMetadataRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAllTracksWithFullMetadata)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetAllTracksWithFullMetadataRow
|
|
for rows.Next() {
|
|
var i GetAllTracksWithFullMetadataRow
|
|
if err := rows.Scan(
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAllTracksWithFullMetadataByLibrary = `-- 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 = ?
|
|
`
|
|
|
|
type GetAllTracksWithFullMetadataByLibraryRow struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
Title string
|
|
ArtistName string
|
|
TrackNumber sql.NullInt64
|
|
DiscNumber sql.NullInt64
|
|
Album string
|
|
Genre string
|
|
Year int64
|
|
Composer string
|
|
FileType string
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
PlayCount int64
|
|
LastPlayed sql.NullTime
|
|
CoverArtPath string
|
|
ArtistMbid string
|
|
ReleaseGroupMbid string
|
|
RecordingMbid string
|
|
}
|
|
|
|
func (q *Queries) GetAllTracksWithFullMetadataByLibrary(ctx context.Context, libraryID int64) ([]GetAllTracksWithFullMetadataByLibraryRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAllTracksWithFullMetadataByLibrary, libraryID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetAllTracksWithFullMetadataByLibraryRow
|
|
for rows.Next() {
|
|
var i GetAllTracksWithFullMetadataByLibraryRow
|
|
if err := rows.Scan(
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAudioFile = `-- name: GetAudioFile :one
|
|
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played, tag_status, group_key, modified_at FROM audio_files
|
|
WHERE id = ? LIMIT 1
|
|
`
|
|
|
|
func (q *Queries) GetAudioFile(ctx context.Context, id int64) (AudioFile, error) {
|
|
row := q.db.QueryRowContext(ctx, getAudioFile, id)
|
|
var i AudioFile
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Basename,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.TagStatus,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAudioFileByPath = `-- name: GetAudioFileByPath :one
|
|
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played, tag_status, group_key, modified_at FROM audio_files
|
|
WHERE file_path = ? LIMIT 1
|
|
`
|
|
|
|
func (q *Queries) GetAudioFileByPath(ctx context.Context, filePath string) (AudioFile, error) {
|
|
row := q.db.QueryRowContext(ctx, getAudioFileByPath, filePath)
|
|
var i AudioFile
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Basename,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.TagStatus,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAudioFileGroupKey = `-- name: GetAudioFileGroupKey :one
|
|
SELECT group_key FROM audio_files
|
|
WHERE id = ? LIMIT 1
|
|
`
|
|
|
|
func (q *Queries) GetAudioFileGroupKey(ctx context.Context, id int64) (string, error) {
|
|
row := q.db.QueryRowContext(ctx, getAudioFileGroupKey, id)
|
|
var group_key string
|
|
err := row.Scan(&group_key)
|
|
return group_key, err
|
|
}
|
|
|
|
const getAudioFilesByLibrary = `-- name: GetAudioFilesByLibrary :many
|
|
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played, tag_status, group_key, modified_at FROM audio_files WHERE library_id = ?
|
|
`
|
|
|
|
func (q *Queries) GetAudioFilesByLibrary(ctx context.Context, libraryID int64) ([]AudioFile, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAudioFilesByLibrary, libraryID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []AudioFile
|
|
for rows.Next() {
|
|
var i AudioFile
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Basename,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.TagStatus,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAudioFilesByPaths = `-- name: GetAudioFilesByPaths :many
|
|
SELECT id, library_id, file_path, group_key FROM audio_files
|
|
WHERE file_path IN (/*SLICE:paths*/?)
|
|
`
|
|
|
|
type GetAudioFilesByPathsRow struct {
|
|
ID int64
|
|
LibraryID int64
|
|
FilePath string
|
|
GroupKey string
|
|
}
|
|
|
|
func (q *Queries) GetAudioFilesByPaths(ctx context.Context, paths []string) ([]GetAudioFilesByPathsRow, error) {
|
|
query := getAudioFilesByPaths
|
|
var queryParams []interface{}
|
|
if len(paths) > 0 {
|
|
for _, v := range paths {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:paths*/?", strings.Repeat(",?", len(paths))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:paths*/?", "NULL", 1)
|
|
}
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetAudioFilesByPathsRow
|
|
for rows.Next() {
|
|
var i GetAudioFilesByPathsRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.LibraryID,
|
|
&i.FilePath,
|
|
&i.GroupKey,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAudioFilesByReleaseGroup = `-- 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
|
|
`
|
|
|
|
type GetAudioFilesByReleaseGroupRow struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
Title string
|
|
ArtistName string
|
|
TrackNumber sql.NullInt64
|
|
DiscNumber sql.NullInt64
|
|
Album string
|
|
Genre string
|
|
Year int64
|
|
Composer string
|
|
FileType string
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
ArtistMbid string
|
|
ReleaseGroupMbid string
|
|
RecordingMbid string
|
|
}
|
|
|
|
func (q *Queries) GetAudioFilesByReleaseGroup(ctx context.Context, releaseGroupID int64) ([]GetAudioFilesByReleaseGroupRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAudioFilesByReleaseGroup, releaseGroupID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetAudioFilesByReleaseGroupRow
|
|
for rows.Next() {
|
|
var i GetAudioFilesByReleaseGroupRow
|
|
if err := rows.Scan(
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAudioFilesByReleaseGroupByLibrary = `-- 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
|
|
`
|
|
|
|
type GetAudioFilesByReleaseGroupByLibraryParams struct {
|
|
ReleaseGroupID int64
|
|
LibraryID int64
|
|
}
|
|
|
|
type GetAudioFilesByReleaseGroupByLibraryRow struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
Title string
|
|
ArtistName string
|
|
TrackNumber sql.NullInt64
|
|
DiscNumber sql.NullInt64
|
|
Album string
|
|
Genre string
|
|
Year int64
|
|
Composer string
|
|
FileType string
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
ArtistMbid string
|
|
ReleaseGroupMbid string
|
|
RecordingMbid string
|
|
}
|
|
|
|
func (q *Queries) GetAudioFilesByReleaseGroupByLibrary(ctx context.Context, arg GetAudioFilesByReleaseGroupByLibraryParams) ([]GetAudioFilesByReleaseGroupByLibraryRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAudioFilesByReleaseGroupByLibrary, arg.ReleaseGroupID, arg.LibraryID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetAudioFilesByReleaseGroupByLibraryRow
|
|
for rows.Next() {
|
|
var i GetAudioFilesByReleaseGroupByLibraryRow
|
|
if err := rows.Scan(
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getAudioFilesNeedingMetadata = `-- name: GetAudioFilesNeedingMetadata :many
|
|
SELECT id, file_path, length_milliseconds, file_type_id, recording_id, sample_rate, bit_depth, channels, bitrate, file_size, basename, library_id, play_count, last_played, tag_status, group_key, modified_at FROM audio_files
|
|
WHERE recording_id = 0
|
|
`
|
|
|
|
func (q *Queries) GetAudioFilesNeedingMetadata(ctx context.Context) ([]AudioFile, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAudioFilesNeedingMetadata)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []AudioFile
|
|
for rows.Next() {
|
|
var i AudioFile
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.FileTypeID,
|
|
&i.RecordingID,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Basename,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.TagStatus,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getFilePathsByRecordingMBIDs = `-- 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 (/*SLICE:mbids*/?)
|
|
ORDER BY af.file_path
|
|
`
|
|
|
|
type GetFilePathsByRecordingMBIDsRow struct {
|
|
RecordingMbid sql.NullString
|
|
FilePath string
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) GetFilePathsByRecordingMBIDs(ctx context.Context, mbids []sql.NullString) ([]GetFilePathsByRecordingMBIDsRow, error) {
|
|
query := getFilePathsByRecordingMBIDs
|
|
var queryParams []interface{}
|
|
if len(mbids) > 0 {
|
|
for _, v := range mbids {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:mbids*/?", strings.Repeat(",?", len(mbids))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:mbids*/?", "NULL", 1)
|
|
}
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetFilePathsByRecordingMBIDsRow
|
|
for rows.Next() {
|
|
var i GetFilePathsByRecordingMBIDsRow
|
|
if err := rows.Scan(&i.RecordingMbid, &i.FilePath); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getFilePathsByRecordingMBIDsByLibrary = `-- 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 (/*SLICE:mbids*/?)
|
|
AND af.library_id = ?
|
|
ORDER BY af.file_path
|
|
`
|
|
|
|
type GetFilePathsByRecordingMBIDsByLibraryParams struct {
|
|
Mbids []sql.NullString
|
|
LibraryID int64
|
|
}
|
|
|
|
type GetFilePathsByRecordingMBIDsByLibraryRow struct {
|
|
RecordingMbid sql.NullString
|
|
FilePath string
|
|
}
|
|
|
|
func (q *Queries) GetFilePathsByRecordingMBIDsByLibrary(ctx context.Context, arg GetFilePathsByRecordingMBIDsByLibraryParams) ([]GetFilePathsByRecordingMBIDsByLibraryRow, error) {
|
|
query := getFilePathsByRecordingMBIDsByLibrary
|
|
var queryParams []interface{}
|
|
if len(arg.Mbids) > 0 {
|
|
for _, v := range arg.Mbids {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:mbids*/?", strings.Repeat(",?", len(arg.Mbids))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:mbids*/?", "NULL", 1)
|
|
}
|
|
queryParams = append(queryParams, arg.LibraryID)
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetFilePathsByRecordingMBIDsByLibraryRow
|
|
for rows.Next() {
|
|
var i GetFilePathsByRecordingMBIDsByLibraryRow
|
|
if err := rows.Scan(&i.RecordingMbid, &i.FilePath); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getFilePathsByReleaseGroups = `-- 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 (/*SLICE:release_group_ids*/?)
|
|
ORDER BY rgr.disc_number, rgr.track_number
|
|
`
|
|
|
|
type GetFilePathsByReleaseGroupsRow struct {
|
|
ReleaseGroupID int64
|
|
FilePath string
|
|
}
|
|
|
|
// "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.
|
|
func (q *Queries) GetFilePathsByReleaseGroups(ctx context.Context, releaseGroupIds []int64) ([]GetFilePathsByReleaseGroupsRow, error) {
|
|
query := getFilePathsByReleaseGroups
|
|
var queryParams []interface{}
|
|
if len(releaseGroupIds) > 0 {
|
|
for _, v := range releaseGroupIds {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:release_group_ids*/?", strings.Repeat(",?", len(releaseGroupIds))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:release_group_ids*/?", "NULL", 1)
|
|
}
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetFilePathsByReleaseGroupsRow
|
|
for rows.Next() {
|
|
var i GetFilePathsByReleaseGroupsRow
|
|
if err := rows.Scan(&i.ReleaseGroupID, &i.FilePath); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getFilePathsByReleaseGroupsByLibrary = `-- 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 (/*SLICE:release_group_ids*/?)
|
|
AND af.library_id = ?
|
|
ORDER BY rgr.disc_number, rgr.track_number
|
|
`
|
|
|
|
type GetFilePathsByReleaseGroupsByLibraryParams struct {
|
|
ReleaseGroupIds []int64
|
|
LibraryID int64
|
|
}
|
|
|
|
type GetFilePathsByReleaseGroupsByLibraryRow struct {
|
|
ReleaseGroupID int64
|
|
FilePath string
|
|
}
|
|
|
|
func (q *Queries) GetFilePathsByReleaseGroupsByLibrary(ctx context.Context, arg GetFilePathsByReleaseGroupsByLibraryParams) ([]GetFilePathsByReleaseGroupsByLibraryRow, error) {
|
|
query := getFilePathsByReleaseGroupsByLibrary
|
|
var queryParams []interface{}
|
|
if len(arg.ReleaseGroupIds) > 0 {
|
|
for _, v := range arg.ReleaseGroupIds {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:release_group_ids*/?", strings.Repeat(",?", len(arg.ReleaseGroupIds))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:release_group_ids*/?", "NULL", 1)
|
|
}
|
|
queryParams = append(queryParams, arg.LibraryID)
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetFilePathsByReleaseGroupsByLibraryRow
|
|
for rows.Next() {
|
|
var i GetFilePathsByReleaseGroupsByLibraryRow
|
|
if err := rows.Scan(&i.ReleaseGroupID, &i.FilePath); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getLibraryMaxModifiedAt = `-- name: GetLibraryMaxModifiedAt :one
|
|
SELECT CAST(COALESCE(MAX(modified_at), 0) AS INTEGER) FROM audio_files
|
|
WHERE library_id = ?
|
|
`
|
|
|
|
// Newest recorded mtime in a library, for the startup soft scan. 0 when
|
|
// the library is empty or no row has a baseline yet.
|
|
func (q *Queries) GetLibraryMaxModifiedAt(ctx context.Context, libraryID int64) (int64, error) {
|
|
row := q.db.QueryRowContext(ctx, getLibraryMaxModifiedAt, libraryID)
|
|
var column_1 int64
|
|
err := row.Scan(&column_1)
|
|
return column_1, err
|
|
}
|
|
|
|
const getRandomAudioFilePath = `-- name: GetRandomAudioFilePath :one
|
|
SELECT file_path FROM audio_files
|
|
ORDER BY RANDOM()
|
|
LIMIT 1
|
|
`
|
|
|
|
func (q *Queries) GetRandomAudioFilePath(ctx context.Context) (string, error) {
|
|
row := q.db.QueryRowContext(ctx, getRandomAudioFilePath)
|
|
var file_path string
|
|
err := row.Scan(&file_path)
|
|
return file_path, err
|
|
}
|
|
|
|
const getTrackMetadataByPath = `-- 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
|
|
`
|
|
|
|
type GetTrackMetadataByPathRow struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
Title string
|
|
Artist string
|
|
Album string
|
|
CoverArtPath string
|
|
ArtistMbid string
|
|
ReleaseGroupMbid string
|
|
RecordingMbid string
|
|
}
|
|
|
|
func (q *Queries) GetTrackMetadataByPath(ctx context.Context, filePath string) (GetTrackMetadataByPathRow, error) {
|
|
row := q.db.QueryRowContext(ctx, getTrackMetadataByPath, filePath)
|
|
var i GetTrackMetadataByPathRow
|
|
err := row.Scan(
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.Artist,
|
|
&i.Album,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const lookupTrackMetaByPaths = `-- 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 (/*SLICE:paths*/?)
|
|
`
|
|
|
|
type LookupTrackMetaByPathsRow struct {
|
|
ID int64
|
|
FilePath string
|
|
Title string
|
|
ArtistName string
|
|
Album string
|
|
CoverArtPath string
|
|
ArtistMbid string
|
|
ReleaseGroupMbid string
|
|
RecordingMbid string
|
|
}
|
|
|
|
func (q *Queries) LookupTrackMetaByPaths(ctx context.Context, paths []string) ([]LookupTrackMetaByPathsRow, error) {
|
|
query := lookupTrackMetaByPaths
|
|
var queryParams []interface{}
|
|
if len(paths) > 0 {
|
|
for _, v := range paths {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:paths*/?", strings.Repeat(",?", len(paths))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:paths*/?", "NULL", 1)
|
|
}
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []LookupTrackMetaByPathsRow
|
|
for rows.Next() {
|
|
var i LookupTrackMetaByPathsRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.Album,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const searchAudioFilesByBasename = `-- 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 ?
|
|
`
|
|
|
|
type SearchAudioFilesByBasenameParams struct {
|
|
Basename string
|
|
Limit int64
|
|
}
|
|
|
|
type SearchAudioFilesByBasenameRow struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
Title string
|
|
Artist string
|
|
Album string
|
|
}
|
|
|
|
func (q *Queries) SearchAudioFilesByBasename(ctx context.Context, arg SearchAudioFilesByBasenameParams) ([]SearchAudioFilesByBasenameRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, searchAudioFilesByBasename, arg.Basename, arg.Limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []SearchAudioFilesByBasenameRow
|
|
for rows.Next() {
|
|
var i SearchAudioFilesByBasenameRow
|
|
if err := rows.Scan(
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.Artist,
|
|
&i.Album,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const setAudioFileGroupKey = `-- name: SetAudioFileGroupKey :exec
|
|
UPDATE audio_files SET group_key = ? WHERE id = ?
|
|
`
|
|
|
|
type SetAudioFileGroupKeyParams struct {
|
|
GroupKey string
|
|
ID int64
|
|
}
|
|
|
|
func (q *Queries) SetAudioFileGroupKey(ctx context.Context, arg SetAudioFileGroupKeyParams) error {
|
|
_, err := q.db.ExecContext(ctx, setAudioFileGroupKey, arg.GroupKey, arg.ID)
|
|
return err
|
|
}
|
|
|
|
const updateAudioFile = `-- 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 = ?
|
|
`
|
|
|
|
type UpdateAudioFileParams struct {
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
FileTypeID int64
|
|
RecordingID int64
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
Basename string
|
|
ID int64
|
|
}
|
|
|
|
func (q *Queries) UpdateAudioFile(ctx context.Context, arg UpdateAudioFileParams) error {
|
|
_, err := q.db.ExecContext(ctx, updateAudioFile,
|
|
arg.FilePath,
|
|
arg.LengthMilliseconds,
|
|
arg.FileTypeID,
|
|
arg.RecordingID,
|
|
arg.SampleRate,
|
|
arg.BitDepth,
|
|
arg.Channels,
|
|
arg.Bitrate,
|
|
arg.FileSize,
|
|
arg.Basename,
|
|
arg.ID,
|
|
)
|
|
return err
|
|
}
|
|
|
|
const updateAudioFileRecording = `-- name: UpdateAudioFileRecording :exec
|
|
UPDATE audio_files
|
|
SET recording_id = ?, sample_rate = ?, bit_depth = ?, channels = ?, bitrate = ?, file_size = ?, length_milliseconds = ?, modified_at = ?
|
|
WHERE id = ?
|
|
`
|
|
|
|
type UpdateAudioFileRecordingParams struct {
|
|
RecordingID int64
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
LengthMilliseconds int64
|
|
ModifiedAt int64
|
|
ID int64
|
|
}
|
|
|
|
func (q *Queries) UpdateAudioFileRecording(ctx context.Context, arg UpdateAudioFileRecordingParams) error {
|
|
_, err := q.db.ExecContext(ctx, updateAudioFileRecording,
|
|
arg.RecordingID,
|
|
arg.SampleRate,
|
|
arg.BitDepth,
|
|
arg.Channels,
|
|
arg.Bitrate,
|
|
arg.FileSize,
|
|
arg.LengthMilliseconds,
|
|
arg.ModifiedAt,
|
|
arg.ID,
|
|
)
|
|
return err
|
|
}
|
|
|
|
const updateAudioFileStat = `-- name: UpdateAudioFileStat :exec
|
|
UPDATE audio_files
|
|
SET modified_at = ?, file_size = ?
|
|
WHERE id = ?
|
|
`
|
|
|
|
type UpdateAudioFileStatParams struct {
|
|
ModifiedAt int64
|
|
FileSize int64
|
|
ID int64
|
|
}
|
|
|
|
// 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.
|
|
func (q *Queries) UpdateAudioFileStat(ctx context.Context, arg UpdateAudioFileStatParams) error {
|
|
_, err := q.db.ExecContext(ctx, updateAudioFileStat, arg.ModifiedAt, arg.FileSize, arg.ID)
|
|
return err
|
|
}
|