const express = require("express"), fs = require("fs"), path = require("path"), WebSocket = require('ws'), { Station } = require("@fridgefm/radio-core") const station = new Station({}) const PORT = process.env.PORT || 8080 const cachePath = path.join(__dirname, "cache"), cacheFile = path.join(__dirname, "cache.json"), configFile = path.join(__dirname, "config.json") var lanyardData if (!fs.existsSync(cachePath)) { fs.mkdirSync(cachePath) } if (!fs.existsSync(cacheFile) || !JSON.parse(fs.readFileSync(cacheFile)).songs) { fs.writeFileSync(cacheFile, fs.readFileSync(path.join(__dirname, "defaults/cache.json"))) } if (!fs.existsSync(configFile)) { fs.writeFileSync(configFile, fs.readFileSync(path.join(__dirname, "defaults/config.json")))} var cacheDirs = ["songs", "imgs"] for (var i = 0; i < cacheDirs.length; i++) { if (!fs.existsSync(path.join(cachePath, cacheDirs[i]))) { fs.mkdirSync(path.join(cachePath, cacheDirs[i])) } } var conf = JSON.parse(fs.readFileSync(path.join(__dirname, "config.json"))) const Spotify = require("spotifydl-core").default const spotifydl = new Spotify({ clientId: "5420a0e8fae24101ac6ad57278bde694", clientSecret: conf.Spotify.client_secret }) const songCache = path.join(cachePath, "songs"), imgCache = path.join(cachePath, "imgs") var app = express() const imgWaitMax = 2 app.use("/songs", express.static(songCache)) app.get("/spotify/stream", (req, res) => { station.connectListener(req, res) }) var lastSong = undefined app.get("/cached/*", (req, res) => { var imgURL = req.originalUrl imgURL = imgURL.substring(imgURL.indexOf("/", 2) + 1) var imgData = cachedImages.imgs[imgURL] var imgWait = 0 function waitForImage() { if (imgWait < imgWaitMax) { imgWait += 0.1 setTimeout(() => { imgData = cachedImages.imgs[imgURL] console.log(imgData) if (imgData) { fs.createReadStream(path.join(imgCache, imgData.file)).pipe(res) } else { waitForImage() } }, 100); } else { fs.createReadStream(path.join(__dirname, "/imgs/notFound.png")).pipe(res) } } if (imgData) { fs.createReadStream(path.join(imgCache, imgData.file)).pipe(res) } else { waitForImage() } }) app.listen(PORT, () => { console.log("Violet's cache is now listening on port: " + PORT) }) var cachedImages = JSON.parse(fs.readFileSync(cacheFile)) var constants = JSON.parse(fs.readFileSync(path.join(__dirname, "constants.json"))) var activityImages = constants.activityImages var thumborURL = "https://thumbor-production-0e82.up.railway.app/unsafe/" var imgExtension = "png" var thumborArgs = `filters:format(${imgExtension})/` function get_img_url(activity, size = "large_image") { if ("assets" in activity) { var image = activity.assets[size] if (image) { if (image.includes("https/")) { return decodeURIComponent('https://' + image.substr(image.indexOf('https/') + 6, image.length)) } else if (image.includes("spotify")) { return decodeURIComponent('https://i.scdn.co/image/' + image.substr(image.indexOf('spotify:') + 8, image.length)) } else { return decodeURIComponent('https://cdn.discordapp.com/app-assets/' + activity.application_id + "/" + image + ".png") } } } if (!image && size == "large_image") { if (activity.name in activityImages) { return decodeURIComponent(activityImages[activity.name]) } } return null } function socketeer() { var lanyard = new WebSocket('https://api.violets-purgatory.dev') lanyard.on("error", (error) => { console.log(error) }) lanyard.on("close", () => { console.log("Connection Closed. Attempting Reconnect in 30 seconds.") setTimeout(() => { socketeer() }, 30000); }) function ping(dur) { lanyard.send(JSON.stringify({ op: 3 })) setTimeout(() => { ping(dur) if (Date.now() - lastPong > 120000) { lanyard.close() console.log("Max duration since last pong exceeded- Closing socket.") } }, dur); } lanyard.addEventListener("message", async (res) => { var data = JSON.parse(res.data) // console.log(data.op) if (data.op == 1) { console.log("Connected to API Websocket!") ping(30000) lastPong = Date.now() } else if (data.op == 3) { lastPong = Date.now() } else if (data.op == 0) { lanyardData = data.d var spotify = lanyardData.spotify if (conf.Spotify.client_secret && spotify) { var trackURL = "https://open.spotify.com/track/" + spotify.track_id var songPath = path.join(cachePath, "/songs/", spotify.track_id + ".mp3") if (!fs.existsSync(songPath)) { await spotifydl.downloadTrack(trackURL, songPath) } if (lastSong != spotify.track_id) { lastSong = spotify.track_id station.addFolder(songPath) const removeSongs = (list) => list .filter(track => track.fsStats.fullPath !== songPath); station.reorderPlaylist(removeSongs) station.start() station.next() } } else { console.log(spotify) } for (let index = 0; index < lanyardData.activities.length; index++) { const activity = lanyardData.activities[index]; var imgType = undefined var imgRes = "512x512/" for (var i = 0; i < 2; i++) { if (get_img_url(activity, imgType)) { var url = get_img_url(activity, imgType) var fn = Object.keys(cachedImages.imgs).length + "." + imgExtension var fp = path.join(imgCache, fn) if (!cachedImages.imgs[url]) { const response = await (await fetch(thumborURL + imgRes + thumborArgs + url)).arrayBuffer() fs.writeFileSync(fp, Buffer.from(response)) cachedImages.imgs[url] = { "file": fn, "lastUpdated": Date.now() } fs.writeFileSync(cacheFile, JSON.stringify(cachedImages)) } imgType = "small_image" imgRes = "128x128/" } } } } }) } socketeer()