fix: index albums through the endpoint Lidarr does not throw from
Build and publish container / build (pull_request) Successful in 8m1s
Build and publish container / build (pull_request) Successful in 8m1s
Indexing fetched albums one artist at a time, and `GET /api/v1/album?artistId=` is Lidarr's unguarded path. It maps straight from the album service with no hydration: the mapper then dereferences model.Images and model.SecondaryTypes without a null check, follows model.Artist?.Value where only the first link is guarded, and selects the monitored release with SingleOrDefault, which throws outright when an album has two of them. Any of those is a 500 that aborts the whole index. The unfiltered `GET /api/v1/album` builds its own artist and release lookups and skips an album whose metadata is missing rather than dereferencing it. Use that instead, once, and group by artistId locally. It is the defensive path and it costs N fewer requests. Tracks and files have no unfiltered endpoint -- Lidarr rejects a call with no filter at all -- so those stay per artist. A failure on one artist now skips that artist rather than ending the run, but the count is recorded in the store and the coverage report leads with it: a missing artist makes their played music look unplayed, which is precisely the error that costs music later, so an incomplete index must not be culled against. Errors now carry the request URL and whatever the server put in the body. The original report of this failure was "album: HTTP 500", which points at the URL and the credentials -- neither of which was at fault.
This commit is contained in:
+23
-2
@@ -1,6 +1,8 @@
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
|
||||
import pytest
|
||||
@@ -128,7 +130,10 @@ class FakeLidarr:
|
||||
exercise the same stitching the real thing does.
|
||||
"""
|
||||
|
||||
def __init__(self, artists=()):
|
||||
def __init__(self, artists=(), fail=()):
|
||||
# (path, artistId) pairs the fake refuses to serve, standing in for the
|
||||
# 500s Lidarr returns on data it cannot hydrate.
|
||||
self.fail = set(fail)
|
||||
self.artists, self.albums, self.tracks, self.files = [], [], [], []
|
||||
for artist_index, entry in enumerate(artists, start=1):
|
||||
artist_id = artist_index
|
||||
@@ -190,10 +195,26 @@ class FakeLidarr:
|
||||
}
|
||||
self.calls.append((path, query))
|
||||
|
||||
artist_id = int(query.get("artistId", 0))
|
||||
if (path, artist_id) in self.fail:
|
||||
raise urllib.error.HTTPError(
|
||||
url, 500, "Internal Server Error", {}, io.BytesIO(b'{"message": "boom"}')
|
||||
)
|
||||
|
||||
if path == "artist":
|
||||
return json.dumps(self.artists)
|
||||
artist_id = int(query.get("artistId", 0))
|
||||
source = {"album": self.albums, "track": self.tracks, "trackfile": self.files}[path]
|
||||
if path == "album" and not artist_id:
|
||||
# Lidarr's unfiltered album endpoint returns the lot.
|
||||
return json.dumps(source)
|
||||
if not artist_id:
|
||||
raise urllib.error.HTTPError(
|
||||
url,
|
||||
400,
|
||||
"Bad Request",
|
||||
{},
|
||||
io.BytesIO(b'{"message": "artistId must be provided"}'),
|
||||
)
|
||||
return json.dumps([row for row in source if row["artistId"] == artist_id])
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user