added fallback cover art for songs with no cover art

This commit is contained in:
yhqe committed 2026-05-02 22:10:53 -05:00
1 parent c146e4b9c6
commit 42960d4e28
4 files changed
+305 -56

No files matched your search

+4 -1
View File
@@ -8,4 +8,7 @@ config.js
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
package-lock.json package-lock.json
# Art cache
art_cache.json
+58 -53
View File
@@ -9,8 +9,8 @@ const client = new Client({
clientId: config.discordClientId, clientId: config.discordClientId,
}); });
const albumArtCache = new Map(); // Cache for uploaded album art URLs const albumArtCache = new Map();
const CACHE_FILE = 'art_cache.json'; // The file to store our cache const CACHE_FILE = 'art_cache.json';
let lastTrackId = null; let lastTrackId = null;
async function loadCache() { async function loadCache() {
@@ -18,26 +18,24 @@ async function loadCache() {
const data = await fs.readFile(CACHE_FILE, 'utf-8'); const data = await fs.readFile(CACHE_FILE, 'utf-8');
if (!data) { 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; return;
} }
const parsed = JSON.parse(data); const parsed = JSON.parse(data);
// --- THE FIX --- // the fix...
// Instead of reassigning albumArtCache, clear the existing map
// and add the loaded entries to it. This preserves the global reference.
albumArtCache.clear(); albumArtCache.clear();
for (const [key, value] of Object.entries(parsed)) { for (const [key, value] of Object.entries(parsed)) {
albumArtCache.set(key, value); 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) { } catch (error) {
if (error.code === 'ENOENT') { 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 { } 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 { try {
const dataToSave = Object.fromEntries(albumArtCache); const dataToSave = Object.fromEntries(albumArtCache);
await fs.writeFile(CACHE_FILE, JSON.stringify(dataToSave, null, 2)); 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) { } 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<string|null>} The public URL of the uploaded image, or null on failure.
*/
async function uploadImageToHost(imageUrl) { async function uploadImageToHost(imageUrl) {
try { try {
const imageResponse = await axios.get(imageUrl, { const imageResponse = await axios.get(imageUrl, {
responseType: 'arraybuffer', responseType: 'arraybuffer',
validateStatus: () => true, // mmmmmm
}); });
if (imageResponse.status === 404) {
return null;
}
const imageBuffer = Buffer.from(imageResponse.data); const imageBuffer = Buffer.from(imageResponse.data);
if (imageBuffer.length === 0) { 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; return null;
} }
@@ -89,20 +85,20 @@ async function uploadImageToHost(imageUrl) {
}); });
if (uploadResponse.status === 200 && uploadResponse.data.startsWith('http')) { 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; return uploadResponse.data;
} else { } 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}`); console.error(` - Status: ${uploadResponse.status}, Data: ${uploadResponse.data}`);
return null; return null;
} }
} catch (error) { } 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; return null;
} }
} }
async function getJellyfinSession() { async function getjellyfinSession() {
try { try {
const response = await axios.get(`${config.jellyfinServerUrl}/Sessions`, { const response = await axios.get(`${config.jellyfinServerUrl}/Sessions`, {
headers: { 'X-Emby-Token': config.jellyfinApiKey } headers: { 'X-Emby-Token': config.jellyfinApiKey }
@@ -117,7 +113,7 @@ async function getJellyfinSession() {
); );
} catch (error) { } catch (error) {
if (error.code !== 'ECONNREFUSED') { if (error.code !== 'ECONNREFUSED') {
console.error(`[Jellyfin] Error fetching session: ${error.message}`); console.error(`[jellyfin] error fetching session!! ${error.message}`);
} }
return null; return null;
} }
@@ -125,14 +121,15 @@ async function getJellyfinSession() {
async function getAlbumArtUrl(albumId, trackId) { async function getAlbumArtUrl(albumId, trackId) {
const artId = albumId || trackId; const artId = albumId || trackId;
if (!artId) return 'jellyfin_logo'; if (!artId) return 'disc';
if (albumArtCache.has(artId)) { if (albumArtCache.has(artId)) {
return albumArtCache.get(artId); return albumArtCache.get(artId);
} }
// This log is important because it only happens once per new album/single. albumArtCache.set(artId, 'disc'); // fallback so it doesnt spam the console with "uplloading"
console.log(`[Album Art] New item detected (ID: ${artId}). Proceeding to upload...`);
console.log(`[covers] new item detected!! (id: ${artId}) proceeding to upload...`);
const localArtUrl = `${config.jellyfinServerUrl}/Items/${artId}/Images/Primary`; const localArtUrl = `${config.jellyfinServerUrl}/Items/${artId}/Images/Primary`;
const publicUrl = await uploadImageToHost(localArtUrl); const publicUrl = await uploadImageToHost(localArtUrl);
@@ -140,13 +137,15 @@ async function getAlbumArtUrl(albumId, trackId) {
albumArtCache.set(artId, publicUrl); albumArtCache.set(artId, publicUrl);
await saveCache(); await saveCache();
return publicUrl; return publicUrl;
} else {
console.log(`[covers] no art found for ${artId}, using fallback`);
} }
return 'jellyfin_logo'; return 'disc';
} }
async function updatePresence() { async function updatePresence() {
const session = await getJellyfinSession(); const session = await getjellyfinSession();
if (session && session.NowPlayingItem && session.PlayState && !session.PlayState.IsPaused) { if (session && session.NowPlayingItem && session.PlayState && !session.PlayState.IsPaused) {
const item = session.NowPlayingItem; const item = session.NowPlayingItem;
@@ -157,19 +156,19 @@ async function updatePresence() {
lastTrackId = currentId; lastTrackId = currentId;
if (itemType === 'Audio') { 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') { } else if (itemType === 'Movie') {
console.log(`[Discord] Now watching movie: ${item.Name}`); console.log(`[discord] now watching movie: ${item.Name}`);
} else if (itemType === 'Episode') { } 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 { } else {
console.log(`[Discord] Now playing: ${item.Name || itemType}`); console.log(`[discord] now playing: ${item.Name || itemType}`);
} }
} }
let details = item.Name || 'Playing media'; let details = item.Name || 'Playing media';
let state = ''; let state = '';
let largeImageText = 'Jellyfin'; let largeImageText = 'jellyfin';
let artAlbumId = null; let artAlbumId = null;
let artTrackId = item.Id; let artTrackId = item.Id;
@@ -193,14 +192,14 @@ async function updatePresence() {
} }
const largeImageUrl = await getAlbumArtUrl(artAlbumId, artTrackId); const largeImageUrl = await getAlbumArtUrl(artAlbumId, artTrackId);
// console.log('[discord] setting activity for user:', client.user?.username); sjhut up prick
client.user?.setActivity({ client.user?.setActivity({
details, details,
state, state,
largeImageKey: largeImageUrl, largeImageKey: largeImageUrl,
largeImageText, largeImageText,
smallImageKey: 'jellyfin_logo', smallImageKey: 'jellyfin_logo',
smallImageText: 'Jellyfin', smallImageText: 'jellyfin',
startTimestamp: Date.now() - Math.floor(session.PlayState.PositionTicks / 10000), startTimestamp: Date.now() - Math.floor(session.PlayState.PositionTicks / 10000),
endTimestamp: Date.now() + Math.floor((item.RunTimeTicks - session.PlayState.PositionTicks) / 10000), endTimestamp: Date.now() + Math.floor((item.RunTimeTicks - session.PlayState.PositionTicks) / 10000),
type: itemType === 'Audio' ? 2 : 3 type: itemType === 'Audio' ? 2 : 3
@@ -208,7 +207,7 @@ async function updatePresence() {
} else { } else {
if (lastTrackId !== null) { if (lastTrackId !== null) {
console.log('[Discord] Playback stopped or paused. Clearing presence.'); console.log('[discord] playback stopped or paused... clearing presence');
lastTrackId = null; lastTrackId = null;
client.user?.clearActivity(); client.user?.clearActivity();
} }
@@ -216,35 +215,41 @@ async function updatePresence() {
} }
client.on('ready', () => { client.on('ready', () => {
console.log(`[Discord] RPC connected for user ${client.user.username}`); console.log(`[discord] rpc connected for user ${client.user.username}`);
console.log('[Jellyfin] Monitoring for listening activity...'); console.log('[jellyfin] monitoring for listening activity...');
updatePresence(); updatePresence();
setInterval(updatePresence, 2 * 1000); setInterval(updatePresence, 2 * 1000);
}); });
client.on('disconnected', () => { 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() { async function connectToDiscord() {
await loadCache(); // --- MODIFIED: Load cache before starting --- await loadCache(); // IMPORTANT FOR covers DO NOT TOUCH!@!!!!!!!!@
console.log('[Discord] Connecting to RPC...'); console.log('[discord] connecting to rpc...');
try { try {
// Create a timeout promise
const timeout = new Promise((_, reject) => 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]); await Promise.race([client.login(), timeout]);
} catch (err) { } catch (err) {
console.error(`\n[Discord] Failed to connect: ${err.message}`); console.error(`\n[discord] failed to connect: ${err.message}`);
console.error("Please check the following:"); console.error("please check the following");
console.error(" 1. Is the Discord desktop application running?"); console.error(" 1. is the discord client running?");
console.error(" 2. Is the 'discordClientId' in config.js correct?"); 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."); 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 process.exit(1);
} }
} }
connectToDiscord(); 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);
+4 -2
View File
@@ -5,7 +5,8 @@
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "node index.js" "start": "node index.js",
"dev": "nodemon index.js"
}, },
"keywords": [ "keywords": [
"jellyfin", "jellyfin",
@@ -17,6 +18,7 @@
"dependencies": { "dependencies": {
"@xhayper/discord-rpc": "^1.1.2", "@xhayper/discord-rpc": "^1.1.2",
"axios": "^1.7.2", "axios": "^1.7.2",
"form-data": "^4.0.4" "form-data": "^4.0.4",
"nodemon": "^2.0.22"
} }
} }
+239
View File
@@ -17,6 +17,9 @@ importers:
form-data: form-data:
specifier: ^4.0.4 specifier: ^4.0.4
version: 4.0.5 version: 4.0.5
nodemon:
specifier: ^2.0.22
version: 2.0.22
packages: packages:
@@ -48,20 +51,53 @@ packages:
resolution: {integrity: sha512-Ih48GHiua7TtZgKO+f0uZPhCeQqb84fY2qUys/oMh8UbUfiUkUJLVCmd/v2AK0/pV33euh0aqSXo7+9LiPSwGw==} resolution: {integrity: sha512-Ih48GHiua7TtZgKO+f0uZPhCeQqb84fY2qUys/oMh8UbUfiUkUJLVCmd/v2AK0/pV33euh0aqSXo7+9LiPSwGw==}
engines: {node: '>=20'} engines: {node: '>=20'}
anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
asynckit@0.4.0: asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
axios@1.14.0: axios@1.14.0:
resolution: {integrity: sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==} 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: call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'} 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: combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'} 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: delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
@@ -89,6 +125,10 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
follow-redirects@1.15.11: follow-redirects@1.15.11:
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
@@ -102,6 +142,11 @@ packages:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'} 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: function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
@@ -113,10 +158,18 @@ packages:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
gopd@1.2.0: gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'} 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: has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -129,6 +182,25 @@ packages:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'} 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: magic-bytes.js@1.13.0:
resolution: {integrity: sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==} resolution: {integrity: sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==}
@@ -144,13 +216,66 @@ packages:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'} 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: proxy-from-env@2.1.0:
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'} 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: tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
undefsafe@2.0.5:
resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==}
undici@6.24.1: undici@6.24.1:
resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==} resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==}
engines: {node: '>=18.17'} engines: {node: '>=18.17'}
@@ -203,6 +328,11 @@ snapshots:
- bufferutil - bufferutil
- utf-8-validate - utf-8-validate
anymatch@3.1.3:
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.2
asynckit@0.4.0: {} asynckit@0.4.0: {}
axios@1.14.0: axios@1.14.0:
@@ -213,15 +343,48 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- debug - 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: call-bind-apply-helpers@1.0.2:
dependencies: dependencies:
es-errors: 1.3.0 es-errors: 1.3.0
function-bind: 1.1.2 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: combined-stream@1.0.8:
dependencies: dependencies:
delayed-stream: 1.0.0 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: {} delayed-stream@1.0.0: {}
discord-api-types@0.38.44: {} discord-api-types@0.38.44: {}
@@ -247,6 +410,10 @@ snapshots:
has-tostringtag: 1.0.2 has-tostringtag: 1.0.2
hasown: 2.0.2 hasown: 2.0.2
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
follow-redirects@1.15.11: {} follow-redirects@1.15.11: {}
form-data@4.0.5: form-data@4.0.5:
@@ -257,6 +424,9 @@ snapshots:
hasown: 2.0.2 hasown: 2.0.2
mime-types: 2.1.35 mime-types: 2.1.35
fsevents@2.3.3:
optional: true
function-bind@1.1.2: {} function-bind@1.1.2: {}
get-intrinsic@1.3.0: get-intrinsic@1.3.0:
@@ -277,8 +447,14 @@ snapshots:
dunder-proto: 1.0.1 dunder-proto: 1.0.1
es-object-atoms: 1.1.1 es-object-atoms: 1.1.1
glob-parent@5.1.2:
dependencies:
is-glob: 4.0.3
gopd@1.2.0: {} gopd@1.2.0: {}
has-flag@3.0.0: {}
has-symbols@1.1.0: {} has-symbols@1.1.0: {}
has-tostringtag@1.0.2: has-tostringtag@1.0.2:
@@ -289,6 +465,20 @@ snapshots:
dependencies: dependencies:
function-bind: 1.1.2 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: {} magic-bytes.js@1.13.0: {}
math-intrinsics@1.1.0: {} math-intrinsics@1.1.0: {}
@@ -299,10 +489,59 @@ snapshots:
dependencies: dependencies:
mime-db: 1.52.0 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: {} 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: {} tslib@2.8.1: {}
undefsafe@2.0.5: {}
undici@6.24.1: {} undici@6.24.1: {}
ws@8.20.0: {} ws@8.20.0: {}