Add Discogs as cover source fallback after MusicBrainz
- _discogs_cover_url(): searches Discogs database/search API by artist+album, returns primary image URL; uses DISCOGS_TOKEN if set, else anonymous - download_discogs_cover(): downloads and saves as folder.jpg (PNG→JPEG via PIL) - resolve_cover() priority: local → MusicBrainz → Discogs - music_enricher: pass artist/album to resolve_cover() for Discogs lookup Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8212a918dd
commit
031e595ff7
2 changed files with 64 additions and 0 deletions
|
|
@ -221,10 +221,67 @@ def embed_cover(audio_path: Path, cover_path: Path) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _discogs_cover_url(artist: Optional[str], album: Optional[str]) -> Optional[str]:
|
||||||
|
"""Sucht auf Discogs nach artist+album und gibt die primäre Image-URL zurück."""
|
||||||
|
if not HAS_REQUESTS or not artist or not album:
|
||||||
|
return None
|
||||||
|
import os
|
||||||
|
token = os.getenv("DISCOGS_TOKEN", "")
|
||||||
|
headers = {"User-Agent": "MusicMetadataEnricher/1.0"}
|
||||||
|
if token:
|
||||||
|
headers["Authorization"] = f"Discogs token={token}"
|
||||||
|
try:
|
||||||
|
r = requests.get(
|
||||||
|
"https://api.discogs.com/database/search",
|
||||||
|
params={"artist": artist, "release_title": album, "type": "release", "per_page": 3},
|
||||||
|
headers=headers,
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
if r.status_code != 200:
|
||||||
|
return None
|
||||||
|
results = r.json().get("results", [])
|
||||||
|
for result in results:
|
||||||
|
cover = result.get("cover_image") or result.get("thumb")
|
||||||
|
if cover and "spacer" not in cover:
|
||||||
|
return cover
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ Discogs-Suchfehler: {e}", file=sys.stderr)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def download_discogs_cover(artist: Optional[str], album: Optional[str], dest_dir: Path) -> Optional[Path]:
|
||||||
|
url = _discogs_cover_url(artist, album)
|
||||||
|
if not url:
|
||||||
|
return None
|
||||||
|
dest = dest_dir / "folder.jpg"
|
||||||
|
try:
|
||||||
|
r = requests.get(url, timeout=15, headers={"User-Agent": "MusicMetadataEnricher/1.0"})
|
||||||
|
if r.status_code != 200:
|
||||||
|
return None
|
||||||
|
ct = r.headers.get("content-type", "")
|
||||||
|
if ("png" in ct or url.lower().endswith(".png")) and HAS_PIL:
|
||||||
|
import io
|
||||||
|
with Image.open(io.BytesIO(r.content)) as img:
|
||||||
|
buf = io.BytesIO()
|
||||||
|
img.convert("RGB").save(buf, format="JPEG", quality=92)
|
||||||
|
dest.write_bytes(buf.getvalue())
|
||||||
|
else:
|
||||||
|
dest.write_bytes(r.content)
|
||||||
|
if _image_ok(dest):
|
||||||
|
return dest
|
||||||
|
dest.unlink(missing_ok=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ Discogs-Cover-Fehler: {e}", file=sys.stderr)
|
||||||
|
dest.unlink(missing_ok=True)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def resolve_cover(
|
def resolve_cover(
|
||||||
image_files: List[Path],
|
image_files: List[Path],
|
||||||
release_mbid: Optional[str],
|
release_mbid: Optional[str],
|
||||||
album_dir: Path,
|
album_dir: Path,
|
||||||
|
artist: Optional[str] = None,
|
||||||
|
album: Optional[str] = None,
|
||||||
) -> tuple[Optional[Path], Optional[str]]:
|
) -> tuple[Optional[Path], Optional[str]]:
|
||||||
"""Returns (cover_path, source_label)."""
|
"""Returns (cover_path, source_label)."""
|
||||||
local = find_local_cover(image_files)
|
local = find_local_cover(image_files)
|
||||||
|
|
@ -237,4 +294,9 @@ def resolve_cover(
|
||||||
if downloaded:
|
if downloaded:
|
||||||
return downloaded, "musicbrainz"
|
return downloaded, "musicbrainz"
|
||||||
|
|
||||||
|
if artist or album:
|
||||||
|
downloaded = download_discogs_cover(artist, album, album_dir)
|
||||||
|
if downloaded:
|
||||||
|
return downloaded, "discogs"
|
||||||
|
|
||||||
return None, None
|
return None, None
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,8 @@ def process_album(
|
||||||
hints.cover_images,
|
hints.cover_images,
|
||||||
proposal.mbid,
|
proposal.mbid,
|
||||||
album_dir,
|
album_dir,
|
||||||
|
artist=proposal.albumartist,
|
||||||
|
album=proposal.album,
|
||||||
)
|
)
|
||||||
if cover_path and not args.no_cover:
|
if cover_path and not args.no_cover:
|
||||||
proposal.cover_path = cover_path
|
proposal.cover_path = cover_path
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue