feat(requests): show ingest progress while requests are in flight

After Lidarr accepts a request the local reconciler imports albums and
tracks as they arrive — but until the request hit 'completed' the
operator had no way to see "is anything happening?" The /requests and
/admin/requests rows now surface a live progress line whenever the
request's matched entity has children in our library.

Backend:
- New CountAlbumsByArtist + CountTracksByArtist sqlc queries.
- requestView gains imported_album_count and imported_track_count.
- New fillProgress helper computes them from the matched entity:
  - kind=artist  → counts albums + tracks under matched_artist_id
  - kind=album   → counts tracks under matched_album_id
  - kind=track   → 1 once matched_track_id is set
  N+1 in the list endpoints; acceptable at admin scale.
- handleListRequests, handleGetRequest, and handleListAdminRequests
  populate the new fields on every response.

Frontend:
- LidarrRequest TS type extended with the two counters.
- Both the operator's /requests page and the admin /admin/requests
  page render an accent-colored line under the row meta when at
  least one counter is non-zero, e.g.:
    Artist:  "5 albums · 47 tracks ingested"
    Album:   "12 tracks ingested"
    Track:   "Track ingested"
- Updated test fixtures to include the new required fields.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-02 13:04:38 -04:00
parent 70d3d60d21
commit f77245bc41
13 changed files with 134 additions and 6 deletions
+13
View File
@@ -23,6 +23,19 @@ func (q *Queries) CountAlbums(ctx context.Context) (int64, error) {
return count, err
}
const countAlbumsByArtist = `-- name: CountAlbumsByArtist :one
SELECT COUNT(*) FROM albums WHERE artist_id = $1
`
// Used by request-progress reporting to count how many albums have been
// ingested for a matched artist while a request is still in flight.
func (q *Queries) CountAlbumsByArtist(ctx context.Context, artistID pgtype.UUID) (int64, error) {
row := q.db.QueryRow(ctx, countAlbumsByArtist, artistID)
var count int64
err := row.Scan(&count)
return count, err
}
const countAlbumsMatching = `-- name: CountAlbumsMatching :one
SELECT COUNT(*) FROM albums WHERE title ILIKE '%' || $1::text || '%'
`
+14
View File
@@ -22,6 +22,20 @@ func (q *Queries) CountTracksByAlbum(ctx context.Context, albumID pgtype.UUID) (
return count, err
}
const countTracksByArtist = `-- name: CountTracksByArtist :one
SELECT COUNT(*) FROM tracks WHERE artist_id = $1
`
// Used by request-progress reporting to count tracks ingested under a
// matched artist (sum across all the artist's albums) while a request
// is still in flight.
func (q *Queries) CountTracksByArtist(ctx context.Context, artistID pgtype.UUID) (int64, error) {
row := q.db.QueryRow(ctx, countTracksByArtist, artistID)
var count int64
err := row.Scan(&count)
return count, err
}
const countTracksMatching = `-- name: CountTracksMatching :one
SELECT COUNT(*) FROM tracks WHERE title ILIKE '%' || $1::text || '%'
`
+5
View File
@@ -80,3 +80,8 @@ LIMIT $1 OFFSET $2;
-- name: CountAlbums :one
-- M6a: total album count for the /api/library/albums envelope.
SELECT COUNT(*) FROM albums;
-- name: CountAlbumsByArtist :one
-- Used by request-progress reporting to count how many albums have been
-- ingested for a matched artist while a request is still in flight.
SELECT COUNT(*) FROM albums WHERE artist_id = $1;
+6
View File
@@ -90,3 +90,9 @@ WHERE t.artist_id = $1
)
ORDER BY albums.release_date NULLS LAST, albums.sort_title,
t.disc_number NULLS FIRST, t.track_number NULLS FIRST, t.id;
-- name: CountTracksByArtist :one
-- Used by request-progress reporting to count tracks ingested under a
-- matched artist (sum across all the artist's albums) while a request
-- is still in flight.
SELECT COUNT(*) FROM tracks WHERE artist_id = $1;