// 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, 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.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, 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.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, 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.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, 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.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 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 }