Fix "Invalid Date" display in album details
- Backfill album release date from tracks if missing in API response. - Gracefully handle invalid or missing dates in UI rendering. - Fix potential NaN in download folder naming due to invalid dates.
This commit is contained in:
parent
0aee58b823
commit
43f04e7454
4 changed files with 62 additions and 13 deletions
|
|
@ -138,9 +138,9 @@
|
|||
<header class="detail-header">
|
||||
<img id="album-detail-image" src="" alt="" class="detail-header-image">
|
||||
<div class="detail-header-info">
|
||||
<div class="type">Album</div>
|
||||
<h1 class="title" id="album-detail-title"></h1>
|
||||
<div class="meta" id="album-detail-meta"></div>
|
||||
<div class="meta" id="album-detail-producer"></div>
|
||||
<div class="detail-header-actions">
|
||||
<button id="play-album-btn" class="btn-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
||||
|
|
|
|||
14
js/api.js
14
js/api.js
|
|
@ -324,6 +324,20 @@ export class LosslessAPI {
|
|||
}
|
||||
}
|
||||
|
||||
// If album exists but has no releaseDate, try to extract from tracks
|
||||
if (album && !album.releaseDate && tracksSection?.items && tracksSection.items.length > 0) {
|
||||
const firstTrack = tracksSection.items[0];
|
||||
const track = firstTrack.item || firstTrack;
|
||||
|
||||
if (track) {
|
||||
if (track.album && track.album.releaseDate) {
|
||||
album = { ...album, releaseDate: track.album.releaseDate };
|
||||
} else if (track.streamStartDate) {
|
||||
album = { ...album, releaseDate: track.streamStartDate.split('T')[0] };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tracks = (tracksSection?.items || []).map(i => this.prepareTrack(i.item || i));
|
||||
const result = { album, tracks };
|
||||
|
||||
|
|
|
|||
|
|
@ -207,10 +207,13 @@ export async function downloadAlbumAsZip(album, tracks, api, quality, lyricsMana
|
|||
const zip = new JSZip();
|
||||
|
||||
const template = localStorage.getItem('zip-folder-template') || '{albumTitle} - {albumArtist} - monochrome.tf';
|
||||
const releaseDate = album.releaseDate ? new Date(album.releaseDate) : null;
|
||||
const year = (releaseDate && !isNaN(releaseDate.getTime())) ? releaseDate.getFullYear() : '';
|
||||
|
||||
const folderName = formatTemplate(template, {
|
||||
albumTitle: album.title,
|
||||
albumArtist: album.artist?.name,
|
||||
year: new Date(album.releaseDate).getFullYear()
|
||||
year: year
|
||||
});
|
||||
|
||||
const notification = createBulkDownloadNotification('album', album.title, tracks.length);
|
||||
|
|
@ -380,10 +383,13 @@ export async function downloadDiscography(artist, api, quality, lyricsManager =
|
|||
|
||||
try {
|
||||
const { album: fullAlbum, tracks } = await api.getAlbum(album.id);
|
||||
const releaseDate = fullAlbum.releaseDate ? new Date(fullAlbum.releaseDate) : null;
|
||||
const year = (releaseDate && !isNaN(releaseDate.getTime())) ? releaseDate.getFullYear() : '';
|
||||
|
||||
const albumFolder = formatTemplate(template, {
|
||||
albumTitle: fullAlbum.title,
|
||||
albumArtist: fullAlbum.artist?.name,
|
||||
year: new Date(fullAlbum.releaseDate).getFullYear()
|
||||
year: year
|
||||
});
|
||||
|
||||
for (const track of tracks) {
|
||||
|
|
|
|||
49
js/ui.js
49
js/ui.js
|
|
@ -40,6 +40,15 @@ export class UIRenderer {
|
|||
const trackArtists = getTrackArtists(track);
|
||||
const trackTitle = getTrackTitle(track);
|
||||
|
||||
let yearDisplay = '';
|
||||
const releaseDate = track.album?.releaseDate || track.streamStartDate;
|
||||
if (releaseDate) {
|
||||
const date = new Date(releaseDate);
|
||||
if (!isNaN(date.getTime())) {
|
||||
yearDisplay = ` • ${date.getFullYear()}`;
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="track-item" data-track-id="${track.id}">
|
||||
${trackNumberHTML}
|
||||
|
|
@ -49,7 +58,7 @@ export class UIRenderer {
|
|||
${trackTitle}
|
||||
${explicitBadge}
|
||||
</div>
|
||||
<div class="artist">${trackArtists}</div>
|
||||
<div class="artist">${trackArtists}${yearDisplay}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="track-item-duration">${formatTime(track.duration)}</div>
|
||||
|
|
@ -66,13 +75,21 @@ export class UIRenderer {
|
|||
|
||||
createAlbumCardHTML(album) {
|
||||
const explicitBadge = hasExplicitContent(album) ? this.createExplicitBadge() : '';
|
||||
let yearDisplay = '';
|
||||
if (album.releaseDate) {
|
||||
const date = new Date(album.releaseDate);
|
||||
if (!isNaN(date.getTime())) {
|
||||
yearDisplay = `${date.getFullYear()}`;
|
||||
}
|
||||
}
|
||||
return `
|
||||
<a href="#album/${album.id}" class="card">
|
||||
<div class="card-image-wrapper">
|
||||
<img src="${this.api.getCoverUrl(album.cover, '320')}" alt="${album.title}" class="card-image" loading="lazy">
|
||||
</div>
|
||||
<h3 class="card-title">${album.title} ${explicitBadge}</h3>
|
||||
<p class="card-subtitle">Album • ${album.artist?.name ?? ''}</p>
|
||||
<p class="card-subtitle">${album.artist?.name ?? ''}</p>
|
||||
<p class="card-subtitle">${yearDisplay}</p>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
|
@ -84,7 +101,6 @@ export class UIRenderer {
|
|||
<img src="${this.api.getArtistPictureUrl(artist.picture, '320')}" alt="${artist.name}" class="card-image" loading="lazy">
|
||||
</div>
|
||||
<h3 class="card-title">${artist.name}</h3>
|
||||
<p class="card-subtitle">Artist</p>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
|
@ -109,7 +125,7 @@ export class UIRenderer {
|
|||
<div class="skeleton-card ${isArtist ? 'artist' : ''}">
|
||||
<div class="skeleton skeleton-card-image"></div>
|
||||
<div class="skeleton skeleton-card-title"></div>
|
||||
<div class="skeleton skeleton-card-subtitle"></div>
|
||||
${!isArtist ? '<div class="skeleton skeleton-card-subtitle"></div>' : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -257,12 +273,14 @@ export class UIRenderer {
|
|||
const imageEl = document.getElementById('album-detail-image');
|
||||
const titleEl = document.getElementById('album-detail-title');
|
||||
const metaEl = document.getElementById('album-detail-meta');
|
||||
const prodEl = document.getElementById('album-detail-producer');
|
||||
const tracklistContainer = document.getElementById('album-detail-tracklist');
|
||||
|
||||
imageEl.src = '';
|
||||
imageEl.style.backgroundColor = 'var(--muted)';
|
||||
titleEl.innerHTML = '<div class="skeleton" style="height: 48px; width: 300px; max-width: 90%;"></div>';
|
||||
metaEl.innerHTML = '<div class="skeleton" style="height: 16px; width: 200px; max-width: 80%;"></div>';
|
||||
prodEl.innerHTML = '<div class="skeleton" style="height: 16px; width: 200px; max-width: 80%;"></div>';
|
||||
tracklistContainer.innerHTML = `
|
||||
<div class="track-list-header">
|
||||
<span style="width: 40px; text-align: center;">#</span>
|
||||
|
|
@ -282,15 +300,26 @@ export class UIRenderer {
|
|||
titleEl.innerHTML = `${album.title} ${explicitBadge}`;
|
||||
|
||||
const totalDuration = calculateTotalDuration(tracks);
|
||||
const releaseDate = new Date(album.releaseDate);
|
||||
const year = releaseDate.getFullYear();
|
||||
let dateDisplay = '';
|
||||
if (album.releaseDate) {
|
||||
const releaseDate = new Date(album.releaseDate);
|
||||
if (!isNaN(releaseDate.getTime())) {
|
||||
const year = releaseDate.getFullYear();
|
||||
dateDisplay = window.innerWidth > 768
|
||||
? releaseDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
|
||||
: year;
|
||||
}
|
||||
}
|
||||
|
||||
const dateDisplay = window.innerWidth > 768
|
||||
? releaseDate.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })
|
||||
: year;
|
||||
const firstCopyright = tracks.find(track => track.copyright)?.copyright;
|
||||
|
||||
metaEl.innerHTML =
|
||||
`By <a href="#artist/${album.artist.id}">${album.artist.name}</a> • ${dateDisplay} • ${tracks.length} tracks • ${formatDuration(totalDuration)}`;
|
||||
(dateDisplay ? `${dateDisplay} • ` : '') +
|
||||
`${tracks.length} tracks • ${formatDuration(totalDuration)}`;
|
||||
|
||||
prodEl.innerHTML =
|
||||
`By <a href="#artist/${album.artist.id}">${album.artist.name}</a>` +
|
||||
(firstCopyright ? ` • ${firstCopyright}` : '');
|
||||
|
||||
tracklistContainer.innerHTML = `
|
||||
<div class="track-list-header">
|
||||
|
|
|
|||
Loading…
Reference in a new issue