lastfm-discord-rp/main.js

236 lines
6.5 KiB
JavaScript
Raw Normal View History

2023-07-13 11:33:59 -04:00
const rpc = require("discord-rpc");
const fetch = require("request-promise");
const prettyMilliseconds = require("pretty-ms");
const fs = require("fs");
const config = JSON.parse(fs.readFileSync("config.json"));
2024-08-06 08:51:54 -04:00
const updateInterval = 5000;
2023-07-13 11:33:59 -04:00
const retryInterval = 30000;
const restartInterval = 2 * 60 * 60 * 1000;
const reconnectDelay = 10000;
2024-08-06 08:49:12 -04:00
const extendedReconnectDelay = 90000;
2023-07-13 11:33:59 -04:00
let rp;
let startTime = Date.now();
let reconnecting = false;
let lastTrack = null;
2024-08-08 07:30:45 -04:00
let currentClient = null;
2023-07-13 11:33:59 -04:00
function formatNumber(number) {
var x = number.split(".");
var x1 = x[0];
var x2 = x.length > 1 ? "." + x[1] : "";
var rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, "$1" + "," + "$2");
}
return x1 + x2;
}
function createClient() {
2024-08-08 07:30:45 -04:00
if (reconnecting || currentClient) return;
2023-07-13 11:33:59 -04:00
rp = new rpc.Client({ transport: "ipc" });
2024-08-08 07:30:45 -04:00
currentClient = rp;
2023-07-13 11:33:59 -04:00
rp.on("ready", () => {
console.log("Connected to Discord!");
updateStatus();
});
rp.on("disconnected", () => {
console.log("Disconnected from Discord!");
rp = null;
2024-08-08 07:30:45 -04:00
currentClient = null;
2024-08-06 08:49:12 -04:00
reconnect("connection closed");
});
rp.transport.on("error", (error) => {
console.error("Error with Discord connection:", error);
rp = null;
2024-08-08 07:30:45 -04:00
currentClient = null;
2024-08-06 08:49:12 -04:00
if (error.message.includes('RPC_CONNECTION_TIMEOUT')) {
reconnect("RPC_CONNECTION_TIMEOUT");
} else if (error.message.includes('connection closed')) {
reconnect("connection closed");
} else {
reconnect();
}
2023-07-13 11:33:59 -04:00
});
rp.login({ clientId: config.clientId }).catch((error) => {
console.error("Error connecting to Discord:", error);
rp = null;
2024-08-08 07:30:45 -04:00
currentClient = null;
2024-08-06 08:49:12 -04:00
if (error.message.includes('RPC_CONNECTION_TIMEOUT')) {
reconnect("RPC_CONNECTION_TIMEOUT");
} else if (error.message.includes('connection closed')) {
reconnect("connection closed");
} else {
reconnect();
}
2023-07-13 11:33:59 -04:00
});
}
2024-08-06 08:49:12 -04:00
function reconnect(errorType) {
if (reconnecting) return;
reconnecting = true;
2024-08-06 08:49:12 -04:00
let delay = reconnectDelay;
if (errorType === "RPC_CONNECTION_TIMEOUT" || errorType === "connection closed") {
delay = extendedReconnectDelay;
console.log(`Encountered ${errorType}, waiting for ${extendedReconnectDelay / 1000} seconds before reconnecting...`);
}
setTimeout(() => {
reconnecting = false;
createClient();
2024-08-06 08:49:12 -04:00
}, delay);
}
2023-07-13 11:33:59 -04:00
async function updateStatus() {
try {
const data = await fetchCurrentScrobble(config.username);
if (!data || !rp) {
console.log("Reconnecting to Discord...");
reconnect();
2023-07-13 11:33:59 -04:00
return;
}
if (lastTrack && lastTrack.trackName === data.trackName && lastTrack.artist === data.artist) {
console.log("Same track, skipping update.");
setTimeout(updateStatus, updateInterval);
return;
}
lastTrack = data;
2023-07-13 11:33:59 -04:00
await rp.setActivity({
2023-09-24 12:42:48 -04:00
largeImageKey: data.cover !== "default_cover" ? data.cover : "default_cover",
2023-07-13 11:33:59 -04:00
largeImageText: `${data.playcount} plays.`,
smallImageKey: data.whenScrobbled ? "playing" : "stopped",
smallImageText: data.scrobbleStatus,
details: data.trackName,
state: `${data.artist} - ${data.album}`,
buttons: [
{
label: `${formatNumber(data.scrobbles)} ${config.label}`,
url: JSON.parse(config.clickable) ? `https://www.last.fm/user/${config.username}/` : "javascript:void(0);"
2023-07-13 11:33:59 -04:00
},
],
});
console.log("Discord status updated. Current track: " + data.trackName + ", Artist: " + data.artist);
2023-07-13 11:33:59 -04:00
setTimeout(updateStatus, updateInterval);
} catch (error) {
console.error("Failed to update status:", error);
rp = null;
2024-08-08 07:30:45 -04:00
currentClient = null;
reconnect();
2023-07-13 11:33:59 -04:00
}
}
async function fetchCurrentScrobble(user) {
2023-07-14 12:54:16 -04:00
let lastTrackName;
let lastArtist;
2023-07-13 11:33:59 -04:00
try {
const optionsGetTrack = {
uri: "http://ws.audioscrobbler.com/2.0/",
json: true,
qs: {
method: "user.getrecenttracks",
user: user,
api_key: config.apiKey,
format: "json",
limit: "1",
},
};
const lastTrack = await fetch(optionsGetTrack);
2023-07-14 12:54:16 -04:00
if (!lastTrack.recenttracks.track[0]) {
console.error("No track data in recenttracks");
return null;
}
lastArtist = lastTrack.recenttracks.track[0].artist["#text"];
lastTrackName = lastTrack.recenttracks.track[0].name;
2023-07-13 11:33:59 -04:00
const options = {
uri: "http://ws.audioscrobbler.com/2.0/",
json: true,
qs: {
method: "track.getInfo",
user: user,
track: lastTrackName,
artist: lastArtist,
api_key: config.apiKey,
format: "json",
},
};
const rData = await fetch(options);
let playcount = "0";
if (rData.track && rData.track.userplaycount) {
playcount = rData.track.userplaycount;
} else {
console.warn("No track data in track.getInfo for track: " + lastTrackName + " by artist: " + lastArtist);
2023-07-14 12:54:16 -04:00
}
2023-07-13 11:33:59 -04:00
let images = lastTrack.recenttracks.track[0].image;
let coverURL = images && images[images.length - 1]["#text"].trim() ? images[images.length - 1]["#text"].trim() : "default_cover";
let albumName = lastTrack.recenttracks.track[0].album["#text"];
2023-09-24 12:42:48 -04:00
if (!albumName || albumName === "Unknown Album") {
albumName = lastTrackName;
}
2023-09-24 12:42:48 -04:00
2023-07-13 11:33:59 -04:00
const data = {
artist: lastArtist,
album: albumName,
2023-07-13 11:33:59 -04:00
trackName: lastTrackName,
playcount: playcount,
2023-07-13 11:33:59 -04:00
scrobbles: lastTrack.recenttracks["@attr"].total,
whenScrobbled: lastTrack.recenttracks.track[0]["@attr"],
scrobbleStatus: !lastTrack.recenttracks.track[0]["@attr"] ? `Last scrobbled ${prettyMilliseconds(Date.now() - lastTrack.recenttracks.track[0].date.uts * 1000)} ago.` : "Now scrobbling.",
cover: coverURL,
};
return data;
} catch (error) {
2023-07-14 12:54:16 -04:00
console.error("Failed to fetch current scrobble for track: " + lastTrackName + " by artist: " + lastArtist, error);
2023-07-13 11:33:59 -04:00
return null;
}
}
function cleanupAndRestart() {
if (rp) {
rp.clearActivity().then(() => {
rp.destroy();
rp = null;
2024-08-08 07:30:45 -04:00
currentClient = null;
console.log("RPC connection closed.");
2024-08-06 08:49:12 -04:00
setTimeout(main, extendedReconnectDelay);
}).catch(error => {
console.error("Error clearing activity:", error);
rp = null;
2024-08-08 07:30:45 -04:00
currentClient = null;
2024-08-06 08:49:12 -04:00
setTimeout(main, extendedReconnectDelay);
});
} else {
2024-08-06 08:49:12 -04:00
setTimeout(main, extendedReconnectDelay);
}
}
function main() {
createClient();
setTimeout(() => {
console.log("Restarting internal logic...");
cleanupAndRestart();
}, restartInterval);
}
main();