Files
minstrel/internal/db/dbq/likes.sql.go
T
bvandeusenandClaude Opus 5.5 5b372f61d7
release / govulncheck (push) Successful in 21s
release / web (push) Successful in 1m7s
release / go (push) Successful in 1m29s
release / integration (push) Successful in 4m11s
release / Attach APK to the Release (tag releases only) (push) Canceled after 0s
release / Build + push container image (push) Canceled after 0s
release / Verify release artifacts (tag releases only) (push) Canceled after 0s
release / android (push) Canceled after 5m58s
release / Build signed APK (releases and dev) (push) Canceled after 5m59s
feat: the same song on several releases counts as one song (M498 #5438)
A single and the album it is on stay two files, since each fulfils its own
release in Lidarr, but they are one song to the listener.

- tracks.song_id links copies; the generated song_key (song_id, else the
  track's own id) is what they share (migration 0076).
- The resolver links each cross-release group every pass (idempotent; only
  with auto-resolve on) and, when a link is new, shares existing likes across
  the song and logs them for sync.
- A like or unlike (web and Subsonic) reaches every copy; each change is
  logged and published so clients update every heart.
- The Liked list and its count show the song once; Shuffle and the mix writer
  take one copy per song.
- A merge keeps the removed copy's song link; "Not the same song" on the
  Across releases tab dismisses the group and undoes the link.

Shared plays ("heard via another copy") are left for a later step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:07:55 -04:00

409 lines
10 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(DISTINCT t.song_key) FROM general_likes l
JOIN tracks t ON t.id = l.track_id
WHERE l.user_id = $1
`
// Liked songs, each counted once however many releases hold it.
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 :many
INSERT INTO general_likes (user_id, track_id)
SELECT $1::uuid, t.id
FROM tracks t
WHERE t.song_key = (SELECT s.song_key FROM tracks s WHERE s.id = $2::uuid)
ON CONFLICT (user_id, track_id) DO NOTHING
RETURNING track_id
`
type LikeTrackParams struct {
UserID pgtype.UUID
TrackID pgtype.UUID
}
// A like is on the song (M498 #5438): every copy of it sharing the track's
// song_key is liked. Returns the tracks newly liked, for the sync log; empty
// when the song was already liked.
func (q *Queries) LikeTrack(ctx context.Context, arg LikeTrackParams) ([]pgtype.UUID, error) {
rows, err := q.db.Query(ctx, likeTrack, arg.UserID, arg.TrackID)
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 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, a.release_group_mbid 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,
&i.ReleaseGroupMbid,
); 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, t.mbid_source, t.song_id, t.song_key FROM tracks t
JOIN (SELECT DISTINCT ON (lt.song_key) l.track_id, l.liked_at
FROM general_likes l
JOIN tracks lt ON lt.id = l.track_id
WHERE l.user_id = $1
ORDER BY lt.song_key, (lt.missing_since IS NOT NULL), l.liked_at, lt.id) l
ON l.track_id = t.id
ORDER BY l.liked_at DESC, t.id
LIMIT $2 OFFSET $3
`
type ListLikedTrackRowsParams struct {
UserID pgtype.UUID
Limit int32
Offset int32
}
// One row per liked song: of its copies, the one present on disk, then the
// one liked first.
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,
&i.MbidSource,
&i.SongID,
&i.SongKey,
); 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 :many
DELETE FROM general_likes
WHERE user_id = $1::uuid
AND (track_id = $2::uuid
OR track_id IN (SELECT t.id FROM tracks t
WHERE t.song_key = (SELECT s.song_key FROM tracks s WHERE s.id = $2::uuid)))
RETURNING track_id
`
type UnlikeTrackParams struct {
UserID pgtype.UUID
TrackID pgtype.UUID
}
// Unlikes the song: every copy sharing the track's song_key. Returns the
// tracks unliked, for the sync log.
func (q *Queries) UnlikeTrack(ctx context.Context, arg UnlikeTrackParams) ([]pgtype.UUID, error) {
rows, err := q.db.Query(ctx, unlikeTrack, arg.UserID, arg.TrackID)
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
}