Show track ownership indicators and file metadata in wishlist modal for all releases
This commit is contained in:
parent
2ca410fadd
commit
3663a75769
3 changed files with 67 additions and 18 deletions
|
|
@ -6097,7 +6097,7 @@ def library_check_tracks():
|
|||
|
||||
if not db_tracks:
|
||||
# No tracks by this artist in DB — none owned
|
||||
owned_map = {t.get('name', ''): False for t in tracks if t.get('name')}
|
||||
owned_map = {t.get('name', ''): {"owned": False} for t in tracks if t.get('name')}
|
||||
return jsonify({"success": True, "owned_tracks": owned_map})
|
||||
|
||||
# Pre-normalize all DB track titles for fast in-memory comparison
|
||||
|
|
@ -6126,8 +6126,8 @@ def library_check_tracks():
|
|||
cleaned = re.sub(r'\s+', ' ', cleaned).strip()
|
||||
return cleaned
|
||||
|
||||
# Pre-compute normalized DB titles once
|
||||
db_title_pairs = [(_normalize(t.title), _clean_title(t.title)) for t in db_tracks]
|
||||
# Pre-compute normalized DB titles once (keep reference to db_track for metadata)
|
||||
db_title_entries = [(_normalize(t.title), _clean_title(t.title), t) for t in db_tracks]
|
||||
|
||||
owned_map = {}
|
||||
for track in tracks:
|
||||
|
|
@ -6137,12 +6137,12 @@ def library_check_tracks():
|
|||
|
||||
search_norm = _normalize(track_name)
|
||||
search_clean = _clean_title(track_name)
|
||||
is_owned = False
|
||||
matched_db_track = None
|
||||
|
||||
for db_norm, db_clean in db_title_pairs:
|
||||
for db_norm, db_clean, db_track in db_title_entries:
|
||||
# Check normalized match first (fast path for exact/near-exact)
|
||||
if search_norm == db_norm or search_clean == db_clean:
|
||||
is_owned = True
|
||||
matched_db_track = db_track
|
||||
break
|
||||
# Fuzzy match: try both normalized and cleaned
|
||||
sim = max(
|
||||
|
|
@ -6150,10 +6150,20 @@ def library_check_tracks():
|
|||
SequenceMatcher(None, search_clean, db_clean).ratio()
|
||||
)
|
||||
if sim >= 0.7:
|
||||
is_owned = True
|
||||
matched_db_track = db_track
|
||||
break
|
||||
|
||||
owned_map[track_name] = is_owned
|
||||
if matched_db_track:
|
||||
import os
|
||||
file_ext = os.path.splitext(matched_db_track.file_path or '')[1].lstrip('.').upper() or None
|
||||
owned_map[track_name] = {
|
||||
"owned": True,
|
||||
"format": file_ext,
|
||||
"bitrate": matched_db_track.bitrate,
|
||||
"album": getattr(matched_db_track, 'album_title', None)
|
||||
}
|
||||
else:
|
||||
owned_map[track_name] = {"owned": False}
|
||||
|
||||
return jsonify({"success": True, "owned_tracks": owned_map})
|
||||
|
||||
|
|
|
|||
|
|
@ -10907,9 +10907,11 @@ function generateWishlistTrackList(tracks, trackOwnership) {
|
|||
const artistsString = formatArtists(track.artists) || 'Unknown Artist';
|
||||
const duration = formatDuration(track.duration_ms);
|
||||
|
||||
const isOwned = trackOwnership ? trackOwnership[track.name] === true : null;
|
||||
const ownershipClass = isOwned === true ? 'owned' : (isOwned === false ? 'missing' : '');
|
||||
const badge = isOwned === true
|
||||
const trackData = trackOwnership ? trackOwnership[track.name] : null;
|
||||
const isOwned = trackData && (trackData.owned === true || trackData === true);
|
||||
const isKnown = trackData !== null && trackData !== undefined;
|
||||
const ownershipClass = isOwned ? 'owned' : (isKnown && !isOwned ? 'missing' : '');
|
||||
const badge = isOwned
|
||||
? '<div class="wishlist-track-badge owned"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg></div>'
|
||||
: '';
|
||||
|
||||
|
|
@ -11095,10 +11097,26 @@ async function lazyLoadTrackOwnership(artistName, tracks, sourceCard) {
|
|||
trackItems.forEach((item, index) => {
|
||||
const track = tracks[index];
|
||||
if (!track) return;
|
||||
const isOwned = ownership[track.name] === true;
|
||||
const trackData = ownership[track.name];
|
||||
const isOwned = trackData && trackData.owned === true;
|
||||
if (isOwned) {
|
||||
ownedCount++;
|
||||
item.classList.add('owned');
|
||||
// Add metadata line below track name
|
||||
const trackInfo = item.querySelector('.wishlist-track-info');
|
||||
if (trackInfo && (trackData.format || trackData.bitrate)) {
|
||||
const metaDiv = document.createElement('div');
|
||||
metaDiv.className = 'wishlist-track-meta';
|
||||
let metaHtml = '';
|
||||
if (trackData.format) {
|
||||
metaHtml += `<span class="wishlist-track-format">${trackData.format}</span>`;
|
||||
}
|
||||
if (trackData.bitrate) {
|
||||
metaHtml += `<span class="wishlist-track-bitrate">${trackData.bitrate} kbps</span>`;
|
||||
}
|
||||
metaDiv.innerHTML = metaHtml;
|
||||
trackInfo.appendChild(metaDiv);
|
||||
}
|
||||
const badge = document.createElement('div');
|
||||
badge.className = 'wishlist-track-badge owned';
|
||||
badge.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>';
|
||||
|
|
@ -26488,10 +26506,8 @@ function createReleaseCard(release) {
|
|||
hideLoadingOverlay();
|
||||
await openAddToWishlistModal(albumData, currentArtist, data.tracks, albumType);
|
||||
|
||||
// Lazy-load per-track ownership for partial albums (non-blocking)
|
||||
if (rel.track_completion && typeof rel.track_completion === 'object' && rel.track_completion.missing_tracks > 0) {
|
||||
lazyLoadTrackOwnership(currentArtist.name, data.tracks, card);
|
||||
}
|
||||
// Always lazy-load track ownership + metadata (non-blocking)
|
||||
lazyLoadTrackOwnership(currentArtist.name, data.tracks, card);
|
||||
|
||||
} catch (error) {
|
||||
hideLoadingOverlay();
|
||||
|
|
|
|||
|
|
@ -14176,12 +14176,12 @@ body {
|
|||
|
||||
/* Track ownership indicators */
|
||||
.wishlist-track-item.owned {
|
||||
opacity: 0.5;
|
||||
opacity: 0.65;
|
||||
border-left: 3px solid rgba(29, 185, 84, 0.6);
|
||||
}
|
||||
|
||||
.wishlist-track-item.owned:hover {
|
||||
opacity: 0.7;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.wishlist-track-item.missing {
|
||||
|
|
@ -14203,6 +14203,29 @@ body {
|
|||
color: rgba(29, 185, 84, 0.9);
|
||||
}
|
||||
|
||||
/* Track metadata (format + bitrate) */
|
||||
.wishlist-track-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.wishlist-track-format {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px;
|
||||
background: rgba(29, 185, 84, 0.15);
|
||||
color: rgba(29, 185, 84, 0.9);
|
||||
}
|
||||
|
||||
.wishlist-track-bitrate {
|
||||
font-size: 11px;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
}
|
||||
|
||||
/* Modal Footer */
|
||||
.add-to-wishlist-modal-footer {
|
||||
background: linear-gradient(135deg,
|
||||
|
|
|
|||
Loading…
Reference in a new issue