Track details from the queue, a playlist or a smart playlist looked the track up in libraryStore's whole-library array, and returned silently when it had not loaded, so the menu item did nothing. That dependency is also why every track was fetched eagerly at startup (20.5 MB at 26k tracks). GetTracksByPaths answers for the paths in hand, and trackCache holds the answers: bounded, coalesced into one call per task, and forgetting what the retag, removal and scan events name. Every opener, and track-details' own refresh after a save, go through it. A source sweep pins the whole-library array to the store and the Tracks view. Closes #279
1264 lines
34 KiB
Go
1264 lines
34 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(*) AS count FROM audio_files
|
|
WHERE library_id = COALESCE(NULLIF(CAST(?1 AS INTEGER), 0), library_id)
|
|
`
|
|
|
|
func (q *Queries) CountAudioFiles(ctx context.Context, libraryID int64) (int64, error) {
|
|
row := q.db.QueryRowContext(ctx, countAudioFiles, libraryID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const createAudioFile = `-- name: CreateAudioFile :one
|
|
|
|
|
|
INSERT INTO audio_files (
|
|
file_path, library_id, file_type_id,
|
|
length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size,
|
|
title, artist_credit, artist_id, album_id,
|
|
track_number, disc_number, total_tracks, year, composer, comment,
|
|
recording_mbid, basename, group_key, modified_at, tag_status
|
|
) VALUES (
|
|
?, ?, ?,
|
|
?, ?, ?, ?, ?, ?,
|
|
?, ?, ?, ?,
|
|
?, ?, ?, ?, ?, ?,
|
|
?, ?, ?, ?, ?
|
|
)
|
|
RETURNING id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, skip_count, last_skipped, tag_status
|
|
`
|
|
|
|
type CreateAudioFileParams struct {
|
|
FilePath string
|
|
LibraryID int64
|
|
FileTypeID int64
|
|
LengthMilliseconds int64
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
Title string
|
|
ArtistCredit string
|
|
ArtistID sql.NullInt64
|
|
AlbumID sql.NullInt64
|
|
TrackNumber sql.NullInt64
|
|
DiscNumber sql.NullInt64
|
|
TotalTracks sql.NullInt64
|
|
Year sql.NullInt64
|
|
Composer string
|
|
Comment string
|
|
RecordingMbid sql.NullString
|
|
Basename string
|
|
GroupKey string
|
|
ModifiedAt int64
|
|
TagStatus string
|
|
}
|
|
|
|
// Queries over audio_files and the track_metadata view above it.
|
|
//
|
|
// Every query that returns "a track" selects from `track_metadata`,
|
|
// which is the one place the projection is defined. The scoped and
|
|
// unscoped variants that used to be written twice are one query now:
|
|
// library_id 0 means "every library", and `(:id = 0 OR library_id = :id)`
|
|
// costs nothing measurable (23 ms vs 21 ms over 26k rows) because these
|
|
// queries scan either way.
|
|
// ---------------------------------------------------------------------
|
|
// Writes
|
|
// ---------------------------------------------------------------------
|
|
func (q *Queries) CreateAudioFile(ctx context.Context, arg CreateAudioFileParams) (AudioFile, error) {
|
|
row := q.db.QueryRowContext(ctx, createAudioFile,
|
|
arg.FilePath,
|
|
arg.LibraryID,
|
|
arg.FileTypeID,
|
|
arg.LengthMilliseconds,
|
|
arg.SampleRate,
|
|
arg.BitDepth,
|
|
arg.Channels,
|
|
arg.Bitrate,
|
|
arg.FileSize,
|
|
arg.Title,
|
|
arg.ArtistCredit,
|
|
arg.ArtistID,
|
|
arg.AlbumID,
|
|
arg.TrackNumber,
|
|
arg.DiscNumber,
|
|
arg.TotalTracks,
|
|
arg.Year,
|
|
arg.Composer,
|
|
arg.Comment,
|
|
arg.RecordingMbid,
|
|
arg.Basename,
|
|
arg.GroupKey,
|
|
arg.ModifiedAt,
|
|
arg.TagStatus,
|
|
)
|
|
var i AudioFile
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LibraryID,
|
|
&i.FileTypeID,
|
|
&i.LengthMilliseconds,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Title,
|
|
&i.ArtistCredit,
|
|
&i.ArtistID,
|
|
&i.AlbumID,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.TotalTracks,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.Comment,
|
|
&i.RecordingMbid,
|
|
&i.Basename,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.SkipCount,
|
|
&i.LastSkipped,
|
|
&i.TagStatus,
|
|
)
|
|
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 getAudioFile = `-- name: GetAudioFile :one
|
|
|
|
SELECT id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, skip_count, last_skipped, tag_status FROM audio_files WHERE id = ? LIMIT 1
|
|
`
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Reads: the file row itself
|
|
// ---------------------------------------------------------------------
|
|
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.LibraryID,
|
|
&i.FileTypeID,
|
|
&i.LengthMilliseconds,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Title,
|
|
&i.ArtistCredit,
|
|
&i.ArtistID,
|
|
&i.AlbumID,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.TotalTracks,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.Comment,
|
|
&i.RecordingMbid,
|
|
&i.Basename,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.SkipCount,
|
|
&i.LastSkipped,
|
|
&i.TagStatus,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getAudioFileByPath = `-- name: GetAudioFileByPath :one
|
|
SELECT id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, skip_count, last_skipped, tag_status 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.LibraryID,
|
|
&i.FileTypeID,
|
|
&i.LengthMilliseconds,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Title,
|
|
&i.ArtistCredit,
|
|
&i.ArtistID,
|
|
&i.AlbumID,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.TotalTracks,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.Comment,
|
|
&i.RecordingMbid,
|
|
&i.Basename,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.SkipCount,
|
|
&i.LastSkipped,
|
|
&i.TagStatus,
|
|
)
|
|
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 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 getAudioFilesInLibrary = `-- name: GetAudioFilesInLibrary :many
|
|
SELECT id, file_path, library_id, file_type_id, length_milliseconds, sample_rate, bit_depth, channels, bitrate, file_size, title, artist_credit, artist_id, album_id, track_number, disc_number, total_tracks, year, composer, comment, recording_mbid, basename, group_key, modified_at, play_count, last_played, skip_count, last_skipped, tag_status FROM audio_files WHERE library_id = ?
|
|
`
|
|
|
|
func (q *Queries) GetAudioFilesInLibrary(ctx context.Context, libraryID int64) ([]AudioFile, error) {
|
|
rows, err := q.db.QueryContext(ctx, getAudioFilesInLibrary, 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.LibraryID,
|
|
&i.FileTypeID,
|
|
&i.LengthMilliseconds,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.Title,
|
|
&i.ArtistCredit,
|
|
&i.ArtistID,
|
|
&i.AlbumID,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.TotalTracks,
|
|
&i.Year,
|
|
&i.Composer,
|
|
&i.Comment,
|
|
&i.RecordingMbid,
|
|
&i.Basename,
|
|
&i.GroupKey,
|
|
&i.ModifiedAt,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.SkipCount,
|
|
&i.LastSkipped,
|
|
&i.TagStatus,
|
|
); 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 getFilePathsByAlbums = `-- name: GetFilePathsByAlbums :many
|
|
|
|
SELECT album_id, library_id, file_path FROM audio_files
|
|
WHERE album_id IN (/*SLICE:album_ids*/?)
|
|
ORDER BY disc_number, track_number
|
|
`
|
|
|
|
type GetFilePathsByAlbumsRow struct {
|
|
AlbumID sql.NullInt64
|
|
LibraryID int64
|
|
FilePath string
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Reads: file paths, grouped by whatever the caller asked about
|
|
// ---------------------------------------------------------------------
|
|
// These answer "what can I play" and they all ask audio_files, because
|
|
// that is the only table whose rows are files. Grouped rather than
|
|
// flattened because the caller owns the order.
|
|
// The library filter is applied in Go rather than here: sqlc numbers a
|
|
// named parameter (?2) but expands a slice into N placeholders, so the
|
|
// two together bind the wrong values - GetFilePathsByAlbums([1,2], 0)
|
|
// read album id 2 as the library id. Returning library_id and
|
|
// filtering the (small) result is the version that cannot be wrong.
|
|
func (q *Queries) GetFilePathsByAlbums(ctx context.Context, albumIds []sql.NullInt64) ([]GetFilePathsByAlbumsRow, error) {
|
|
query := getFilePathsByAlbums
|
|
var queryParams []interface{}
|
|
if len(albumIds) > 0 {
|
|
for _, v := range albumIds {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:album_ids*/?", strings.Repeat(",?", len(albumIds))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:album_ids*/?", "NULL", 1)
|
|
}
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetFilePathsByAlbumsRow
|
|
for rows.Next() {
|
|
var i GetFilePathsByAlbumsRow
|
|
if err := rows.Scan(&i.AlbumID, &i.LibraryID, &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 getFilePathsByArtistMBID = `-- name: GetFilePathsByArtistMBID :many
|
|
SELECT DISTINCT af.file_path
|
|
FROM audio_files af
|
|
JOIN artists a ON a.id = af.artist_id
|
|
WHERE a.mbid = ?
|
|
`
|
|
|
|
func (q *Queries) GetFilePathsByArtistMBID(ctx context.Context, mbid sql.NullString) ([]string, error) {
|
|
rows, err := q.db.QueryContext(ctx, getFilePathsByArtistMBID, mbid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []string
|
|
for rows.Next() {
|
|
var file_path string
|
|
if err := rows.Scan(&file_path); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, file_path)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const getFilePathsByGenres = `-- name: GetFilePathsByGenres :many
|
|
SELECT g.name AS genre, af.library_id, af.file_path
|
|
FROM audio_files af
|
|
JOIN file_genres fg ON fg.audio_file_id = af.id
|
|
JOIN genres g ON g.id = fg.genre_id
|
|
WHERE g.name IN (/*SLICE:genres*/?)
|
|
ORDER BY af.disc_number, af.track_number
|
|
`
|
|
|
|
type GetFilePathsByGenresRow struct {
|
|
Genre string
|
|
LibraryID int64
|
|
FilePath string
|
|
}
|
|
|
|
func (q *Queries) GetFilePathsByGenres(ctx context.Context, genres []string) ([]GetFilePathsByGenresRow, error) {
|
|
query := getFilePathsByGenres
|
|
var queryParams []interface{}
|
|
if len(genres) > 0 {
|
|
for _, v := range genres {
|
|
queryParams = append(queryParams, v)
|
|
}
|
|
query = strings.Replace(query, "/*SLICE:genres*/?", strings.Repeat(",?", len(genres))[1:], 1)
|
|
} else {
|
|
query = strings.Replace(query, "/*SLICE:genres*/?", "NULL", 1)
|
|
}
|
|
rows, err := q.db.QueryContext(ctx, query, queryParams...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []GetFilePathsByGenresRow
|
|
for rows.Next() {
|
|
var i GetFilePathsByGenresRow
|
|
if err := rows.Scan(&i.Genre, &i.LibraryID, &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 getFilePathsByRecordingMBIDs = `-- name: GetFilePathsByRecordingMBIDs :many
|
|
SELECT recording_mbid, library_id, file_path FROM audio_files
|
|
WHERE recording_mbid IN (/*SLICE:mbids*/?)
|
|
ORDER BY file_path
|
|
`
|
|
|
|
type GetFilePathsByRecordingMBIDsRow struct {
|
|
RecordingMbid sql.NullString
|
|
LibraryID int64
|
|
FilePath string
|
|
}
|
|
|
|
// The ownership question in its only honest form: which of these
|
|
// catalog recordings has a *file* behind it. Asked of audio_files, so
|
|
// a metadata row with no file cannot answer yes.
|
|
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.LibraryID, &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 getTrackByPath = `-- name: GetTrackByPath :one
|
|
SELECT id, file_path, length_milliseconds, title, artist_name, track_number, disc_number, album, genre, year, release_year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size, library_id, play_count, last_played, cover_art_path, artist_mbid, release_group_mbid, recording_mbid, album_id, artist_id FROM track_metadata WHERE file_path = ? LIMIT 1
|
|
`
|
|
|
|
func (q *Queries) GetTrackByPath(ctx context.Context, filePath string) (TrackMetadatum, error) {
|
|
row := q.db.QueryRowContext(ctx, getTrackByPath, filePath)
|
|
var i TrackMetadatum
|
|
err := row.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.ReleaseYear,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
&i.AlbumID,
|
|
&i.ArtistID,
|
|
)
|
|
return i, err
|
|
}
|
|
|
|
const getTracks = `-- name: GetTracks :many
|
|
|
|
SELECT id, file_path, length_milliseconds, title, artist_name, track_number, disc_number, album, genre, year, release_year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size, library_id, play_count, last_played, cover_art_path, artist_mbid, release_group_mbid, recording_mbid, album_id, artist_id FROM track_metadata
|
|
WHERE library_id = COALESCE(NULLIF(CAST(?1 AS INTEGER), 0), library_id)
|
|
`
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Reads: tracks
|
|
// ---------------------------------------------------------------------
|
|
func (q *Queries) GetTracks(ctx context.Context, libraryID int64) ([]TrackMetadatum, error) {
|
|
rows, err := q.db.QueryContext(ctx, getTracks, libraryID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []TrackMetadatum
|
|
for rows.Next() {
|
|
var i TrackMetadatum
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.ReleaseYear,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
&i.AlbumID,
|
|
&i.ArtistID,
|
|
); 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 getTracksByAlbum = `-- name: GetTracksByAlbum :many
|
|
SELECT id, file_path, length_milliseconds, title, artist_name, track_number, disc_number, album, genre, year, release_year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size, library_id, play_count, last_played, cover_art_path, artist_mbid, release_group_mbid, recording_mbid, album_id, artist_id FROM track_metadata
|
|
WHERE album_id = ?1
|
|
AND library_id = COALESCE(NULLIF(CAST(?2 AS INTEGER), 0), library_id)
|
|
ORDER BY disc_number, track_number
|
|
`
|
|
|
|
type GetTracksByAlbumParams struct {
|
|
AlbumID sql.NullInt64
|
|
LibraryID int64
|
|
}
|
|
|
|
func (q *Queries) GetTracksByAlbum(ctx context.Context, arg GetTracksByAlbumParams) ([]TrackMetadatum, error) {
|
|
rows, err := q.db.QueryContext(ctx, getTracksByAlbum, arg.AlbumID, arg.LibraryID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []TrackMetadatum
|
|
for rows.Next() {
|
|
var i TrackMetadatum
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.ReleaseYear,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
&i.AlbumID,
|
|
&i.ArtistID,
|
|
); 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 getTracksByGenre = `-- name: GetTracksByGenre :many
|
|
SELECT tm.id, tm.file_path, tm.length_milliseconds, tm.title, tm.artist_name, tm.track_number, tm.disc_number, tm.album, tm.genre, tm.year, tm.release_year, tm.composer, tm.file_type, tm.sample_rate, tm.bit_depth, tm.channels, tm.bitrate, tm.file_size, tm.library_id, tm.play_count, tm.last_played, tm.cover_art_path, tm.artist_mbid, tm.release_group_mbid, tm.recording_mbid, tm.album_id, tm.artist_id FROM track_metadata tm
|
|
JOIN file_genres fg ON fg.audio_file_id = tm.id
|
|
JOIN genres g ON g.id = fg.genre_id
|
|
WHERE g.name = ?1
|
|
AND tm.library_id = COALESCE(NULLIF(CAST(?2 AS INTEGER), 0), tm.library_id)
|
|
`
|
|
|
|
type GetTracksByGenreParams struct {
|
|
Genre string
|
|
LibraryID int64
|
|
}
|
|
|
|
func (q *Queries) GetTracksByGenre(ctx context.Context, arg GetTracksByGenreParams) ([]TrackMetadatum, error) {
|
|
rows, err := q.db.QueryContext(ctx, getTracksByGenre, arg.Genre, arg.LibraryID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []TrackMetadatum
|
|
for rows.Next() {
|
|
var i TrackMetadatum
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.ReleaseYear,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
&i.AlbumID,
|
|
&i.ArtistID,
|
|
); 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 getTracksByPaths = `-- name: GetTracksByPaths :many
|
|
SELECT id, file_path, length_milliseconds, title, artist_name, track_number, disc_number, album, genre, year, release_year, composer, file_type, sample_rate, bit_depth, channels, bitrate, file_size, library_id, play_count, last_played, cover_art_path, artist_mbid, release_group_mbid, recording_mbid, album_id, artist_id FROM track_metadata WHERE file_path IN (/*SLICE:paths*/?)
|
|
`
|
|
|
|
func (q *Queries) GetTracksByPaths(ctx context.Context, paths []string) ([]TrackMetadatum, error) {
|
|
query := getTracksByPaths
|
|
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 []TrackMetadatum
|
|
for rows.Next() {
|
|
var i TrackMetadatum
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.Album,
|
|
&i.Genre,
|
|
&i.Year,
|
|
&i.ReleaseYear,
|
|
&i.Composer,
|
|
&i.FileType,
|
|
&i.SampleRate,
|
|
&i.BitDepth,
|
|
&i.Channels,
|
|
&i.Bitrate,
|
|
&i.FileSize,
|
|
&i.LibraryID,
|
|
&i.PlayCount,
|
|
&i.LastPlayed,
|
|
&i.CoverArtPath,
|
|
&i.ArtistMbid,
|
|
&i.ReleaseGroupMbid,
|
|
&i.RecordingMbid,
|
|
&i.AlbumID,
|
|
&i.ArtistID,
|
|
); 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 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 ownedAlbumMBIDs = `-- name: OwnedAlbumMBIDs :many
|
|
SELECT DISTINCT al.mbid FROM albums al
|
|
JOIN audio_files af ON af.album_id = al.id
|
|
WHERE al.mbid IN (/*SLICE:mbids*/?)
|
|
`
|
|
|
|
func (q *Queries) OwnedAlbumMBIDs(ctx context.Context, mbids []sql.NullString) ([]sql.NullString, error) {
|
|
query := ownedAlbumMBIDs
|
|
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 []sql.NullString
|
|
for rows.Next() {
|
|
var mbid sql.NullString
|
|
if err := rows.Scan(&mbid); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, mbid)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const ownedArtistMBIDs = `-- name: OwnedArtistMBIDs :many
|
|
SELECT DISTINCT a.mbid FROM artists a
|
|
JOIN audio_files af ON af.artist_id = a.id
|
|
WHERE a.mbid IN (/*SLICE:mbids*/?)
|
|
`
|
|
|
|
func (q *Queries) OwnedArtistMBIDs(ctx context.Context, mbids []sql.NullString) ([]sql.NullString, error) {
|
|
query := ownedArtistMBIDs
|
|
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 []sql.NullString
|
|
for rows.Next() {
|
|
var mbid sql.NullString
|
|
if err := rows.Scan(&mbid); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, mbid)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const ownedRecordingMBIDs = `-- name: OwnedRecordingMBIDs :many
|
|
|
|
SELECT DISTINCT recording_mbid FROM audio_files
|
|
WHERE recording_mbid IN (/*SLICE:mbids*/?)
|
|
`
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Ownership, asked in bulk
|
|
// ---------------------------------------------------------------------
|
|
// Which of these recording MBIDs are actually in the library. This is
|
|
// what marks a catalog tracklist owned; it used to be
|
|
// `SELECT mbid FROM recordings`, which answered yes for 129 tracks in a
|
|
// real library that had no file at all.
|
|
func (q *Queries) OwnedRecordingMBIDs(ctx context.Context, mbids []sql.NullString) ([]sql.NullString, error) {
|
|
query := ownedRecordingMBIDs
|
|
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 []sql.NullString
|
|
for rows.Next() {
|
|
var recording_mbid sql.NullString
|
|
if err := rows.Scan(&recording_mbid); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, recording_mbid)
|
|
}
|
|
if err := rows.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const promoteAudioFileTagStatusIfUntagged = `-- name: PromoteAudioFileTagStatusIfUntagged :exec
|
|
UPDATE audio_files
|
|
SET tag_status = 'user_confirmed'
|
|
WHERE id = ? AND tag_status = 'untagged'
|
|
`
|
|
|
|
// A rescan re-reads the tags of a file whose mtime moved, so a file
|
|
// another tagger stamped with MBIDs since import arrives here still
|
|
// carrying the 'untagged' status it was created with (only the insert
|
|
// path sets it). Promote it the same way saveAudioFile does.
|
|
// Guarded on 'untagged' so it cannot overwrite a deliberate
|
|
// 'user_skipped_permanent', and so a file losing its MBIDs is left
|
|
// alone -- demotion is the scan's judgement, not this statement's.
|
|
func (q *Queries) PromoteAudioFileTagStatusIfUntagged(ctx context.Context, id int64) error {
|
|
_, err := q.db.ExecContext(ctx, promoteAudioFileTagStatusIfUntagged, id)
|
|
return err
|
|
}
|
|
|
|
const searchTracksByBasename = `-- name: SearchTracksByBasename :many
|
|
SELECT id, file_path, length_milliseconds, title, artist_name, album
|
|
FROM track_metadata
|
|
WHERE file_path IN (
|
|
SELECT file_path FROM audio_files WHERE basename = ?1
|
|
)
|
|
LIMIT ?2
|
|
`
|
|
|
|
type SearchTracksByBasenameParams struct {
|
|
Basename string
|
|
Lim int64
|
|
}
|
|
|
|
type SearchTracksByBasenameRow struct {
|
|
ID int64
|
|
FilePath string
|
|
LengthMilliseconds int64
|
|
Title string
|
|
ArtistName string
|
|
Album string
|
|
}
|
|
|
|
func (q *Queries) SearchTracksByBasename(ctx context.Context, arg SearchTracksByBasenameParams) ([]SearchTracksByBasenameRow, error) {
|
|
rows, err := q.db.QueryContext(ctx, searchTracksByBasename, arg.Basename, arg.Lim)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []SearchTracksByBasenameRow
|
|
for rows.Next() {
|
|
var i SearchTracksByBasenameRow
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.FilePath,
|
|
&i.LengthMilliseconds,
|
|
&i.Title,
|
|
&i.ArtistName,
|
|
&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 setAudioFileRecordingMBID = `-- name: SetAudioFileRecordingMBID :exec
|
|
UPDATE audio_files SET recording_mbid = ? WHERE id = ?
|
|
`
|
|
|
|
type SetAudioFileRecordingMBIDParams struct {
|
|
RecordingMbid sql.NullString
|
|
ID int64
|
|
}
|
|
|
|
func (q *Queries) SetAudioFileRecordingMBID(ctx context.Context, arg SetAudioFileRecordingMBIDParams) error {
|
|
_, err := q.db.ExecContext(ctx, setAudioFileRecordingMBID, arg.RecordingMbid, 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
|
|
}
|
|
|
|
const updateAudioFileTags = `-- name: UpdateAudioFileTags :exec
|
|
UPDATE audio_files
|
|
SET title = ?, artist_credit = ?, artist_id = ?, album_id = ?,
|
|
track_number = ?, disc_number = ?, total_tracks = ?, year = ?,
|
|
composer = ?, comment = ?, recording_mbid = ?,
|
|
sample_rate = ?, bit_depth = ?, channels = ?, bitrate = ?,
|
|
file_size = ?, length_milliseconds = ?, modified_at = ?
|
|
WHERE id = ?
|
|
`
|
|
|
|
type UpdateAudioFileTagsParams struct {
|
|
Title string
|
|
ArtistCredit string
|
|
ArtistID sql.NullInt64
|
|
AlbumID sql.NullInt64
|
|
TrackNumber sql.NullInt64
|
|
DiscNumber sql.NullInt64
|
|
TotalTracks sql.NullInt64
|
|
Year sql.NullInt64
|
|
Composer string
|
|
Comment string
|
|
RecordingMbid sql.NullString
|
|
SampleRate int64
|
|
BitDepth int64
|
|
Channels int64
|
|
Bitrate int64
|
|
FileSize int64
|
|
LengthMilliseconds int64
|
|
ModifiedAt int64
|
|
ID int64
|
|
}
|
|
|
|
// A rescan of a file whose mtime moved: the tags are re-read and
|
|
// written over the same row. Under the old schema this created a
|
|
// *new* recording and repointed the file at it, abandoning the old one
|
|
// -- which is where 812 orphaned rows and every phantom "you own this"
|
|
// came from. There is nothing to orphan now.
|
|
func (q *Queries) UpdateAudioFileTags(ctx context.Context, arg UpdateAudioFileTagsParams) error {
|
|
_, err := q.db.ExecContext(ctx, updateAudioFileTags,
|
|
arg.Title,
|
|
arg.ArtistCredit,
|
|
arg.ArtistID,
|
|
arg.AlbumID,
|
|
arg.TrackNumber,
|
|
arg.DiscNumber,
|
|
arg.TotalTracks,
|
|
arg.Year,
|
|
arg.Composer,
|
|
arg.Comment,
|
|
arg.RecordingMbid,
|
|
arg.SampleRate,
|
|
arg.BitDepth,
|
|
arg.Channels,
|
|
arg.Bitrate,
|
|
arg.FileSize,
|
|
arg.LengthMilliseconds,
|
|
arg.ModifiedAt,
|
|
arg.ID,
|
|
)
|
|
return err
|
|
}
|