From 977c7d048349dce3310a167c9b32ec34bb289f4b Mon Sep 17 00:00:00 2001 From: p1nkhamster Date: Fri, 24 Apr 2026 11:51:27 +0200 Subject: [PATCH] fix(hifi): use Tidal v2 API for artist biography The v1 endpoint (api.tidal.com/v1/artists/{id}/bio) returns 404. Switch getArtistBiography to the v2 OpenAPI endpoint with include=biography, extracting the biography from the included resources in the JSON:API response. Also restore text and source on JsonApiIncludeAttributes, which were dropped when the v2 artist fix was reverted in b528720. --- js/HiFi.ts | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/js/HiFi.ts b/js/HiFi.ts index b7cc551..e7dd306 100644 --- a/js/HiFi.ts +++ b/js/HiFi.ts @@ -1026,6 +1026,8 @@ interface JsonApiIncludeAttributes { albumType?: string; createdAt?: string; type?: string; + text?: string; + source?: string; } /** An included resource node from a TIDAL OpenAPI JSON:API response. */ @@ -1933,14 +1935,34 @@ class HiFiClient { return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, albums: page_data, tracks }); } async getArtistBiography(artistId: number, signal?: AbortSignal): Promise> { - const url = `https://api.tidal.com/v1/artists/${artistId}/bio`; - const params = { - locale: this.#locale, - countryCode: this.#countryCode, - }; - const data = await this.#fetchJson(url, params, signal); + const url = `https://openapi.tidal.com/v2/artists/${artistId}`; + const payload = await this.#fetchJson<{ data?: JsonApiInclude; included?: JsonApiInclude[] }>( + url, + { countryCode: this.#countryCode, include: 'biography' }, + signal + ); - return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data: data }); + const includedMap = new Map(); + for (const item of payload?.included ?? []) { + includedMap.set(`${item.type}:${item.id}`, item); + } + + const bioRelData = payload?.data?.relationships?.biography?.data; + const bioRef = (Array.isArray(bioRelData) ? bioRelData[0] : bioRelData) as JsonApiRef | undefined; + const bioItem = bioRef + ? (includedMap.get(`${bioRef.type}:${bioRef.id}`) ?? + includedMap.get(`biographies:${bioRef.id}`) ?? + includedMap.get(`biography:${bioRef.id}`)) + : undefined; + + const data: ArtistBiography = { + text: bioItem?.attributes?.text ?? '', + source: bioItem?.attributes?.source ?? 'Tidal', + lastUpdated: '', + summary: '', + }; + + return HiFiClient.#jsonResponse({ version: HiFiClient.API_VERSION, data }); } #buildCoverEntry(cover_slug: string, name?: string | null, track_id?: number | null): CoverEntry {