From 823af0c65fdb83e18815b6bc08b282115260c0a1 Mon Sep 17 00:00:00 2001 From: fucksophie Date: Sun, 1 Feb 2026 16:46:18 +0200 Subject: [PATCH] add new-releases (cursed ass script) --- .gitignore | 3 + package.json | 3 +- src/new-releases.js | 148 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 src/new-releases.js diff --git a/.gitignore b/.gitignore index 501a0fc..fdb9aee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,8 @@ deezer_cache orpheus_links.txt discord_send.txt +new_releases.json state.json node_modules +fixcache.js +new-releases-reparser.js diff --git a/package.json b/package.json index c32b638..ca0a3ba 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "request-bot": "bun run src/request-bot.js", "pillow": "bun run src/pillow.js", "sendgb": "bun run src/sendgb.js", - "uamal": "bun run src/unambigious-artist-lookup.js" + "uamal": "bun run src/unambigious-artist-lookup.js", + "new-releases": "bun run src/new-releases" }, "engineStrict": true, "engines": { diff --git a/src/new-releases.js b/src/new-releases.js new file mode 100644 index 0000000..8d91725 --- /dev/null +++ b/src/new-releases.js @@ -0,0 +1,148 @@ +#!/usr/bin/env bun + +import { writeFileSync, mkdirSync, readFileSync, existsSync } from "fs"; +import crypto from "crypto"; +import path from "path"; + +const MIN_DATE = new Date("2026-01-01"); +const CONCURRENT_LIMIT = 2; + +const CACHE_DIR = "./deezer_cache"; +mkdirSync(CACHE_DIR, { recursive: true }); + +function cachePath(url) { + const hash = crypto.createHash("sha1").update(url).digest("hex"); + return path.join(CACHE_DIR, `${hash}.json`); +} + +async function cachedFetch(url) { + const file = cachePath(url); + + if (existsSync(file)) { + return JSON.parse(readFileSync(file, "utf8")); + } + + const res = await fetch(url); + if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); + const json = await res.json(); + + writeFileSync(file, JSON.stringify(json, null, 2)); + return json; +} + +async function subsonicRequest(endpoint, params = {}) { + params.u = process.env.NAVIDROME_USER; + params.p = process.env.NAVIDROME_PASS; + params.v = "1.16.1"; + params.c = "NewAlbumChecker"; + params.f = "json"; + + const urlParams = new URLSearchParams(params); + const url = `${process.env.NAVIDROME_URL}/rest/${endpoint}.view?${urlParams}`; + const res = await fetch(url); + if (!res.ok) throw new Error(`Subsonic request failed: ${res.status}`); + const data = await res.json(); + return data["subsonic-response"]; +} + +async function getAllArtists() { + const data = await subsonicRequest("getArtists"); + return data.artists.index.flatMap((z) => z.artist).map((a) => a.name); +} + +async function searchDeezerArtist(artistName) { + try { + const data = await cachedFetch( + `https://api.deezer.com/search/artist?q=${encodeURIComponent(artistName)}`, + ); + if (!data.data || !data.data.length) return null; + return data.data[0].id; + } catch (err) { + return null; + } +} + +async function getDeezerAlbums(artistId) { + try { + const data = await cachedFetch( + `https://api.deezer.com/artist/${artistId}/albums`, + ); + + return data.data + .filter((album) => { + if (!album.release_date) return false; + const date = new Date(album.release_date); + return date >= MIN_DATE; + }) + .map((album) => ({ + title: album.title, + explicit: album.explicit_lyrics, + release_date: album.release_date, + url: album.link, + })); + } catch (err) { + return []; + } +} + +async function runWithConcurrency(items, worker, limit = CONCURRENT_LIMIT) { + const results = []; + let i = 0; + + async function next() { + if (i >= items.length) return; + const index = i++; + try { + const res = await worker(items[index]); + results.push(...res); + } catch (e) { + console.error("Error processing", items[index], e); + } + return next(); + } + + const runners = Array.from({ length: Math.min(limit, items.length) }, next); + await Promise.all(runners); + return results; +} + +async function checkNewReleases() { + const artists = await getAllArtists(); + console.log(`Found ${artists.length} artists, checking Deezer...`); + + const releases = await runWithConcurrency( + artists, + async (artist) => { + console.log("checking " + artist); + const deezerId = await searchDeezerArtist(artist); + if (!deezerId) return []; + + const albums = await getDeezerAlbums(deezerId); + const formatted = albums.map((a) => ({ + artist, + album: a.title, + release_date: a.release_date, + url: a.url, + explicit: a.explicit, + })); + console.log("checked " + artist); + return formatted; + }, + CONCURRENT_LIMIT, + ); + + return releases; +} + +(async () => { + const releases = await checkNewReleases(); + if (releases.length) { + console.log("New albums/tracks since 2026-01-01 on Deezer:"); + releases.forEach((r) => { + console.log(`${r.release_date} - ${r.artist} - ${r.album}`); + }); + writeFileSync("new_releases.json", JSON.stringify(releases, null, 2)); + } else { + console.log("No new releases found since 2026-01-01."); + } +})();