feat(db): add lidarr_config + lidarr_requests schema (migration 0010)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: albums.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: artists.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: contextual_likes.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
|
||||
package dbq
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: events.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: lidarr_config.sql
|
||||
|
||||
package dbq
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const getLidarrConfig = `-- name: GetLidarrConfig :one
|
||||
SELECT id, enabled, base_url, api_key, default_quality_profile_id,
|
||||
default_root_folder_path, created_at, updated_at
|
||||
FROM lidarr_config
|
||||
WHERE id = 1
|
||||
`
|
||||
|
||||
func (q *Queries) GetLidarrConfig(ctx context.Context) (LidarrConfig, error) {
|
||||
row := q.db.QueryRow(ctx, getLidarrConfig)
|
||||
var i LidarrConfig
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Enabled,
|
||||
&i.BaseUrl,
|
||||
&i.ApiKey,
|
||||
&i.DefaultQualityProfileID,
|
||||
&i.DefaultRootFolderPath,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateLidarrConfig = `-- name: UpdateLidarrConfig :one
|
||||
UPDATE lidarr_config
|
||||
SET enabled = $1,
|
||||
base_url = $2,
|
||||
api_key = $3,
|
||||
default_quality_profile_id = $4,
|
||||
default_root_folder_path = $5,
|
||||
updated_at = now()
|
||||
WHERE id = 1
|
||||
RETURNING id, enabled, base_url, api_key, default_quality_profile_id,
|
||||
default_root_folder_path, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateLidarrConfigParams struct {
|
||||
Enabled bool
|
||||
BaseUrl *string
|
||||
ApiKey *string
|
||||
DefaultQualityProfileID *int32
|
||||
DefaultRootFolderPath *string
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateLidarrConfig(ctx context.Context, arg UpdateLidarrConfigParams) (LidarrConfig, error) {
|
||||
row := q.db.QueryRow(ctx, updateLidarrConfig,
|
||||
arg.Enabled,
|
||||
arg.BaseUrl,
|
||||
arg.ApiKey,
|
||||
arg.DefaultQualityProfileID,
|
||||
arg.DefaultRootFolderPath,
|
||||
)
|
||||
var i LidarrConfig
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Enabled,
|
||||
&i.BaseUrl,
|
||||
&i.ApiKey,
|
||||
&i.DefaultQualityProfileID,
|
||||
&i.DefaultRootFolderPath,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -0,0 +1,480 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.31.1
|
||||
// source: lidarr_requests.sql
|
||||
|
||||
package dbq
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const approveLidarrRequest = `-- name: ApproveLidarrRequest :one
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'approved',
|
||||
quality_profile_id = $2,
|
||||
root_folder_path = $3,
|
||||
decided_at = now(),
|
||||
decided_by = $4,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND status = 'pending'
|
||||
RETURNING id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
||||
`
|
||||
|
||||
type ApproveLidarrRequestParams struct {
|
||||
ID pgtype.UUID
|
||||
QualityProfileID *int32
|
||||
RootFolderPath *string
|
||||
DecidedBy pgtype.UUID
|
||||
}
|
||||
|
||||
func (q *Queries) ApproveLidarrRequest(ctx context.Context, arg ApproveLidarrRequestParams) (LidarrRequest, error) {
|
||||
row := q.db.QueryRow(ctx, approveLidarrRequest,
|
||||
arg.ID,
|
||||
arg.QualityProfileID,
|
||||
arg.RootFolderPath,
|
||||
arg.DecidedBy,
|
||||
)
|
||||
var i LidarrRequest
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const cancelLidarrRequest = `-- name: CancelLidarrRequest :one
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'rejected',
|
||||
notes = 'cancelled by user',
|
||||
decided_at = now(),
|
||||
decided_by = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND user_id = $2 AND status = 'pending'
|
||||
RETURNING id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
||||
`
|
||||
|
||||
type CancelLidarrRequestParams struct {
|
||||
ID pgtype.UUID
|
||||
DecidedBy pgtype.UUID
|
||||
}
|
||||
|
||||
func (q *Queries) CancelLidarrRequest(ctx context.Context, arg CancelLidarrRequestParams) (LidarrRequest, error) {
|
||||
row := q.db.QueryRow(ctx, cancelLidarrRequest, arg.ID, arg.DecidedBy)
|
||||
var i LidarrRequest
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const completeLidarrRequest = `-- name: CompleteLidarrRequest :one
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'completed',
|
||||
matched_track_id = $2,
|
||||
matched_album_id = $3,
|
||||
matched_artist_id = $4,
|
||||
completed_at = now(),
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND status = 'approved'
|
||||
RETURNING id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
||||
`
|
||||
|
||||
type CompleteLidarrRequestParams struct {
|
||||
ID pgtype.UUID
|
||||
MatchedTrackID pgtype.UUID
|
||||
MatchedAlbumID pgtype.UUID
|
||||
MatchedArtistID pgtype.UUID
|
||||
}
|
||||
|
||||
// Reconciler transitions an approved request to completed when its
|
||||
// target track/album/artist has appeared in the library.
|
||||
func (q *Queries) CompleteLidarrRequest(ctx context.Context, arg CompleteLidarrRequestParams) (LidarrRequest, error) {
|
||||
row := q.db.QueryRow(ctx, completeLidarrRequest,
|
||||
arg.ID,
|
||||
arg.MatchedTrackID,
|
||||
arg.MatchedAlbumID,
|
||||
arg.MatchedArtistID,
|
||||
)
|
||||
var i LidarrRequest
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createLidarrRequest = `-- name: CreateLidarrRequest :one
|
||||
INSERT INTO lidarr_requests (
|
||||
user_id, kind,
|
||||
lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid,
|
||||
artist_name, album_title, track_title
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
||||
`
|
||||
|
||||
type CreateLidarrRequestParams struct {
|
||||
UserID pgtype.UUID
|
||||
Kind LidarrRequestKind
|
||||
LidarrArtistMbid string
|
||||
LidarrAlbumMbid *string
|
||||
LidarrTrackMbid *string
|
||||
ArtistName string
|
||||
AlbumTitle *string
|
||||
TrackTitle *string
|
||||
}
|
||||
|
||||
func (q *Queries) CreateLidarrRequest(ctx context.Context, arg CreateLidarrRequestParams) (LidarrRequest, error) {
|
||||
row := q.db.QueryRow(ctx, createLidarrRequest,
|
||||
arg.UserID,
|
||||
arg.Kind,
|
||||
arg.LidarrArtistMbid,
|
||||
arg.LidarrAlbumMbid,
|
||||
arg.LidarrTrackMbid,
|
||||
arg.ArtistName,
|
||||
arg.AlbumTitle,
|
||||
arg.TrackTitle,
|
||||
)
|
||||
var i LidarrRequest
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getLidarrRequestByID = `-- name: GetLidarrRequestByID :one
|
||||
SELECT id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM lidarr_requests WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetLidarrRequestByID(ctx context.Context, id pgtype.UUID) (LidarrRequest, error) {
|
||||
row := q.db.QueryRow(ctx, getLidarrRequestByID, id)
|
||||
var i LidarrRequest
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const hasNonTerminalRequestForMBID = `-- name: HasNonTerminalRequestForMBID :one
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM lidarr_requests
|
||||
WHERE status IN ('pending', 'approved', 'completed')
|
||||
AND ((kind = 'artist' AND lidarr_artist_mbid = $1)
|
||||
OR (kind = 'album' AND lidarr_album_mbid = $1)
|
||||
OR (kind = 'track' AND lidarr_track_mbid = $1))
|
||||
)
|
||||
`
|
||||
|
||||
// Returns true if any user has a pending/approved/completed request
|
||||
// whose MBID matches at the given level. Used to set the `requested`
|
||||
// flag on /api/lidarr/search responses. Terminal-status (rejected,
|
||||
// failed) rows do not count.
|
||||
func (q *Queries) HasNonTerminalRequestForMBID(ctx context.Context, lidarrArtistMbid string) (bool, error) {
|
||||
row := q.db.QueryRow(ctx, hasNonTerminalRequestForMBID, lidarrArtistMbid)
|
||||
var exists bool
|
||||
err := row.Scan(&exists)
|
||||
return exists, err
|
||||
}
|
||||
|
||||
const listApprovedLidarrRequestsForReconcile = `-- name: ListApprovedLidarrRequestsForReconcile :many
|
||||
SELECT id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM lidarr_requests
|
||||
WHERE status = 'approved'
|
||||
ORDER BY decided_at ASC
|
||||
LIMIT $1
|
||||
`
|
||||
|
||||
func (q *Queries) ListApprovedLidarrRequestsForReconcile(ctx context.Context, limit int32) ([]LidarrRequest, error) {
|
||||
rows, err := q.db.Query(ctx, listApprovedLidarrRequestsForReconcile, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []LidarrRequest
|
||||
for rows.Next() {
|
||||
var i LidarrRequest
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listLidarrRequestsByStatus = `-- name: ListLidarrRequestsByStatus :many
|
||||
SELECT id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM lidarr_requests
|
||||
WHERE status = $1
|
||||
ORDER BY requested_at DESC
|
||||
LIMIT $2
|
||||
`
|
||||
|
||||
type ListLidarrRequestsByStatusParams struct {
|
||||
Status LidarrRequestStatus
|
||||
Limit int32
|
||||
}
|
||||
|
||||
func (q *Queries) ListLidarrRequestsByStatus(ctx context.Context, arg ListLidarrRequestsByStatusParams) ([]LidarrRequest, error) {
|
||||
rows, err := q.db.Query(ctx, listLidarrRequestsByStatus, arg.Status, arg.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []LidarrRequest
|
||||
for rows.Next() {
|
||||
var i LidarrRequest
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const listLidarrRequestsForUser = `-- name: ListLidarrRequestsForUser :many
|
||||
SELECT id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at FROM lidarr_requests
|
||||
WHERE user_id = $1
|
||||
ORDER BY requested_at DESC
|
||||
LIMIT $2
|
||||
`
|
||||
|
||||
type ListLidarrRequestsForUserParams struct {
|
||||
UserID pgtype.UUID
|
||||
Limit int32
|
||||
}
|
||||
|
||||
func (q *Queries) ListLidarrRequestsForUser(ctx context.Context, arg ListLidarrRequestsForUserParams) ([]LidarrRequest, error) {
|
||||
rows, err := q.db.Query(ctx, listLidarrRequestsForUser, arg.UserID, arg.Limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []LidarrRequest
|
||||
for rows.Next() {
|
||||
var i LidarrRequest
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const rejectLidarrRequest = `-- name: RejectLidarrRequest :one
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'rejected',
|
||||
notes = $2,
|
||||
decided_at = now(),
|
||||
decided_by = $3,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND status = 'pending'
|
||||
RETURNING id, user_id, status, kind, lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid, artist_name, album_title, track_title, quality_profile_id, root_folder_path, decided_at, decided_by, notes, completed_at, matched_track_id, matched_album_id, matched_artist_id, requested_at, updated_at
|
||||
`
|
||||
|
||||
type RejectLidarrRequestParams struct {
|
||||
ID pgtype.UUID
|
||||
Notes *string
|
||||
DecidedBy pgtype.UUID
|
||||
}
|
||||
|
||||
func (q *Queries) RejectLidarrRequest(ctx context.Context, arg RejectLidarrRequestParams) (LidarrRequest, error) {
|
||||
row := q.db.QueryRow(ctx, rejectLidarrRequest, arg.ID, arg.Notes, arg.DecidedBy)
|
||||
var i LidarrRequest
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.UserID,
|
||||
&i.Status,
|
||||
&i.Kind,
|
||||
&i.LidarrArtistMbid,
|
||||
&i.LidarrAlbumMbid,
|
||||
&i.LidarrTrackMbid,
|
||||
&i.ArtistName,
|
||||
&i.AlbumTitle,
|
||||
&i.TrackTitle,
|
||||
&i.QualityProfileID,
|
||||
&i.RootFolderPath,
|
||||
&i.DecidedAt,
|
||||
&i.DecidedBy,
|
||||
&i.Notes,
|
||||
&i.CompletedAt,
|
||||
&i.MatchedTrackID,
|
||||
&i.MatchedAlbumID,
|
||||
&i.MatchedArtistID,
|
||||
&i.RequestedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: likes.sql
|
||||
|
||||
package dbq
|
||||
|
||||
+127
-1
@@ -1,13 +1,104 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
|
||||
package dbq
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type LidarrRequestKind string
|
||||
|
||||
const (
|
||||
LidarrRequestKindArtist LidarrRequestKind = "artist"
|
||||
LidarrRequestKindAlbum LidarrRequestKind = "album"
|
||||
LidarrRequestKindTrack LidarrRequestKind = "track"
|
||||
)
|
||||
|
||||
func (e *LidarrRequestKind) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = LidarrRequestKind(s)
|
||||
case string:
|
||||
*e = LidarrRequestKind(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for LidarrRequestKind: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullLidarrRequestKind struct {
|
||||
LidarrRequestKind LidarrRequestKind
|
||||
Valid bool // Valid is true if LidarrRequestKind is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullLidarrRequestKind) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.LidarrRequestKind, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.LidarrRequestKind.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullLidarrRequestKind) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.LidarrRequestKind), nil
|
||||
}
|
||||
|
||||
type LidarrRequestStatus string
|
||||
|
||||
const (
|
||||
LidarrRequestStatusPending LidarrRequestStatus = "pending"
|
||||
LidarrRequestStatusApproved LidarrRequestStatus = "approved"
|
||||
LidarrRequestStatusRejected LidarrRequestStatus = "rejected"
|
||||
LidarrRequestStatusCompleted LidarrRequestStatus = "completed"
|
||||
LidarrRequestStatusFailed LidarrRequestStatus = "failed"
|
||||
)
|
||||
|
||||
func (e *LidarrRequestStatus) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = LidarrRequestStatus(s)
|
||||
case string:
|
||||
*e = LidarrRequestStatus(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for LidarrRequestStatus: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullLidarrRequestStatus struct {
|
||||
LidarrRequestStatus LidarrRequestStatus
|
||||
Valid bool // Valid is true if LidarrRequestStatus is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullLidarrRequestStatus) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.LidarrRequestStatus, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.LidarrRequestStatus.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullLidarrRequestStatus) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.LidarrRequestStatus), nil
|
||||
}
|
||||
|
||||
type Album struct {
|
||||
ID pgtype.UUID
|
||||
Title string
|
||||
@@ -65,6 +156,41 @@ type GeneralLikesArtist struct {
|
||||
LikedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type LidarrConfig struct {
|
||||
ID int16
|
||||
Enabled bool
|
||||
BaseUrl *string
|
||||
ApiKey *string
|
||||
DefaultQualityProfileID *int32
|
||||
DefaultRootFolderPath *string
|
||||
CreatedAt pgtype.Timestamptz
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type LidarrRequest struct {
|
||||
ID pgtype.UUID
|
||||
UserID pgtype.UUID
|
||||
Status LidarrRequestStatus
|
||||
Kind LidarrRequestKind
|
||||
LidarrArtistMbid string
|
||||
LidarrAlbumMbid *string
|
||||
LidarrTrackMbid *string
|
||||
ArtistName string
|
||||
AlbumTitle *string
|
||||
TrackTitle *string
|
||||
QualityProfileID *int32
|
||||
RootFolderPath *string
|
||||
DecidedAt pgtype.Timestamptz
|
||||
DecidedBy pgtype.UUID
|
||||
Notes *string
|
||||
CompletedAt pgtype.Timestamptz
|
||||
MatchedTrackID pgtype.UUID
|
||||
MatchedAlbumID pgtype.UUID
|
||||
MatchedArtistID pgtype.UUID
|
||||
RequestedAt pgtype.Timestamptz
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type PlayEvent struct {
|
||||
ID pgtype.UUID
|
||||
UserID pgtype.UUID
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: recommendation.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: scrobble.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: sessions.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: similarity.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: tracks.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.27.0
|
||||
// sqlc v1.31.1
|
||||
// source: users.sql
|
||||
|
||||
package dbq
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
DROP INDEX IF EXISTS lidarr_requests_album_mbid_idx;
|
||||
DROP INDEX IF EXISTS lidarr_requests_artist_mbid_idx;
|
||||
DROP INDEX IF EXISTS lidarr_requests_status_idx;
|
||||
DROP INDEX IF EXISTS lidarr_requests_user_id_idx;
|
||||
DROP TABLE IF EXISTS lidarr_requests;
|
||||
DROP TYPE IF EXISTS lidarr_request_kind;
|
||||
DROP TYPE IF EXISTS lidarr_request_status;
|
||||
DROP TABLE IF EXISTS lidarr_config;
|
||||
@@ -0,0 +1,63 @@
|
||||
-- M5a: Lidarr integration foundation. Two tables:
|
||||
--
|
||||
-- lidarr_config — singleton (CHECK id=1) holding the operator's Lidarr
|
||||
-- connection. enabled=false is the unconfigured state.
|
||||
--
|
||||
-- lidarr_requests — per-request lifecycle row created by users at
|
||||
-- /discover, transitioned by admin at /admin/requests, and matched
|
||||
-- back to library tracks by the reconciler worker. Three matched_*_id
|
||||
-- FKs (one per kind) instead of polymorphic — clean SQL, ON DELETE
|
||||
-- SET NULL preserves audit even if the matched track is later removed.
|
||||
|
||||
CREATE TABLE lidarr_config (
|
||||
id smallint PRIMARY KEY DEFAULT 1 CHECK (id = 1),
|
||||
enabled boolean NOT NULL DEFAULT false,
|
||||
base_url text,
|
||||
api_key text,
|
||||
default_quality_profile_id int,
|
||||
default_root_folder_path text,
|
||||
created_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
INSERT INTO lidarr_config (id, enabled) VALUES (1, false);
|
||||
|
||||
CREATE TYPE lidarr_request_status AS ENUM (
|
||||
'pending', 'approved', 'rejected', 'completed', 'failed'
|
||||
);
|
||||
CREATE TYPE lidarr_request_kind AS ENUM ('artist', 'album', 'track');
|
||||
|
||||
CREATE TABLE lidarr_requests (
|
||||
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
status lidarr_request_status NOT NULL DEFAULT 'pending',
|
||||
kind lidarr_request_kind NOT NULL,
|
||||
|
||||
lidarr_artist_mbid text NOT NULL,
|
||||
lidarr_album_mbid text,
|
||||
lidarr_track_mbid text,
|
||||
artist_name text NOT NULL,
|
||||
album_title text,
|
||||
track_title text,
|
||||
|
||||
quality_profile_id int,
|
||||
root_folder_path text,
|
||||
|
||||
decided_at timestamptz,
|
||||
decided_by uuid REFERENCES users(id) ON DELETE SET NULL,
|
||||
notes text,
|
||||
|
||||
completed_at timestamptz,
|
||||
matched_track_id uuid REFERENCES tracks(id) ON DELETE SET NULL,
|
||||
matched_album_id uuid REFERENCES albums(id) ON DELETE SET NULL,
|
||||
matched_artist_id uuid REFERENCES artists(id) ON DELETE SET NULL,
|
||||
|
||||
requested_at timestamptz NOT NULL DEFAULT now(),
|
||||
updated_at timestamptz NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX lidarr_requests_user_id_idx ON lidarr_requests (user_id);
|
||||
CREATE INDEX lidarr_requests_status_idx ON lidarr_requests (status);
|
||||
CREATE INDEX lidarr_requests_artist_mbid_idx ON lidarr_requests (lidarr_artist_mbid);
|
||||
CREATE INDEX lidarr_requests_album_mbid_idx ON lidarr_requests (lidarr_album_mbid)
|
||||
WHERE lidarr_album_mbid IS NOT NULL;
|
||||
@@ -0,0 +1,17 @@
|
||||
-- name: GetLidarrConfig :one
|
||||
SELECT id, enabled, base_url, api_key, default_quality_profile_id,
|
||||
default_root_folder_path, created_at, updated_at
|
||||
FROM lidarr_config
|
||||
WHERE id = 1;
|
||||
|
||||
-- name: UpdateLidarrConfig :one
|
||||
UPDATE lidarr_config
|
||||
SET enabled = $1,
|
||||
base_url = $2,
|
||||
api_key = $3,
|
||||
default_quality_profile_id = $4,
|
||||
default_root_folder_path = $5,
|
||||
updated_at = now()
|
||||
WHERE id = 1
|
||||
RETURNING id, enabled, base_url, api_key, default_quality_profile_id,
|
||||
default_root_folder_path, created_at, updated_at;
|
||||
@@ -0,0 +1,85 @@
|
||||
-- name: CreateLidarrRequest :one
|
||||
INSERT INTO lidarr_requests (
|
||||
user_id, kind,
|
||||
lidarr_artist_mbid, lidarr_album_mbid, lidarr_track_mbid,
|
||||
artist_name, album_title, track_title
|
||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetLidarrRequestByID :one
|
||||
SELECT * FROM lidarr_requests WHERE id = $1;
|
||||
|
||||
-- name: ListLidarrRequestsForUser :many
|
||||
SELECT * FROM lidarr_requests
|
||||
WHERE user_id = $1
|
||||
ORDER BY requested_at DESC
|
||||
LIMIT $2;
|
||||
|
||||
-- name: ListLidarrRequestsByStatus :many
|
||||
SELECT * FROM lidarr_requests
|
||||
WHERE status = $1
|
||||
ORDER BY requested_at DESC
|
||||
LIMIT $2;
|
||||
|
||||
-- name: ListApprovedLidarrRequestsForReconcile :many
|
||||
SELECT * FROM lidarr_requests
|
||||
WHERE status = 'approved'
|
||||
ORDER BY decided_at ASC
|
||||
LIMIT $1;
|
||||
|
||||
-- name: ApproveLidarrRequest :one
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'approved',
|
||||
quality_profile_id = $2,
|
||||
root_folder_path = $3,
|
||||
decided_at = now(),
|
||||
decided_by = $4,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND status = 'pending'
|
||||
RETURNING *;
|
||||
|
||||
-- name: RejectLidarrRequest :one
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'rejected',
|
||||
notes = $2,
|
||||
decided_at = now(),
|
||||
decided_by = $3,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND status = 'pending'
|
||||
RETURNING *;
|
||||
|
||||
-- name: CancelLidarrRequest :one
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'rejected',
|
||||
notes = 'cancelled by user',
|
||||
decided_at = now(),
|
||||
decided_by = $2,
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND user_id = $2 AND status = 'pending'
|
||||
RETURNING *;
|
||||
|
||||
-- name: CompleteLidarrRequest :one
|
||||
-- Reconciler transitions an approved request to completed when its
|
||||
-- target track/album/artist has appeared in the library.
|
||||
UPDATE lidarr_requests
|
||||
SET status = 'completed',
|
||||
matched_track_id = $2,
|
||||
matched_album_id = $3,
|
||||
matched_artist_id = $4,
|
||||
completed_at = now(),
|
||||
updated_at = now()
|
||||
WHERE id = $1 AND status = 'approved'
|
||||
RETURNING *;
|
||||
|
||||
-- name: HasNonTerminalRequestForMBID :one
|
||||
-- Returns true if any user has a pending/approved/completed request
|
||||
-- whose MBID matches at the given level. Used to set the `requested`
|
||||
-- flag on /api/lidarr/search responses. Terminal-status (rejected,
|
||||
-- failed) rows do not count.
|
||||
SELECT EXISTS (
|
||||
SELECT 1 FROM lidarr_requests
|
||||
WHERE status IN ('pending', 'approved', 'completed')
|
||||
AND ((kind = 'artist' AND lidarr_artist_mbid = $1)
|
||||
OR (kind = 'album' AND lidarr_album_mbid = $1)
|
||||
OR (kind = 'track' AND lidarr_track_mbid = $1))
|
||||
);
|
||||
Reference in New Issue
Block a user