diff --git a/js/api.js b/js/api.js
index 02d8557..d980e8c 100644
--- a/js/api.js
+++ b/js/api.js
@@ -1247,6 +1247,19 @@ export class LosslessAPI {
return `https://resources.tidal.com/images/${formattedId}/${size}x${size}.jpg`;
}
+ getVideoCoverUrl(id, size = '1280') {
+ if (!id) {
+ return null;
+ }
+
+ const parts = id.split('-');
+ if (parts.length !== 5) {
+ return null;
+ }
+
+ return `https://resources.tidal.com/videos/${parts[0]}/${parts[1]}/${parts[2]}/${parts[3]}/${parts[4]}/${size}x${size}.mp4`;
+ }
+
getArtistPictureUrl(id, size = '320') {
if (!id) {
return `https://picsum.photos/seed/${Math.random()}/${size}`;
diff --git a/js/music-api.js b/js/music-api.js
index 106974a..adb8ee2 100644
--- a/js/music-api.js
+++ b/js/music-api.js
@@ -131,6 +131,14 @@ export class MusicAPI {
return this.tidalAPI.getCoverUrl(id, size);
}
+ getVideoCoverUrl(videoCoverId, fallbackCoverId, size = '1280') {
+ if (videoCoverId) {
+ const videoUrl = this.tidalAPI.getVideoCoverUrl(videoCoverId, size);
+ if (videoUrl) return videoUrl;
+ }
+ return this.getCoverUrl(fallbackCoverId, size);
+ }
+
getArtistPictureUrl(id, size = '320') {
if (typeof id === 'string' && id.startsWith('q:')) {
return this.qobuzAPI.getArtistPictureUrl(id.slice(2), size);
diff --git a/js/player.js b/js/player.js
index 7fdcab5..e7d7d48 100644
--- a/js/player.js
+++ b/js/player.js
@@ -173,7 +173,34 @@ export class Player {
const albumEl = document.querySelector('.now-playing-bar .album');
const artistEl = document.querySelector('.now-playing-bar .artist');
- if (coverEl) coverEl.src = this.api.getCoverUrl(track.album?.cover);
+ if (coverEl) {
+ const videoCoverUrl = track.album?.videoCover
+ ? this.api.tidalAPI.getVideoCoverUrl(track.album.videoCover)
+ : null;
+ const coverUrl = videoCoverUrl || this.api.getCoverUrl(track.album?.cover);
+
+ if (videoCoverUrl) {
+ if (coverEl.tagName === 'IMG') {
+ const video = document.createElement('video');
+ video.src = videoCoverUrl;
+ video.autoplay = true;
+ video.loop = true;
+ video.muted = true;
+ video.playsInline = true;
+ video.className = coverEl.className;
+ coverEl.replaceWith(video);
+ }
+ } else {
+ if (coverEl.tagName === 'VIDEO') {
+ const img = document.createElement('img');
+ img.src = coverUrl;
+ img.className = coverEl.className;
+ coverEl.replaceWith(img);
+ } else {
+ coverEl.src = coverUrl;
+ }
+ }
+ }
if (titleEl) {
const qualityBadge = createQualityBadgeHTML(track);
titleEl.innerHTML = `${escapeHtml(trackTitle)} ${qualityBadge}`;
@@ -365,7 +392,35 @@ export class Player {
const trackArtistsHTML = getTrackArtistsHTML(track);
const yearDisplay = getTrackYearDisplay(track);
- document.querySelector('.now-playing-bar .cover').src = this.api.getCoverUrl(track.album?.cover);
+ const coverEl = document.querySelector('.now-playing-bar .cover');
+ if (coverEl) {
+ const videoCoverUrl = track.album?.videoCover
+ ? this.api.tidalAPI.getVideoCoverUrl(track.album.videoCover)
+ : null;
+ const coverUrl = videoCoverUrl || this.api.getCoverUrl(track.album?.cover);
+
+ if (videoCoverUrl) {
+ if (coverEl.tagName === 'IMG') {
+ const video = document.createElement('video');
+ video.src = videoCoverUrl;
+ video.autoplay = true;
+ video.loop = true;
+ video.muted = true;
+ video.playsInline = true;
+ video.className = coverEl.className;
+ coverEl.replaceWith(video);
+ }
+ } else {
+ if (coverEl.tagName === 'VIDEO') {
+ const img = document.createElement('img');
+ img.src = coverUrl;
+ img.className = coverEl.className;
+ coverEl.replaceWith(img);
+ } else {
+ coverEl.src = coverUrl;
+ }
+ }
+ }
document.querySelector('.now-playing-bar .title').innerHTML =
`${escapeHtml(trackTitle)} ${createQualityBadgeHTML(track)}`;
const albumEl = document.querySelector('.now-playing-bar .album');
diff --git a/js/ui.js b/js/ui.js
index 30ca4d4..ffe0bb6 100644
--- a/js/ui.js
+++ b/js/ui.js
@@ -334,7 +334,7 @@ export class UIRenderer {
const isUnavailable = track.isUnavailable;
const isBlocked = contentBlockingSettings?.shouldHideTrack(track);
const trackImageHTML = showCover
- ? `
`
+ ? this.getCoverHTML(track.album?.videoCover, track.album?.cover, 'Track Cover', 'track-item-cover')
: '';
let displayIndex;
@@ -405,6 +405,14 @@ export class UIRenderer {
`;
}
+ getCoverHTML(videoCover, cover, alt, className = 'card-image', loading = 'lazy') {
+ const videoUrl = videoCover ? this.api.tidalAPI.getVideoCoverUrl(videoCover) : null;
+ if (videoUrl) {
+ return ``;
+ }
+ return `
`;
+ }
+
createBaseCardHTML({
type,
id,
@@ -608,7 +616,7 @@ export class UIRenderer {
href: `/album/${album.id}`,
title: `${escapeHtml(album.title)} ${explicitBadge} ${qualityBadge}`,
subtitle: `${escapeHtml(artistName)} • ${yearDisplay}${typeLabel}`,
- imageHTML: `
`,
+ imageHTML: this.getCoverHTML(album.videoCover, album.cover, escapeHtml(album.title)),
actionButtonsHTML: `