diff --git a/internal/db/dbq/recommendation.sql.go b/internal/db/dbq/recommendation.sql.go index 97a620df..dd1dea47 100644 --- a/internal/db/dbq/recommendation.sql.go +++ b/internal/db/dbq/recommendation.sql.go @@ -34,6 +34,10 @@ WHERE t.id <> $2 WHERE user_id = $1 AND track_id = t.id AND started_at > now() - $3 * interval '1 hour' ) + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $1 AND q.track_id = t.id + ) ` type LoadRadioCandidatesParams struct { @@ -117,6 +121,10 @@ excluded_ids AS ( SELECT pe.track_id AS id FROM play_events pe WHERE pe.user_id = $1 AND pe.started_at > now() - $3 * interval '1 hour' + UNION ALL + SELECT q.track_id AS id + FROM lidarr_quarantine q + WHERE q.user_id = $1 ), lb_similar AS ( SELECT ts.track_b_id AS track_id, ts.score AS sim_score diff --git a/internal/db/dbq/tracks.sql.go b/internal/db/dbq/tracks.sql.go index d17447c9..65486ad8 100644 --- a/internal/db/dbq/tracks.sql.go +++ b/internal/db/dbq/tracks.sql.go @@ -33,6 +33,28 @@ func (q *Queries) CountTracksMatching(ctx context.Context, dollar_1 string) (int return count, err } +const countTracksMatchingForUser = `-- name: CountTracksMatchingForUser :one +SELECT COUNT(*) FROM tracks +WHERE title ILIKE '%' || $1::text || '%' + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $2 AND q.track_id = tracks.id + ) +` + +type CountTracksMatchingForUserParams struct { + Column1 string + UserID pgtype.UUID +} + +// $1 = title query, $2 = user_id. +func (q *Queries) CountTracksMatchingForUser(ctx context.Context, arg CountTracksMatchingForUserParams) (int64, error) { + row := q.db.QueryRow(ctx, countTracksMatchingForUser, arg.Column1, arg.UserID) + var count int64 + err := row.Scan(&count) + return count, err +} + const getTrackByID = `-- name: GetTrackByID :one SELECT id, title, album_id, artist_id, track_number, disc_number, duration_ms, file_path, file_size, file_format, bitrate, mbid, genre, added_at, updated_at FROM tracks WHERE id = $1 ` @@ -127,6 +149,59 @@ func (q *Queries) ListTracksByAlbum(ctx context.Context, albumID pgtype.UUID) ([ return items, nil } +const listTracksByAlbumForUser = `-- name: ListTracksByAlbumForUser :many +SELECT id, title, album_id, artist_id, track_number, disc_number, duration_ms, file_path, file_size, file_format, bitrate, mbid, genre, added_at, updated_at FROM tracks +WHERE album_id = $1 + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $2 AND q.track_id = tracks.id + ) +ORDER BY disc_number NULLS LAST, track_number NULLS LAST +` + +type ListTracksByAlbumForUserParams struct { + AlbumID pgtype.UUID + UserID pgtype.UUID +} + +// Same as ListTracksByAlbum but excludes tracks the user has quarantined. +// $1 = album_id, $2 = user_id. +func (q *Queries) ListTracksByAlbumForUser(ctx context.Context, arg ListTracksByAlbumForUserParams) ([]Track, error) { + rows, err := q.db.Query(ctx, listTracksByAlbumForUser, arg.AlbumID, arg.UserID) + 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, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const searchTracks = `-- name: SearchTracks :many SELECT id, title, album_id, artist_id, track_number, disc_number, duration_ms, file_path, file_size, file_format, bitrate, mbid, genre, added_at, updated_at FROM tracks WHERE title ILIKE '%' || $1 || '%' @@ -176,6 +251,66 @@ func (q *Queries) SearchTracks(ctx context.Context, arg SearchTracksParams) ([]T return items, nil } +const searchTracksForUser = `-- name: SearchTracksForUser :many +SELECT id, title, album_id, artist_id, track_number, disc_number, duration_ms, file_path, file_size, file_format, bitrate, mbid, genre, added_at, updated_at FROM tracks +WHERE title ILIKE '%' || $1 || '%' + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $2 AND q.track_id = tracks.id + ) +ORDER BY title +LIMIT $3 OFFSET $4 +` + +type SearchTracksForUserParams struct { + Column1 *string + UserID pgtype.UUID + Limit int32 + Offset int32 +} + +// $1 = title query, $2 = user_id, $3 = limit, $4 = offset. +func (q *Queries) SearchTracksForUser(ctx context.Context, arg SearchTracksForUserParams) ([]Track, error) { + rows, err := q.db.Query(ctx, searchTracksForUser, + arg.Column1, + 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, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + const upsertTrack = `-- name: UpsertTrack :one INSERT INTO tracks ( title, album_id, artist_id, track_number, disc_number, diff --git a/internal/db/queries/recommendation.sql b/internal/db/queries/recommendation.sql index 10e81601..f3dc081c 100644 --- a/internal/db/queries/recommendation.sql +++ b/internal/db/queries/recommendation.sql @@ -26,6 +26,10 @@ WHERE t.id <> $2 SELECT 1 FROM play_events WHERE user_id = $1 AND track_id = t.id AND started_at > now() - $3 * interval '1 hour' + ) + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $1 AND q.track_id = t.id ); -- name: LoadRadioCandidatesV2 :many @@ -53,6 +57,10 @@ excluded_ids AS ( SELECT pe.track_id AS id FROM play_events pe WHERE pe.user_id = $1 AND pe.started_at > now() - $3 * interval '1 hour' + UNION ALL + SELECT q.track_id AS id + FROM lidarr_quarantine q + WHERE q.user_id = $1 ), lb_similar AS ( SELECT ts.track_b_id AS track_id, ts.score AS sim_score diff --git a/internal/db/queries/tracks.sql b/internal/db/queries/tracks.sql index 04bffc56..d9b06e1f 100644 --- a/internal/db/queries/tracks.sql +++ b/internal/db/queries/tracks.sql @@ -39,3 +39,34 @@ LIMIT $2 OFFSET $3; -- name: CountTracksMatching :one SELECT COUNT(*) FROM tracks WHERE title ILIKE '%' || $1::text || '%'; + +-- name: ListTracksByAlbumForUser :many +-- Same as ListTracksByAlbum but excludes tracks the user has quarantined. +-- $1 = album_id, $2 = user_id. +SELECT * FROM tracks +WHERE album_id = $1 + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $2 AND q.track_id = tracks.id + ) +ORDER BY disc_number NULLS LAST, track_number NULLS LAST; + +-- name: SearchTracksForUser :many +-- $1 = title query, $2 = user_id, $3 = limit, $4 = offset. +SELECT * FROM tracks +WHERE title ILIKE '%' || $1 || '%' + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $2 AND q.track_id = tracks.id + ) +ORDER BY title +LIMIT $3 OFFSET $4; + +-- name: CountTracksMatchingForUser :one +-- $1 = title query, $2 = user_id. +SELECT COUNT(*) FROM tracks +WHERE title ILIKE '%' || $1::text || '%' + AND NOT EXISTS ( + SELECT 1 FROM lidarr_quarantine q + WHERE q.user_id = $2 AND q.track_id = tracks.id + );