From 42960d4e2894e0761da8d9fd4e89928d155a04da Mon Sep 17 00:00:00 2001 From: yhqe Date: Sat, 2 May 2026 22:10:53 -0500 Subject: [PATCH] added fallback cover art for songs with no cover art --- .gitignore | 5 +- index.js | 111 ++++++++++++----------- package.json | 6 +- pnpm-lock.yaml | 239 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 305 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index c4a76a5..bbf98d6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ config.js npm-debug.log* yarn-debug.log* yarn-error.log* -package-lock.json \ No newline at end of file +package-lock.json + +# Art cache +art_cache.json diff --git a/index.js b/index.js index 602aff0..fe98f52 100644 --- a/index.js +++ b/index.js @@ -9,8 +9,8 @@ const client = new Client({ clientId: config.discordClientId, }); -const albumArtCache = new Map(); // Cache for uploaded album art URLs -const CACHE_FILE = 'art_cache.json'; // The file to store our cache +const albumArtCache = new Map(); +const CACHE_FILE = 'art_cache.json'; let lastTrackId = null; async function loadCache() { @@ -18,26 +18,24 @@ async function loadCache() { const data = await fs.readFile(CACHE_FILE, 'utf-8'); if (!data) { - console.log(`[Cache] ${CACHE_FILE} is empty. A new cache will be created.`); + console.log(`[cache] ${CACHE_FILE} is empty! a new cache will be created...`); return; } const parsed = JSON.parse(data); - // --- THE FIX --- - // Instead of reassigning albumArtCache, clear the existing map - // and add the loaded entries to it. This preserves the global reference. + // the fix... albumArtCache.clear(); for (const [key, value] of Object.entries(parsed)) { albumArtCache.set(key, value); } - console.log(`[Cache] Loaded ${albumArtCache.size} items from ${CACHE_FILE}`); + console.log(`[cache] loaded ${albumArtCache.size} items from ${CACHE_FILE}...`); } catch (error) { if (error.code === 'ENOENT') { - console.log(`[Cache] ${CACHE_FILE} not found. A new cache will be created.`); + console.log(`[cache] ${CACHE_FILE} not found. a new cache will be created!`); } else { - console.warn(`[Cache] Warning: Failed to read or parse ${CACHE_FILE}. Starting with a fresh cache. Error: ${error.message}`); + console.warn(`[cache] warning!! failed to read or parse ${CACHE_FILE}. starting with a fresh cache! error: ${error.message}`); } } } @@ -46,28 +44,26 @@ async function saveCache() { try { const dataToSave = Object.fromEntries(albumArtCache); await fs.writeFile(CACHE_FILE, JSON.stringify(dataToSave, null, 2)); - // This is a useful one-time log, so we keep it. - console.log(`[Cache] Successfully saved cache with ${albumArtCache.size} items.`); + console.log(`[cache] successfully saved cache with ${albumArtCache.size} items!`); } catch (error) { - console.error('[Cache] Failed to save cache:', error); + console.error('[cache] failed to save cache!! ', error); } } -/** - * Uploads an image to Catbox.moe by manually constructing the multipart/form-data request. - * This avoids using the 'form-data' library which was causing conflicts. - * @param {string} imageUrl The local URL of the image to upload. - * @returns {Promise} The public URL of the uploaded image, or null on failure. - */ async function uploadImageToHost(imageUrl) { try { const imageResponse = await axios.get(imageUrl, { responseType: 'arraybuffer', + validateStatus: () => true, // mmmmmm }); + + if (imageResponse.status === 404) { + return null; + } const imageBuffer = Buffer.from(imageResponse.data); if (imageBuffer.length === 0) { - console.error('[Album Art] Downloaded image is empty. Aborting upload.'); + console.error('[covers] downloaded image is empty :( aborting upload!'); return null; } @@ -89,20 +85,20 @@ async function uploadImageToHost(imageUrl) { }); if (uploadResponse.status === 200 && uploadResponse.data.startsWith('http')) { - console.log(`[Album Art] Uploaded to: ${uploadResponse.data}`); + console.log(`[covers] success! uploaded to: ${uploadResponse.data}`); return uploadResponse.data; } else { - console.error('[Album Art] Upload failed. Catbox.moe responded with:'); + console.error('[covers] upload failed!! catbox responded with:'); console.error(` - Status: ${uploadResponse.status}, Data: ${uploadResponse.data}`); return null; } } catch (error) { - console.error(`[Album Art] A network error occurred during upload: ${error.message}`); + console.error(`[covers] a network error occurred during upload!! ${error.message}`); return null; } } -async function getJellyfinSession() { +async function getjellyfinSession() { try { const response = await axios.get(`${config.jellyfinServerUrl}/Sessions`, { headers: { 'X-Emby-Token': config.jellyfinApiKey } @@ -117,7 +113,7 @@ async function getJellyfinSession() { ); } catch (error) { if (error.code !== 'ECONNREFUSED') { - console.error(`[Jellyfin] Error fetching session: ${error.message}`); + console.error(`[jellyfin] error fetching session!! ${error.message}`); } return null; } @@ -125,14 +121,15 @@ async function getJellyfinSession() { async function getAlbumArtUrl(albumId, trackId) { const artId = albumId || trackId; - if (!artId) return 'jellyfin_logo'; + if (!artId) return 'disc'; if (albumArtCache.has(artId)) { return albumArtCache.get(artId); } - // This log is important because it only happens once per new album/single. - console.log(`[Album Art] New item detected (ID: ${artId}). Proceeding to upload...`); + albumArtCache.set(artId, 'disc'); // fallback so it doesnt spam the console with "uplloading" + + console.log(`[covers] new item detected!! (id: ${artId}) proceeding to upload...`); const localArtUrl = `${config.jellyfinServerUrl}/Items/${artId}/Images/Primary`; const publicUrl = await uploadImageToHost(localArtUrl); @@ -140,13 +137,15 @@ async function getAlbumArtUrl(albumId, trackId) { albumArtCache.set(artId, publicUrl); await saveCache(); return publicUrl; + } else { + console.log(`[covers] no art found for ${artId}, using fallback`); } - return 'jellyfin_logo'; + return 'disc'; } async function updatePresence() { - const session = await getJellyfinSession(); + const session = await getjellyfinSession(); if (session && session.NowPlayingItem && session.PlayState && !session.PlayState.IsPaused) { const item = session.NowPlayingItem; @@ -157,19 +156,19 @@ async function updatePresence() { lastTrackId = currentId; if (itemType === 'Audio') { - console.log(`[Discord] Now listening to: ${(item.Artists || []).join(', ')} - ${item.Name}`); + console.log(`[discord] now listening to: ${(item.Artists || []).join(', ')} - ${item.Name}`); } else if (itemType === 'Movie') { - console.log(`[Discord] Now watching movie: ${item.Name}`); + console.log(`[discord] now watching movie: ${item.Name}`); } else if (itemType === 'Episode') { - console.log(`[Discord] Now watching TV: ${item.SeriesName || 'Unknown Show'} - ${item.Name}`); + console.log(`[discord] now watching TV: ${item.SeriesName || 'Unknown Show'} - ${item.Name}`); } else { - console.log(`[Discord] Now playing: ${item.Name || itemType}`); + console.log(`[discord] now playing: ${item.Name || itemType}`); } } let details = item.Name || 'Playing media'; let state = ''; - let largeImageText = 'Jellyfin'; + let largeImageText = 'jellyfin'; let artAlbumId = null; let artTrackId = item.Id; @@ -193,14 +192,14 @@ async function updatePresence() { } const largeImageUrl = await getAlbumArtUrl(artAlbumId, artTrackId); - + // console.log('[discord] setting activity for user:', client.user?.username); sjhut up prick client.user?.setActivity({ details, state, largeImageKey: largeImageUrl, largeImageText, smallImageKey: 'jellyfin_logo', - smallImageText: 'Jellyfin', + smallImageText: 'jellyfin', startTimestamp: Date.now() - Math.floor(session.PlayState.PositionTicks / 10000), endTimestamp: Date.now() + Math.floor((item.RunTimeTicks - session.PlayState.PositionTicks) / 10000), type: itemType === 'Audio' ? 2 : 3 @@ -208,7 +207,7 @@ async function updatePresence() { } else { if (lastTrackId !== null) { - console.log('[Discord] Playback stopped or paused. Clearing presence.'); + console.log('[discord] playback stopped or paused... clearing presence'); lastTrackId = null; client.user?.clearActivity(); } @@ -216,35 +215,41 @@ async function updatePresence() { } client.on('ready', () => { - console.log(`[Discord] RPC connected for user ${client.user.username}`); - console.log('[Jellyfin] Monitoring for listening activity...'); + console.log(`[discord] rpc connected for user ${client.user.username}`); + console.log('[jellyfin] monitoring for listening activity...'); updatePresence(); setInterval(updatePresence, 2 * 1000); }); client.on('disconnected', () => { - console.log('[Discord] RPC disconnected. Will try to reconnect if the script is restarted.'); + console.log('[discord] rpc disconnected. will try to reconnect if the script is restarted!'); }); -// --- NEW: Connection logic with a 15-second timeout --- async function connectToDiscord() { - await loadCache(); // --- MODIFIED: Load cache before starting --- - console.log('[Discord] Connecting to RPC...'); + await loadCache(); // IMPORTANT FOR covers DO NOT TOUCH!@!!!!!!!!@ + console.log('[discord] connecting to rpc...'); try { - // Create a timeout promise const timeout = new Promise((_, reject) => - setTimeout(() => reject(new Error('Connection timed out after 15 seconds.')), 15000) + setTimeout(() => reject(new Error('connection timed out after 15 seconds!!')), 15000) ); - // Race the connection against the timeout await Promise.race([client.login(), timeout]); } catch (err) { - console.error(`\n[Discord] Failed to connect: ${err.message}`); - console.error("Please check the following:"); - console.error(" 1. Is the Discord desktop application running?"); - console.error(" 2. Is the 'discordClientId' in config.js correct?"); - console.error(" 3. Go to Discord Settings > Activity Privacy > 'Share your activity status by default' and ensure it's enabled."); - process.exit(1); // Exit the script so it doesn't run in a broken state + console.error(`\n[discord] failed to connect: ${err.message}`); + console.error("please check the following"); + console.error(" 1. is the discord client running?"); + console.error(" 2. is the 'discordClientId' in config.js correct?"); + console.error(" 3. go to discord settings > 'Activity Privacy' > 'Share your activity status by default' and ensure it's enabled"); + process.exit(1); } } -connectToDiscord(); \ No newline at end of file +connectToDiscord(); + +async function shutdown() { + console.log('[discord] shutting down, clearing presence...'); + await client.user?.clearActivity(); + process.exit(0); +} + +process.on('SIGINT', shutdown); +process.on('SIGTERM', shutdown); \ No newline at end of file diff --git a/package.json b/package.json index 9cc80a6..5df25c7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "index.js", "type": "module", "scripts": { - "start": "node index.js" + "start": "node index.js", + "dev": "nodemon index.js" }, "keywords": [ "jellyfin", @@ -17,6 +18,7 @@ "dependencies": { "@xhayper/discord-rpc": "^1.1.2", "axios": "^1.7.2", - "form-data": "^4.0.4" + "form-data": "^4.0.4", + "nodemon": "^2.0.22" } } \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc53eec..5687418 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: form-data: specifier: ^4.0.4 version: 4.0.5 + nodemon: + specifier: ^2.0.22 + version: 2.0.22 packages: @@ -48,20 +51,53 @@ packages: resolution: {integrity: sha512-Ih48GHiua7TtZgKO+f0uZPhCeQqb84fY2qUys/oMh8UbUfiUkUJLVCmd/v2AK0/pV33euh0aqSXo7+9LiPSwGw==} engines: {node: '>=20'} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} axios@1.14.0: resolution: {integrity: sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -89,6 +125,10 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -102,6 +142,11 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -113,10 +158,18 @@ packages: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} @@ -129,6 +182,25 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + ignore-by-default@1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + magic-bytes.js@1.13.0: resolution: {integrity: sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==} @@ -144,13 +216,66 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nodemon@2.0.22: + resolution: {integrity: sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==} + engines: {node: '>=8.10.0'} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + proxy-from-env@2.1.0: resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} engines: {node: '>=10'} + pstree.remy@1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@7.0.0: + resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} + hasBin: true + + simple-update-notifier@1.1.0: + resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} + engines: {node: '>=8.10.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + touch@3.1.1: + resolution: {integrity: sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==} + hasBin: true + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + undefsafe@2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + undici@6.24.1: resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==} engines: {node: '>=18.17'} @@ -203,6 +328,11 @@ snapshots: - bufferutil - utf-8-validate + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + asynckit@0.4.0: {} axios@1.14.0: @@ -213,15 +343,48 @@ snapshots: transitivePeerDependencies: - debug + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 + concat-map@0.0.1: {} + + debug@3.2.7(supports-color@5.5.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 5.5.0 + delayed-stream@1.0.0: {} discord-api-types@0.38.44: {} @@ -247,6 +410,10 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + follow-redirects@1.15.11: {} form-data@4.0.5: @@ -257,6 +424,9 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 + fsevents@2.3.3: + optional: true + function-bind@1.1.2: {} get-intrinsic@1.3.0: @@ -277,8 +447,14 @@ snapshots: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + gopd@1.2.0: {} + has-flag@3.0.0: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: @@ -289,6 +465,20 @@ snapshots: dependencies: function-bind: 1.1.2 + ignore-by-default@1.0.1: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + magic-bytes.js@1.13.0: {} math-intrinsics@1.1.0: {} @@ -299,10 +489,59 @@ snapshots: dependencies: mime-db: 1.52.0 + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + ms@2.1.3: {} + + nodemon@2.0.22: + dependencies: + chokidar: 3.6.0 + debug: 3.2.7(supports-color@5.5.0) + ignore-by-default: 1.0.1 + minimatch: 3.1.5 + pstree.remy: 1.1.8 + semver: 5.7.2 + simple-update-notifier: 1.1.0 + supports-color: 5.5.0 + touch: 3.1.1 + undefsafe: 2.0.5 + + normalize-path@3.0.0: {} + + picomatch@2.3.2: {} + proxy-from-env@2.1.0: {} + pstree.remy@1.1.8: {} + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + semver@5.7.2: {} + + semver@7.0.0: {} + + simple-update-notifier@1.1.0: + dependencies: + semver: 7.0.0 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + touch@3.1.1: {} + tslib@2.8.1: {} + undefsafe@2.0.5: {} + undici@6.24.1: {} ws@8.20.0: {}