Nothing in Minstrel ever noticed a deleted file. The walk only visits paths that exist, so a row whose file was gone was never scanned, never errored, never counted — permanently invisible. classifyEvent ignores fsnotify removals by design, and the safety-net scan is the same walk, so it covers additions only. Rows accumulated forever. Found on the operator's library: a completed scan reported skipped=24185 errored=0 while the MBID backfill (which opens files by DB path rather than walking) logged ~40 "no such file or directory" across three reorganised albums. Those rows also kept their pre-#2499 welded genre, which is how this surfaced — the version-stamped tag re-read can only reach files the walk visits. The harm is not cosmetic. tracks is the candidate universe for recommendation.sql / discover.sql / system_mixes.sql and nothing filtered on file existence, so a mix could spend a slot on a track that cannot stream. Marks rather than deletes. A missing file is a claim about the filesystem and the filesystem lies transiently — an unmounted volume, a network blip, a container that started before its media mount attached. Every sweep in internal/gc resolves a truth INSIDE the database and is safe to run blind; this one is not, so no deletion happens here. Three guards refuse to act on ambiguous evidence: every scan root must resolve to a non-empty directory, the walk must have seen at least one file, and one reconcile may newly mark at most 25% of the library. Clearing a mark is never the dangerous direction, so it runs unconditionally — otherwise a library that tripped the cap could never recover once the mount returned. Only a full Scan reconciles. The walk's set of seen paths is the evidence, and ScanFiles has no basis for concluding anything about files it did not look at. Excludes marked tracks from all 13 track-emitting queries (radio x2, system mixes x5, discover x4, most-played x2), the 6 play-history seed picks, and the genre browse axis. Deliberately NOT filtered: the shared ListPlaylistTracks read path, because it also serves user-curated playlists where hiding a track the user added would be wrong — system playlists shed orphans on their next daily rebuild instead. History and the taste profile also keep them: those record the past, and a track you played 200 times still says something about your taste. Reconcile tallies land in scan_runs so a disappearance is visible rather than discovered when a mix comes up short.
356 lines
8.7 KiB
Go
356 lines
8.7 KiB
Go
// Code generated by sqlc. DO NOT EDIT.
|
|
// versions:
|
|
// sqlc v1.31.1
|
|
// source: likes.sql
|
|
|
|
package dbq
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
)
|
|
|
|
const countLikedAlbums = `-- name: CountLikedAlbums :one
|
|
SELECT count(*) FROM general_likes_albums WHERE user_id = $1
|
|
`
|
|
|
|
func (q *Queries) CountLikedAlbums(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countLikedAlbums, userID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const countLikedArtists = `-- name: CountLikedArtists :one
|
|
SELECT count(*) FROM general_likes_artists WHERE user_id = $1
|
|
`
|
|
|
|
func (q *Queries) CountLikedArtists(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countLikedArtists, userID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const countLikedTracks = `-- name: CountLikedTracks :one
|
|
SELECT count(*) FROM general_likes WHERE user_id = $1
|
|
`
|
|
|
|
func (q *Queries) CountLikedTracks(ctx context.Context, userID pgtype.UUID) (int64, error) {
|
|
row := q.db.QueryRow(ctx, countLikedTracks, userID)
|
|
var count int64
|
|
err := row.Scan(&count)
|
|
return count, err
|
|
}
|
|
|
|
const likeAlbum = `-- name: LikeAlbum :exec
|
|
INSERT INTO general_likes_albums (user_id, album_id)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (user_id, album_id) DO NOTHING
|
|
`
|
|
|
|
type LikeAlbumParams struct {
|
|
UserID pgtype.UUID
|
|
AlbumID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) LikeAlbum(ctx context.Context, arg LikeAlbumParams) error {
|
|
_, err := q.db.Exec(ctx, likeAlbum, arg.UserID, arg.AlbumID)
|
|
return err
|
|
}
|
|
|
|
const likeArtist = `-- name: LikeArtist :exec
|
|
INSERT INTO general_likes_artists (user_id, artist_id)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (user_id, artist_id) DO NOTHING
|
|
`
|
|
|
|
type LikeArtistParams struct {
|
|
UserID pgtype.UUID
|
|
ArtistID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) LikeArtist(ctx context.Context, arg LikeArtistParams) error {
|
|
_, err := q.db.Exec(ctx, likeArtist, arg.UserID, arg.ArtistID)
|
|
return err
|
|
}
|
|
|
|
const likeTrack = `-- name: LikeTrack :execrows
|
|
INSERT INTO general_likes (user_id, track_id)
|
|
VALUES ($1, $2)
|
|
ON CONFLICT (user_id, track_id) DO NOTHING
|
|
`
|
|
|
|
type LikeTrackParams struct {
|
|
UserID pgtype.UUID
|
|
TrackID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) LikeTrack(ctx context.Context, arg LikeTrackParams) (int64, error) {
|
|
result, err := q.db.Exec(ctx, likeTrack, arg.UserID, arg.TrackID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return result.RowsAffected(), nil
|
|
}
|
|
|
|
const listLikedAlbumIDs = `-- name: ListLikedAlbumIDs :many
|
|
SELECT album_id FROM general_likes_albums WHERE user_id = $1 ORDER BY liked_at DESC
|
|
`
|
|
|
|
func (q *Queries) ListLikedAlbumIDs(ctx context.Context, userID pgtype.UUID) ([]pgtype.UUID, error) {
|
|
rows, err := q.db.Query(ctx, listLikedAlbumIDs, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []pgtype.UUID
|
|
for rows.Next() {
|
|
var album_id pgtype.UUID
|
|
if err := rows.Scan(&album_id); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, album_id)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listLikedAlbumRows = `-- name: ListLikedAlbumRows :many
|
|
SELECT a.id, a.title, a.sort_title, a.artist_id, a.release_date, a.mbid, a.cover_art_path, a.created_at, a.updated_at, a.cover_art_source, a.cover_art_sources_version FROM albums a
|
|
JOIN general_likes_albums l ON l.album_id = a.id
|
|
WHERE l.user_id = $1
|
|
ORDER BY l.liked_at DESC
|
|
LIMIT $2 OFFSET $3
|
|
`
|
|
|
|
type ListLikedAlbumRowsParams struct {
|
|
UserID pgtype.UUID
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
func (q *Queries) ListLikedAlbumRows(ctx context.Context, arg ListLikedAlbumRowsParams) ([]Album, error) {
|
|
rows, err := q.db.Query(ctx, listLikedAlbumRows, arg.UserID, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Album
|
|
for rows.Next() {
|
|
var i Album
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.SortTitle,
|
|
&i.ArtistID,
|
|
&i.ReleaseDate,
|
|
&i.Mbid,
|
|
&i.CoverArtPath,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.CoverArtSource,
|
|
&i.CoverArtSourcesVersion,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listLikedArtistIDs = `-- name: ListLikedArtistIDs :many
|
|
SELECT artist_id FROM general_likes_artists WHERE user_id = $1 ORDER BY liked_at DESC
|
|
`
|
|
|
|
func (q *Queries) ListLikedArtistIDs(ctx context.Context, userID pgtype.UUID) ([]pgtype.UUID, error) {
|
|
rows, err := q.db.Query(ctx, listLikedArtistIDs, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []pgtype.UUID
|
|
for rows.Next() {
|
|
var artist_id pgtype.UUID
|
|
if err := rows.Scan(&artist_id); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, artist_id)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listLikedArtistRows = `-- name: ListLikedArtistRows :many
|
|
SELECT a.id, a.name, a.sort_name, a.mbid, a.created_at, a.updated_at, a.artist_thumb_path, a.artist_fanart_path, a.artist_art_source, a.artist_art_sources_version FROM artists a
|
|
JOIN general_likes_artists l ON l.artist_id = a.id
|
|
WHERE l.user_id = $1
|
|
ORDER BY l.liked_at DESC
|
|
LIMIT $2 OFFSET $3
|
|
`
|
|
|
|
type ListLikedArtistRowsParams struct {
|
|
UserID pgtype.UUID
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
func (q *Queries) ListLikedArtistRows(ctx context.Context, arg ListLikedArtistRowsParams) ([]Artist, error) {
|
|
rows, err := q.db.Query(ctx, listLikedArtistRows, arg.UserID, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Artist
|
|
for rows.Next() {
|
|
var i Artist
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Name,
|
|
&i.SortName,
|
|
&i.Mbid,
|
|
&i.CreatedAt,
|
|
&i.UpdatedAt,
|
|
&i.ArtistThumbPath,
|
|
&i.ArtistFanartPath,
|
|
&i.ArtistArtSource,
|
|
&i.ArtistArtSourcesVersion,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listLikedTrackIDs = `-- name: ListLikedTrackIDs :many
|
|
SELECT track_id FROM general_likes WHERE user_id = $1 ORDER BY liked_at DESC
|
|
`
|
|
|
|
func (q *Queries) ListLikedTrackIDs(ctx context.Context, userID pgtype.UUID) ([]pgtype.UUID, error) {
|
|
rows, err := q.db.Query(ctx, listLikedTrackIDs, userID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []pgtype.UUID
|
|
for rows.Next() {
|
|
var track_id pgtype.UUID
|
|
if err := rows.Scan(&track_id); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, track_id)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const listLikedTrackRows = `-- name: ListLikedTrackRows :many
|
|
SELECT t.id, t.title, t.album_id, t.artist_id, t.track_number, t.disc_number, t.duration_ms, t.file_path, t.file_size, t.file_format, t.bitrate, t.mbid, t.genre, t.added_at, t.updated_at, t.tag_source, t.tag_sources_version, t.tag_read_version, t.missing_since FROM tracks t
|
|
JOIN general_likes l ON l.track_id = t.id
|
|
WHERE l.user_id = $1
|
|
ORDER BY l.liked_at DESC
|
|
LIMIT $2 OFFSET $3
|
|
`
|
|
|
|
type ListLikedTrackRowsParams struct {
|
|
UserID pgtype.UUID
|
|
Limit int32
|
|
Offset int32
|
|
}
|
|
|
|
func (q *Queries) ListLikedTrackRows(ctx context.Context, arg ListLikedTrackRowsParams) ([]Track, error) {
|
|
rows, err := q.db.Query(ctx, listLikedTrackRows, arg.UserID, arg.Limit, arg.Offset)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var items []Track
|
|
for rows.Next() {
|
|
var i Track
|
|
if err := rows.Scan(
|
|
&i.ID,
|
|
&i.Title,
|
|
&i.AlbumID,
|
|
&i.ArtistID,
|
|
&i.TrackNumber,
|
|
&i.DiscNumber,
|
|
&i.DurationMs,
|
|
&i.FilePath,
|
|
&i.FileSize,
|
|
&i.FileFormat,
|
|
&i.Bitrate,
|
|
&i.Mbid,
|
|
&i.Genre,
|
|
&i.AddedAt,
|
|
&i.UpdatedAt,
|
|
&i.TagSource,
|
|
&i.TagSourcesVersion,
|
|
&i.TagReadVersion,
|
|
&i.MissingSince,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
items = append(items, i)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return items, nil
|
|
}
|
|
|
|
const unlikeAlbum = `-- name: UnlikeAlbum :exec
|
|
DELETE FROM general_likes_albums WHERE user_id = $1 AND album_id = $2
|
|
`
|
|
|
|
type UnlikeAlbumParams struct {
|
|
UserID pgtype.UUID
|
|
AlbumID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) UnlikeAlbum(ctx context.Context, arg UnlikeAlbumParams) error {
|
|
_, err := q.db.Exec(ctx, unlikeAlbum, arg.UserID, arg.AlbumID)
|
|
return err
|
|
}
|
|
|
|
const unlikeArtist = `-- name: UnlikeArtist :exec
|
|
DELETE FROM general_likes_artists WHERE user_id = $1 AND artist_id = $2
|
|
`
|
|
|
|
type UnlikeArtistParams struct {
|
|
UserID pgtype.UUID
|
|
ArtistID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) UnlikeArtist(ctx context.Context, arg UnlikeArtistParams) error {
|
|
_, err := q.db.Exec(ctx, unlikeArtist, arg.UserID, arg.ArtistID)
|
|
return err
|
|
}
|
|
|
|
const unlikeTrack = `-- name: UnlikeTrack :exec
|
|
DELETE FROM general_likes WHERE user_id = $1 AND track_id = $2
|
|
`
|
|
|
|
type UnlikeTrackParams struct {
|
|
UserID pgtype.UUID
|
|
TrackID pgtype.UUID
|
|
}
|
|
|
|
func (q *Queries) UnlikeTrack(ctx context.Context, arg UnlikeTrackParams) error {
|
|
_, err := q.db.Exec(ctx, unlikeTrack, arg.UserID, arg.TrackID)
|
|
return err
|
|
}
|