Files
twislaandClaude Opus 5.5 91e4519503 Add MA_URL to download covers through a reverse proxy
The jail can't reach Music Assistant's port 8095. MA_URL replaces the host of Music Assistant's image URLs when downloading. fetch-covers now stops at the first connection failure instead of stalling on every album, and image connections time out after 3 seconds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 21:34:41 +00:00

78 lines
3.3 KiB
Python

import json
import httpx
import pytest
from tag_albums.library import Album, MusicLibrary
def test_search_albums_parses_music_assistant_response():
calls = []
def handler(request: httpx.Request) -> httpx.Response:
calls.append(request)
if request.url.path == "/api/config/config_entries/entry":
return httpx.Response(200, json=[{"entry_id": "ma-entry"}])
body = json.loads(request.content)
assert body == {"config_entry_id": "ma-entry", "name": "lateralus", "media_type": ["album"], "limit": 12}
return httpx.Response(200, json={"service_response": {"albums": [
{"name": "Lateralus", "artists": [{"name": "Tool"}, {"name": "Guest"}], "image": "http://img/1"},
{"name": "No Artist", "artists": []},
]}})
client = httpx.Client(base_url="http://ha", transport=httpx.MockTransport(handler))
library = MusicLibrary("http://ha", "token", client=client)
albums = library.search_albums("lateralus")
assert [(a.artist, a.album, a.image) for a in albums] == [
("Tool", "Lateralus", "http://img/1"),
("", "No Artist", None),
]
library.search_albums("lateralus")
# The Music Assistant config entry is looked up once, then cached.
assert sum(c.url.path == "/api/config/config_entries/entry" for c in calls) == 1
def _library_with_images(responses):
seen = []
def handler(request: httpx.Request) -> httpx.Response:
seen.append(request)
return responses[str(request.url)]
images = httpx.Client(transport=httpx.MockTransport(handler))
return MusicLibrary("http://ha", "ha-token", client=httpx.Client(), image_client=images), seen
def test_fetch_image_checks_type_and_never_sends_ha_token():
library, seen = _library_with_images({
"http://ma/cover": httpx.Response(200, content=b"jpg", headers={"content-type": "image/jpeg"}),
"http://ma/page": httpx.Response(200, content=b"<html>", headers={"content-type": "text/html"}),
})
assert library.fetch_image("http://ma/cover") == (b"jpg", "image/jpeg")
assert "authorization" not in seen[0].headers
with pytest.raises(ValueError):
library.fetch_image("http://ma/page")
def test_find_album_needs_exact_names():
library = MusicLibrary("http://ha", "t", client=httpx.Client())
library.search_albums = lambda query, limit=12: [
Album("Tool", "Lateralus (Live)", "http://img/live"),
Album("TOOL", "lateralus", "http://img/1"),
]
assert library.find_album("Tool", "Lateralus").image == "http://img/1"
assert library.find_album("Mogwai", "Lateralus") is None
def test_ma_url_replaces_music_assistant_host_for_downloads():
library, seen = _library_with_images({
"https://ma.example/imageproxy/abc?size=0":
httpx.Response(200, content=b"jpg", headers={"content-type": "image/jpeg"}),
})
library._ma_url = httpx.URL("https://ma.example")
assert library.fetch_image("http://172.16.40.250:8095/imageproxy/abc?size=0") == (b"jpg", "image/jpeg")
assert str(seen[0].url) == "https://ma.example/imageproxy/abc?size=0"
# Without MA_URL the address is used as Music Assistant reports it.
assert MusicLibrary("http://ha", "t", client=httpx.Client()).image_download_url("http://x:8095/a") == "http://x:8095/a"