From d27ab7e9103004eded1f0a27aefdde16b2c7de69 Mon Sep 17 00:00:00 2001 From: Hidea Date: Thu, 30 Oct 2025 11:41:31 +0100 Subject: [PATCH] Refactor album handling and update configurations Updated Lidarr configuration, added monitoring for exact releases, and improved album processing logic. --- app.py | 318 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 278 insertions(+), 40 deletions(-) diff --git a/app.py b/app.py index fc80425..b199ca0 100644 --- a/app.py +++ b/app.py @@ -8,8 +8,8 @@ app = Flask(__name__) app.secret_key = "your_secret_key_here" # Your Lidarr config -LIDARR_URL = "XXXX" -API_KEY = "XXXX" +LIDARR_URL = "https://XXXX" +API_KEY = "XXXXX" HEADERS = {"X-Api-Key": API_KEY} def check_lidarr_config(): @@ -37,12 +37,30 @@ def get_release_from_barcode(barcode): return data['releases'][0] def get_album_from_barcode(barcode): - release = get_release_from_barcode(barcode) + """ + Fetch the exact release from MusicBrainz using the barcode. + Returns: artist_name, artist_mbid, album_title, release_group_mbid, release_mbid + """ + url = f"https://musicbrainz.org/ws/2/release/?query=barcode:{barcode}&fmt=json" + r = requests.get(url) + r.raise_for_status() + data = r.json() + + if not data.get('releases'): + raise Exception(f"No release found for barcode {barcode}") + + # Take the first exact match (should match the barcode) + release = data['releases'][0] release_group_mbid = release['release-group']['id'] + release_mbid = release['id'] # <-- exact release ID album_title = release['title'] - artist_name = release['artist-credit'][0]['name'] - artist_mbid = release['artist-credit'][0]['artist']['id'] - return artist_name, artist_mbid, album_title, release_group_mbid + + artist_info = release['artist-credit'][0] + artist_name = artist_info['name'] + artist_mbid = artist_info['artist']['id'] + + return artist_name, artist_mbid, album_title, release_group_mbid, release_mbid + def find_or_create_artist(artist_name, artist_mbid): existing = requests.get(f"{LIDARR_URL}/api/v1/artist", headers=HEADERS).json() @@ -66,64 +84,284 @@ def find_or_create_artist(artist_name, artist_mbid): flash(f"✅ Artist '{artist_name}' created (no albums monitored).", "info") return data["id"] -def update_or_add_album(artist_id, release_group_mbid, album_title): - albums_url = f"{LIDARR_URL}/api/v1/album?artistId={artist_id}" - albums_resp = requests.get(albums_url, headers=HEADERS) - albums_resp.raise_for_status() - albums = albums_resp.json() +def update_or_add_album(artist_id, release_group_mbid, release_mbid, album_title): + """ + Add or update album in Lidarr, monitoring the exact release. + """ + # First check if this release already exists + albums = requests.get(f"{LIDARR_URL}/api/v1/album?artistId={artist_id}", headers=HEADERS).json() for album in albums: - if album['foreignAlbumId'] == release_group_mbid: - album_url = f"{LIDARR_URL}/api/v1/album/{album['id']}" - album_resp = requests.get(album_url, headers=HEADERS) - album_resp.raise_for_status() - album_data = album_resp.json() + if album.get('foreignAlbumId') == release_group_mbid: + # Update monitored to True + album_data = requests.get(f"{LIDARR_URL}/api/v1/album/{album['id']}", headers=HEADERS).json() album_data['monitored'] = True - update_url = f"{LIDARR_URL}/api/v1/album/{album['id']}" - update_resp = requests.put(update_url, headers=HEADERS, json=album_data) - update_resp.raise_for_status() - flash(f"✅ Album '{album_title}' is now monitored.", "success") - return album_data - artist_url = f"{LIDARR_URL}/api/v1/artist/{artist_id}" - artist_resp = requests.get(artist_url, headers=HEADERS) - artist_resp.raise_for_status() - artist_data = artist_resp.json() + # Set exact release + album_data['foreignReleaseId'] = release_mbid + r = requests.put(f"{LIDARR_URL}/api/v1/album/{album['id']}", headers=HEADERS, json=album_data) + r.raise_for_status() + flash(f"✅ Album '{album_title}' is now monitored (exact release).", "success") + return r.json() + + # If album not found, create it + artist_data = requests.get(f"{LIDARR_URL}/api/v1/artist/{artist_id}", headers=HEADERS).json() payload = { "artistId": artist_id, "artist": artist_data, "foreignAlbumId": release_group_mbid, + "foreignReleaseId": release_mbid, # <-- exact release "title": album_title, - "monitored": True, # Only this album is monitored + "monitored": True, "addOptions": {"searchForNewAlbum": True} } r = requests.post(f"{LIDARR_URL}/api/v1/album", headers=HEADERS, json=payload) r.raise_for_status() - flash(f"✅ Album '{album_title}' added and monitored.", "success") + flash(f"✅ Album '{album_title}' added and monitored (exact release).", "success") + return r.json() + + +def monitor_exact_release(artist_id, release_group_mbid, release_mbid, album_title): + """ + Add album if missing, then monitor the exact release after Lidarr fetches releases. + """ + # 1️⃣ Check if album exists + albums = requests.get(f"{LIDARR_URL}/api/v1/album?artistId={artist_id}", headers=HEADERS).json() + album_id = None + for album in albums: + if album.get('foreignAlbumId') == release_group_mbid: + album_id = album['id'] + break + + # 2️⃣ Add album if missing + if not album_id: + artist_data = requests.get(f"{LIDARR_URL}/api/v1/artist/{artist_id}", headers=HEADERS).json() + payload = { + "artistId": artist_id, + "artist": artist_data, + "foreignAlbumId": release_group_mbid, + "title": album_title, + "monitored": True, + "addOptions": {"searchForNewAlbum": True} + } + album_resp = requests.post(f"{LIDARR_URL}/api/v1/album", headers=HEADERS, json=payload) + album_resp.raise_for_status() + album_id = album_resp.json()["id"] + + # 3️⃣ Wait for Lidarr to fetch releases (poll until the release appears) + timeout = 30 # seconds + interval = 3 + elapsed = 0 + while elapsed < timeout: + album_data = requests.get(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS).json() + releases = album_data.get("releases", []) + target_release = next((r for r in releases if r["id"] == release_mbid), None) + if target_release: + break + time.sleep(interval) + elapsed += interval + else: + raise Exception("❌ Exact release not found in Lidarr after waiting") + + # 4️⃣ Mark the exact release as monitored + for r in releases: + r["monitored"] = r["id"] == release_mbid # only monitor target release + + album_data["monitored"] = True + album_data["releases"] = releases + + update_resp = requests.put(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS, json=album_data) + update_resp.raise_for_status() + return update_resp.json() + + +def add_album_with_exact_release(barcode): + # --- 1️⃣ Get release info from MusicBrainz --- + r = requests.get(f"https://musicbrainz.org/ws/2/release/?query=barcode:{barcode}&fmt=json") + r.raise_for_status() + data = r.json() + if not data.get("releases"): + raise Exception(f"No release found for barcode {barcode}") + + release = data["releases"][0] + release_mbid = release["id"] # exact release MBID + release_group_mbid = release["release-group"]["id"] + album_title = release["title"] + artist_credit = release["artist-credit"][0] + artist_name = artist_credit["name"] + artist_mbid = artist_credit["artist"]["id"] + + # --- 2️⃣ Find or create artist in Lidarr --- + artists = requests.get(f"{LIDARR_URL}/api/v1/artist", headers=HEADERS).json() + artist_id = None + for a in artists: + if a.get("foreignArtistId") == artist_mbid: + artist_id = a["id"] + break + if not artist_id: + payload = { + "artistName": artist_name, + "foreignArtistId": artist_mbid, + "rootFolderPath": "/music", + "qualityProfileId": 2, + "metadataProfileId": 9, + "monitored": False, + "monitorNewItems": "none", + "addOptions": {"searchForMissingAlbums": False} + } + resp = requests.post(f"{LIDARR_URL}/api/v1/artist", headers=HEADERS, json=payload) + resp.raise_for_status() + artist_id = resp.json()["id"] + + # --- 3️⃣ Check if album exists --- + albums = requests.get(f"{LIDARR_URL}/api/v1/album?artistId={artist_id}", headers=HEADERS).json() + album_id = None + for alb in albums: + if alb.get("foreignAlbumId") == release_group_mbid: + album_id = alb["id"] + break + + # --- 4️⃣ Add or update album --- + if not album_id: + # Album does not exist → create it with exact release + artist_data = requests.get(f"{LIDARR_URL}/api/v1/artist/{artist_id}", headers=HEADERS).json() + payload = { + "artistId": artist_id, + "artist": artist_data, + "foreignAlbumId": release_group_mbid, + "title": album_title, + "monitored": True, + "addOptions": {"searchForNewAlbum": True}, + "releases": [{"id": release_mbid, "monitored": True}] + } + album_resp = requests.post(f"{LIDARR_URL}/api/v1/album", headers=HEADERS, json=payload) + album_resp.raise_for_status() + album_id = album_resp.json()["id"] + else: + # Album exists → monitor exact release + album_data = requests.get(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS).json() + album_data["monitored"] = True + album_data["releases"] = [{"id": release_mbid, "monitored": True}] + update_resp = requests.put(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS, json=album_data) + update_resp.raise_for_status() + + # --- 5️⃣ Ensure release is monitored --- + release_payload = { + "albumId": album_id, + "releaseId": release_mbid, + "monitored": True + } + r = requests.post(f"{LIDARR_URL}/api/v1/release", headers=HEADERS, json=release_payload) + r.raise_for_status() + + return { + "artist": artist_name, + "album": album_title, + "release_mbid": release_mbid, + "album_id": album_id + } + + + + +def update_album_release(album_id, artist_id, album_title, release_mbid): + # Fetch current album info + r = requests.get(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS) + r.raise_for_status() + album = r.json() + + # Prepare payload + payload = { + "id": album['id'], + "artistId": artist_id, + "title": album['title'], + "monitored": True, + "releases": [{"id": release_mbid, "monitored": True}] + } + + # Send update + r = requests.put(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS, json=payload) + r.raise_for_status() return r.json() def process_barcode(barcode): try: - # Step 1: Processing barcode + # Step 1: Fetch exact release from MusicBrainz yield json.dumps({"status": "🔍 Processing barcode...", "progress": 5}) + "\n\n" - artist_name, artist_mbid, album_title, release_group_mbid = get_album_from_barcode(barcode) - yield json.dumps({"status": f"🎵 Album found: {album_title} by {artist_name}", "progress": 15}) + "\n\n" + release = get_release_from_barcode(barcode) + release_mbid = release['id'] # exact release MBID + release_group_mbid = release['release-group']['id'] + album_title = release['title'] + artist_info = release['artist-credit'][0] + artist_name = artist_info['name'] + artist_mbid = artist_info['artist']['id'] + yield json.dumps({"status": f"🎵 Found release '{album_title}' by {artist_name}", "progress": 15}) + "\n\n" - # Step 2: Checking artist + # Step 2: Ensure artist exists yield json.dumps({"status": f"🔎 Checking artist '{artist_name}'...", "progress": 30}) + "\n\n" artist_id = find_or_create_artist(artist_name, artist_mbid) - # Step 3: Waiting for Lidarr - yield json.dumps({"status": "🕒 Waiting 30 seconds for Lidarr to process the artist...", "progress": 50}) + "\n\n" - time.sleep(30) + # Step 3: Ensure album exists + albums = requests.get(f"{LIDARR_URL}/api/v1/album?artistId={artist_id}", headers=HEADERS).json() + album_id = None + for alb in albums: + if alb.get("foreignAlbumId") == release_group_mbid: + album_id = alb['id'] + break - # Step 4: Adding/monitoring album - yield json.dumps({"status": f"💿 Adding/monitoring album '{album_title}'...", "progress": 80}) + "\n\n" - album_info = update_or_add_album(artist_id, release_group_mbid, album_title) + if not album_id: + # Create album + artist_data = requests.get(f"{LIDARR_URL}/api/v1/artist/{artist_id}", headers=HEADERS).json() + payload = { + "artistId": artist_id, + "artist": artist_data, + "foreignAlbumId": release_group_mbid, + "title": album_title, + "monitored": True, + "addOptions": {"searchForNewAlbum": True} + } + album_resp = requests.post(f"{LIDARR_URL}/api/v1/album", headers=HEADERS, json=payload) + album_resp.raise_for_status() + album_id = album_resp.json()["id"] + yield json.dumps({"status": f"💿 Album '{album_title}' created in Lidarr.", "progress": 60}) + "\n\n" + else: + # Update album monitored flag + album_data = requests.get(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS).json() + album_data['monitored'] = True + update_resp = requests.put(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS, json=album_data) + update_resp.raise_for_status() + yield json.dumps({"status": f"💿 Album '{album_title}' already exists. Marked as monitored.", "progress": 60}) + "\n\n" + + # Step 4: Wait for Lidarr to fetch releases and monitor the exact one + timeout = 60 # total seconds to wait + interval = 3 # seconds between polls + elapsed = 0 + matched_release = None + + while elapsed < timeout: + album_data = requests.get(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS).json() + releases = album_data.get("releases", []) + matched_release = next((r for r in releases if r.get("foreignReleaseId") == release_mbid), None) + if matched_release: + break + time.sleep(interval) + elapsed += interval + else: + raise Exception("❌ Exact release not found in Lidarr after waiting") + + # Only monitor the release matching the barcode + for r in releases: + r["monitored"] = r.get("foreignReleaseId") == release_mbid + + album_data["monitored"] = True + album_data["releases"] = releases + + update_resp = requests.put(f"{LIDARR_URL}/api/v1/album/{album_id}", headers=HEADERS, json=album_data) + update_resp.raise_for_status() + yield json.dumps({"status": f"✅ Album '{album_title}' now monitoring exact release!", "progress": 100}) + "\n\n" - # Step 5: Done - yield json.dumps({"status": "✅ Album added and monitored!", "progress": 100}) + "\n\n" except Exception as e: yield json.dumps({"status": f"❌ Error: {str(e)}", "progress": 100}) + "\n\n" + @app.route("/") def index(): return render_template("index.html") @@ -137,4 +375,4 @@ def submit(): return Response(stream_with_context(process_barcode(barcode)), mimetype="text/event-stream") if __name__ == "__main__": - app.run(debug=True, host="0.0.0.0", port=5000) + app.run(debug=True, host="0.0.0.0", port=5083)