Skip to content

Commit f825f07

Browse files
authored
Merge pull request #395 from lklynet/feature/deezer-track-previews
Add Deezer-backed preview matching for release-group tracks, including album resolution and track-level preview URLs. Extend artist detail track rows with inline playlist menus so tracks can be added to existing playlists or saved into a new one without opening a modal. Update backend and frontend plumbing to pass playlist state, loading, and error context through the artist details views. Refactor Deezer album and track caching to reduce repeated lookups while improving preview matching accuracy.
2 parents a1ee97d + e0fac8c commit f825f07

8 files changed

Lines changed: 855 additions & 275 deletions

File tree

backend/routes/artists/handlers/releaseGroup.js

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,28 @@ import { dbOps } from "../../../config/db-helpers.js";
33
import { cacheMiddleware } from "../../../middleware/cache.js";
44
import { warmImageProxy } from "../../../services/imageProxyService.js";
55
import { selectBestAlbumImage } from "../../../services/imageService.js";
6+
import { enrichTracksWithDeezerPreviews } from "../../../services/apiClients.js";
67
import {
78
getAlbumByMbid,
9+
getArtistByMbid,
810
getAlbumTracksByAlbumMbid,
911
} from "../../../services/metadataProvider.js";
1012

1113
const LEGACY_COVER_HOST_PATTERN =
1214
/https?:\/\/(?:caa\.lkly\.net|coverartarchive\.org|archive\.org|[\w-]+\.ca\.archive\.org)\//i;
1315

16+
function extractDeezerArtistIdFromLinks(links = []) {
17+
if (!Array.isArray(links)) return null;
18+
for (const link of links) {
19+
const type = String(link?.type || "").toLowerCase();
20+
const target = String(link?.target || link?.url?.resource || "").trim();
21+
if (type !== "deezer" && !/deezer\.com\/artist\//i.test(target)) continue;
22+
const match = target.match(/deezer\.com\/artist\/(\d+)/i);
23+
if (match?.[1]) return match[1];
24+
}
25+
return null;
26+
}
27+
1428
export default function registerReleaseGroup(router) {
1529
router.get("/release-group/:mbid/cover", cacheMiddleware(86400), async (req, res) => {
1630
try {
@@ -107,16 +121,70 @@ export default function registerReleaseGroup(router) {
107121
return res.status(400).json({ error: "Invalid MBID format" });
108122
}
109123

124+
const artistMbid =
125+
typeof req.query.artistMbid === "string" &&
126+
UUID_REGEX.test(req.query.artistMbid)
127+
? req.query.artistMbid
128+
: "";
129+
const artistName =
130+
typeof req.query.artistName === "string"
131+
? req.query.artistName.trim()
132+
: "";
133+
const albumTitle =
134+
typeof req.query.albumTitle === "string"
135+
? req.query.albumTitle.trim()
136+
: "";
137+
const releaseType =
138+
typeof req.query.releaseType === "string"
139+
? req.query.releaseType.trim()
140+
: "";
141+
const releaseDate =
142+
typeof req.query.releaseDate === "string"
143+
? req.query.releaseDate.trim()
144+
: "";
145+
const deezerAlbumId =
146+
typeof req.query.deezerAlbumId === "string"
147+
? req.query.deezerAlbumId.trim()
148+
: "";
149+
110150
const tracks = await getAlbumTracksByAlbumMbid(mbid);
151+
let deezerArtistId = "";
152+
if (artistMbid) {
153+
const override = dbOps.getArtistOverride(artistMbid);
154+
deezerArtistId = override?.deezerArtistId || "";
155+
if (!deezerArtistId) {
156+
const resolvedArtistMbid = override?.musicbrainzId || artistMbid;
157+
const metadataArtist = await getArtistByMbid(resolvedArtistMbid).catch(
158+
() => null,
159+
);
160+
deezerArtistId =
161+
extractDeezerArtistIdFromLinks(metadataArtist?.links) || "";
162+
}
163+
}
164+
const enrichedTracks = await enrichTracksWithDeezerPreviews(tracks, {
165+
artistName,
166+
deezerArtistId,
167+
deezerAlbumId,
168+
albumTitle,
169+
releaseType,
170+
releaseDate,
171+
cacheKey: `release-group:${mbid}:${
172+
deezerAlbumId || deezerArtistId || artistName
173+
}`,
174+
});
175+
111176
res.json(
112-
tracks.map((track) => ({
177+
enrichedTracks.map((track) => ({
113178
id: track.recordingId || track.id,
114179
mbid: track.recordingId || track.id,
115180
title: track.title,
116181
trackName: track.title,
117182
trackNumber: track.trackPosition || track.trackNumber || 0,
118183
position: track.trackPosition || track.trackNumber || 0,
119184
length: track.durationMs || null,
185+
preview_url: track.preview_url || null,
186+
previewProvider: track.previewProvider || null,
187+
previewTrackId: track.previewTrackId || null,
120188
})),
121189
);
122190
} catch (error) {

0 commit comments

Comments
 (0)