import base64
import extism
import json
@extism.import_fn("extism:host/user", "http_send")
def _http_send(offset: int) -> int: ...
def http_get(url):
"""GET url via Navidrome's HTTP host service. Returns (status_code, body_bytes)."""
request = json.dumps({"request": {"method": "GET", "url": url}}).encode("utf-8")
response_offset = _http_send(extism.memory.alloc(request).offset)
resp = json.loads(extism.memory.string(extism.memory.find(response_offset)))
if resp.get("error"):
raise Exception(f"HTTP request failed: {resp['error']}")
result = resp["result"]
return result["statusCode"], base64.b64decode(result.get("body", ""))
@extism.plugin_fn
def nd_get_album_images():
"""Retrieve album cover images from Cover Art Archive."""
input_data = extism.input_json()
mbid = input_data.get("mbid", "")
if not mbid:
raise Exception("not found: MBID required")
url = f"https://coverartarchive.org/release/{mbid}"
status, body = http_get(url)
if status != 200:
raise Exception(f"not found: CAA returned status {status}")
try:
data = json.loads(body)
except json.JSONDecodeError:
raise Exception("not found: invalid JSON response")
caa_images = data.get("images", [])
if not caa_images:
raise Exception("not found: no images in response")
front_image = find_front_image(caa_images)
if not front_image:
raise Exception("not found: no front cover image")
images = build_image_list(front_image)
if not images:
raise Exception("not found: no usable image URLs")
extism.output_str(json.dumps({"images": images}))
def find_front_image(images):
"""Find the front cover image from CAA response."""
for img in images:
if img.get("front", False):
return img
for img in images:
types = img.get("types", [])
if "Front" in types:
return img
if images:
return images[0]
return None
def build_image_list(img):
"""Build list of images with URLs and sizes from CAA image data."""
images = []
thumbnails = img.get("thumbnails", {})
for size_str, url in thumbnails.items():
if not url:
continue
try:
size = int(size_str)
images.append({"url": url, "size": size})
except ValueError:
pass
if not images:
size_map = {"large": 500, "small": 250}
for size_name, size in size_map.items():
url = thumbnails.get(size_name)
if url:
images.append({"url": url, "size": size})
if not images:
main_url = img.get("image")
if main_url:
images.append({"url": main_url, "size": 0})
return images