const ProviderMusixmatch = (() => { const headers = { authority: "apic-desktop.musixmatch.com", cookie: "x-mxm-token-guid=" }; async function findLyrics(info) { const baseURL = "https://apic-desktop.musixmatch.com/ws/1.1/macro.subtitles.get?format=json&namespace=lyrics_richsynched&subtitle_format=mxm&app_id=web-desktop-app-v1.0&"; const durr = info.duration / 1000; const params = { q_album: info.album, q_artist: info.artist, q_artists: info.artist, q_track: info.title, track_spotify_id: info.uri, q_duration: durr, f_subtitle_length: Math.floor(durr), usertoken: CONFIG.providers.musixmatch.token }; const finalURL = baseURL + Object.keys(params) .map(key => `${key}=${encodeURIComponent(params[key])}`) .join("&"); let body = await Spicetify.CosmosAsync.get(finalURL, null, headers); body = body.message.body.macro_calls; if (body["matcher.track.get"].message.header.status_code !== 200) { return { error: `Requested error: ${body["matcher.track.get"].message.header.mode}`, uri: info.uri }; } if (body["track.lyrics.get"]?.message?.body?.lyrics?.restricted) { return { error: "Unfortunately we're not authorized to show these lyrics.", uri: info.uri }; } return body; } async function getKaraoke(body) { const meta = body?.["matcher.track.get"]?.message?.body; if (!meta) { return null; } if (!meta.track.has_richsync || meta.track.instrumental) { return null; } const baseURL = "https://apic-desktop.musixmatch.com/ws/1.1/track.richsync.get?format=json&subtitle_format=mxm&app_id=web-desktop-app-v1.0&"; const params = { f_subtitle_length: meta.track.track_length, q_duration: meta.track.track_length, commontrack_id: meta.track.commontrack_id, usertoken: CONFIG.providers.musixmatch.token }; const finalURL = baseURL + Object.keys(params) .map(key => `${key}=${encodeURIComponent(params[key])}`) .join("&"); let result = await Spicetify.CosmosAsync.get(finalURL, null, headers); if (result.message.header.status_code !== 200) { return null; } result = result.message.body; const parsedKaraoke = JSON.parse(result.richsync.richsync_body).map(line => { const startTime = line.ts * 1000; const endTime = line.te * 1000; const words = line.l; const text = words.map((word, index, words) => { const wordText = word.c; const wordStartTime = word.o * 1000; const nextWordStartTime = words[index + 1]?.o * 1000; const time = !Number.isNaN(nextWordStartTime) ? nextWordStartTime - wordStartTime : endTime - (wordStartTime + startTime); return { word: wordText, time }; }); return { startTime, text }; }); return parsedKaraoke; } function getSynced(body) { const meta = body?.["matcher.track.get"]?.message?.body; if (!meta) { return null; } const hasSynced = meta?.track?.has_subtitles; const isInstrumental = meta?.track?.instrumental; if (isInstrumental) { return [{ text: "♪ Instrumental ♪", startTime: "0000" }]; } if (hasSynced) { const subtitle = body["track.subtitles.get"]?.message?.body?.subtitle_list?.[0]?.subtitle; if (!subtitle) { return null; } return JSON.parse(subtitle.subtitle_body).map(line => ({ text: line.text || "♪", startTime: line.time.total * 1000 })); } return null; } function getUnsynced(body) { const meta = body?.["matcher.track.get"]?.message?.body; if (!meta) { return null; } const hasUnSynced = meta.track.has_lyrics || meta.track.has_lyrics_crowd; const isInstrumental = meta?.track?.instrumental; if (isInstrumental) { return [{ text: "♪ Instrumental ♪" }]; } if (hasUnSynced) { const lyrics = body["track.lyrics.get"]?.message?.body?.lyrics?.lyrics_body; if (!lyrics) { return null; } return lyrics.split("\n").map(text => ({ text })); } return null; } async function getTranslation(body) { const track_id = body?.["matcher.track.get"]?.message?.body?.track?.track_id; if (!track_id) return null; const baseURL = "https://apic-desktop.musixmatch.com/ws/1.1/crowd.track.translations.get?translation_fields_set=minimal&selected_language=en&comment_format=text&format=json&app_id=web-desktop-app-v1.0&"; const params = { track_id, usertoken: CONFIG.providers.musixmatch.token }; const finalURL = baseURL + Object.keys(params) .map(key => `${key}=${encodeURIComponent(params[key])}`) .join("&"); let result = await Spicetify.CosmosAsync.get(finalURL, null, headers); if (result.message.header.status_code !== 200) return null; result = result.message.body; if (!result.translations_list?.length) return null; return result.translations_list.map(({ translation }) => ({ translation: translation.description, matchedLine: translation.matched_line })); } return { findLyrics, getKaraoke, getSynced, getUnsynced, getTranslation }; })();