dotfiles/.config/spicetify/CustomApps/lyrics-plus/ProviderMusixmatch.js
2024-06-26 12:05:08 +02:00

191 lines
4.9 KiB
JavaScript

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 };
})();