Files
musicseerr/backend/services/album_utils.py
T
Harvey d24e26fb32 In library rework + Monitored/Unmonitored statuses (#50)
* In library rework + Monitored/Unmonitored statuses

* address comments + format
2026-04-16 00:51:13 +01:00

153 lines
5.6 KiB
Python

from typing import Optional
from api.v1.schemas.album import Track
def parse_year(date_str: Optional[str]) -> Optional[int]:
if not date_str:
return None
year = date_str.split("-", 1)[0]
return int(year) if year.isdigit() else None
def find_primary_release(release_group: dict) -> Optional[dict]:
ranked = get_ranked_releases(release_group)
return ranked[0] if ranked else None
def get_ranked_releases(release_group: dict) -> list[dict]:
"""Return official releases sorted so digital/mainstream formats come first."""
releases = release_group.get("releases") or release_group.get("release-list", [])
official = [r for r in releases if r.get("status") == "Official"]
if not official:
official = list(releases)
def _release_sort_key(r: dict) -> tuple[int, str]:
country = (r.get("country") or "").upper()
packaging = (r.get("packaging") or "").lower()
physical_keywords = {"vinyl", "cassette", "gatefold"}
if country == "XW":
rank = 0
elif any(kw in packaging for kw in physical_keywords):
rank = 2
else:
rank = 1
return (rank, r.get("id", ""))
official.sort(key=_release_sort_key)
return official
def extract_artist_info(release_group: dict) -> tuple[str, str]:
artist_credit = release_group.get("artist-credit", [])
artist_name = "Unknown Artist"
artist_id = ""
if artist_credit and isinstance(artist_credit, list):
first_artist = artist_credit[0]
if isinstance(first_artist, dict):
artist_obj = first_artist.get("artist", {})
artist_name = first_artist.get("name") or artist_obj.get("name", "Unknown Artist")
artist_id = artist_obj.get("id", "")
return artist_name, artist_id
def extract_tracks(release_data: dict) -> tuple[list[Track], int]:
tracks = []
total_length = 0
medium_list = release_data.get("media") or release_data.get("medium-list", [])
for medium in medium_list:
try:
disc_number = int(medium.get("position") or medium.get("number") or 1)
except (TypeError, ValueError):
disc_number = 1
track_list = medium.get("tracks") or medium.get("track-list", [])
for track in track_list:
recording = track.get("recording", {})
length_ms = track.get("length") or recording.get("length")
if length_ms:
try:
total_length += int(length_ms)
except (ValueError, TypeError):
pass
tracks.append(
Track(
position=int(track.get("position") or track.get("number", 0)),
disc_number=disc_number,
title=recording.get("title") or track.get("title", "Unknown"),
length=int(length_ms) if length_ms else None,
recording_id=recording.get("id"),
)
)
return tracks, total_length
def extract_label(release_data: dict) -> Optional[str]:
label_info_list = release_data.get("label-info") or release_data.get("label-info-list", [])
if label_info_list:
label_obj = label_info_list[0].get("label")
if label_obj:
return label_obj.get("name")
return None
def build_album_basic_info(
release_group: dict,
release_group_id: str,
artist_name: str,
artist_id: str,
in_library: bool
) -> dict:
return {
"title": release_group.get("title", "Unknown Album"),
"musicbrainz_id": release_group_id,
"artist_name": artist_name,
"artist_id": artist_id,
"release_date": release_group.get("first-release-date"),
"year": parse_year(release_group.get("first-release-date")),
"type": release_group.get("primary-type"),
"disambiguation": release_group.get("disambiguation"),
"tracks": [],
"total_tracks": 0,
"in_library": in_library,
}
def lidarr_to_basic_info(lidarr_album: dict, release_group_id: str, in_library: bool, is_requested: bool = False) -> dict:
year = None
if release_date := lidarr_album.get("release_date"):
try:
year = int(release_date.split("-")[0])
except (ValueError, IndexError):
pass
return {
"title": lidarr_album.get("title", "Unknown Album"),
"musicbrainz_id": release_group_id,
"artist_name": lidarr_album.get("artist_name", "Unknown Artist"),
"artist_id": lidarr_album.get("artist_mbid", ""),
"release_date": lidarr_album.get("release_date"),
"year": year,
"type": lidarr_album.get("album_type"),
"disambiguation": lidarr_album.get("disambiguation"),
"in_library": in_library,
"requested": is_requested and not in_library,
"monitored": lidarr_album.get("monitored", False),
"cover_url": lidarr_album.get("cover_url"),
}
def mb_to_basic_info(release_group: dict, release_group_id: str, in_library: bool, is_requested: bool) -> dict:
artist_name, artist_id = extract_artist_info(release_group)
return {
"title": release_group.get("title", "Unknown Album"),
"musicbrainz_id": release_group_id,
"artist_name": artist_name,
"artist_id": artist_id,
"release_date": release_group.get("first-release-date"),
"year": parse_year(release_group.get("first-release-date")),
"type": release_group.get("primary-type"),
"disambiguation": release_group.get("disambiguation"),
"in_library": in_library,
"requested": is_requested and not in_library,
"monitored": False,
"cover_url": None,
}