From 7ea5eb2c0651526703b0f35b5c3f0407a38f4f31 Mon Sep 17 00:00:00 2001
From: Broque Thomas <26755000+Nezreka@users.noreply.github.com>
Date: Thu, 2 Apr 2026 19:02:23 -0700
Subject: [PATCH] Fix Discogs completion: lazy track counts, edition matching,
type reclassify
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Fetch real track count from source during completion check when
total_tracks is 0 (Discogs masters) — one API call per album, runs
during existing per-album ownership check phase
- Reclassify album cards to single/EP when track count reveals 1-3/4-6
tracks — updates type label and data attribute in place
- Add collectors edition to album title variation patterns for matching
"Damn" against "DAMN. COLLECTORS EDITION." in library
- Hide 0/0 fraction when expected_tracks is 0, show proper count when
fetched
---
database/music_database.py | 2 ++
web_server.py | 12 ++++++++++++
webui/static/script.js | 31 +++++++++++++++++++++++++------
3 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/database/music_database.py b/database/music_database.py
index 5601e5a8..c434cd8a 100644
--- a/database/music_database.py
+++ b/database/music_database.py
@@ -5424,6 +5424,8 @@ class MusicDatabase:
r'\s+special\s*edition?$': ['special', 'special edition'],
r'\s*-\s*deluxe': ['deluxe'],
r'\s*-\s*platinum\s*edition?': ['platinum', 'platinum edition'],
+ r'\s+collector\'?s?\s*edition?$': ['collectors', 'collectors edition'],
+ r'\s*\(collector\'?s?\s*edition?\)': ['collectors', 'collectors edition'],
# Generic catch-alls for any edition in parens/brackets (e.g. Silver Edition, MMXI Special Edition)
r'\s*\([^)]*\bedition\b[^)]*\)': ['edition'],
r'\s*\[[^\]]*\bedition\b[^\]]*\]': ['edition'],
diff --git a/web_server.py b/web_server.py
index d6dba799..329ffff5 100644
--- a/web_server.py
+++ b/web_server.py
@@ -10250,6 +10250,18 @@ def _check_album_completion(db, album_data: dict, artist_name: str, test_mode: b
album_name = album_data.get('name', '')
total_tracks = album_data.get('total_tracks', 0)
album_id = album_data.get('id', '')
+
+ # If total_tracks is 0 (Discogs masters don't include track counts),
+ # try to fetch the real count from the source
+ if total_tracks == 0 and album_id:
+ try:
+ fallback = _get_metadata_fallback_client()
+ album_detail = fallback.get_album_tracks(str(album_id))
+ if album_detail and album_detail.get('items'):
+ total_tracks = len(album_detail['items'])
+ logger.debug(f"Fetched track count for '{album_name}': {total_tracks}")
+ except Exception:
+ pass
print(f"🔍 Checking album: '{album_name}' ({total_tracks} tracks)")
diff --git a/webui/static/script.js b/webui/static/script.js
index b52a19bb..844bfb87 100644
--- a/webui/static/script.js
+++ b/webui/static/script.js
@@ -34371,6 +34371,23 @@ function updateAlbumCompletionOverlay(completionData, containerType) {
return;
}
+ // Reclassify album type if we now know the track count (Discogs lazy fetch)
+ const currentType = albumCard.dataset.albumType;
+ const expectedTracks = completionData.expected_tracks || 0;
+ if (currentType === 'album' && expectedTracks > 0 && expectedTracks <= 3) {
+ albumCard.dataset.albumType = 'single';
+ const typeEl = albumCard.querySelector('.album-card-type');
+ if (typeEl) typeEl.textContent = 'Single';
+ } else if (currentType === 'album' && expectedTracks > 3 && expectedTracks <= 6) {
+ albumCard.dataset.albumType = 'ep';
+ const typeEl = albumCard.querySelector('.album-card-type');
+ if (typeEl) typeEl.textContent = 'EP';
+ }
+ // Update stored total tracks
+ if (expectedTracks > 0) {
+ albumCard.dataset.totalTracks = expectedTracks;
+ }
+
const overlay = albumCard.querySelector('.completion-overlay');
if (!overlay) {
console.warn(`Completion overlay not found for album: ${completionData.name}`);
@@ -34385,12 +34402,13 @@ function updateAlbumCompletionOverlay(completionData, containerType) {
// Update overlay text and content
const statusText = getCompletionStatusText(completionData);
- const progressText = `${completionData.owned_tracks}/${completionData.expected_tracks}`;
+ const progressText = completionData.expected_tracks > 0
+ ? `${completionData.owned_tracks}/${completionData.expected_tracks}`
+ : '';
- overlay.innerHTML = `
- ${statusText}
- ${progressText}
- `;
+ overlay.innerHTML = progressText
+ ? `${statusText}${progressText}`
+ : `${statusText}`;
// Add tooltip with more details
overlay.title = `${completionData.name}\n${statusText} (${completionData.completion_percentage}%)\nTracks: ${completionData.owned_tracks}/${completionData.expected_tracks}\nConfidence: ${completionData.confidence}`;
@@ -42227,7 +42245,8 @@ function createReleaseCard(release) {
}
}
} else {
- completionText.textContent = "Missing";
+ const totalTr = release.total_tracks || release.track_completion?.total_tracks || 0;
+ completionText.textContent = totalTr > 0 ? `Missing (${totalTr} tracks)` : "Not in library";
completionText.className = "completion-text missing";
completionFill.className += " missing";
completionFill.style.width = "0%";