added tv support WITH AI BECAUSE IM A LAZY PIECE OF SHIT

This commit is contained in:
yhqe committed 2026-04-03 02:20:32 -05:00
1 parent af4f9c1b32
commit 6b999102f5
4 files changed
+377 -48

No files matched your search

+19 -1
View File
@@ -2,5 +2,23 @@
"b0e14b847e862ba4872e85211fcc7734": "https://files.catbox.moe/w9sqhb.jpg", "b0e14b847e862ba4872e85211fcc7734": "https://files.catbox.moe/w9sqhb.jpg",
"cebf9979b121c2a246b517b5bc0ade5e": "https://files.catbox.moe/44ta2m.jpg", "cebf9979b121c2a246b517b5bc0ade5e": "https://files.catbox.moe/44ta2m.jpg",
"6aeacb78f346ceaf130b3ee9672bc9ab": "https://files.catbox.moe/p9bk6r.jpg", "6aeacb78f346ceaf130b3ee9672bc9ab": "https://files.catbox.moe/p9bk6r.jpg",
"159c2bdcb53fe7d19a0c03106ecd55f4": "https://files.catbox.moe/qoj4oi.jpg" "159c2bdcb53fe7d19a0c03106ecd55f4": "https://files.catbox.moe/qoj4oi.jpg",
"c427b476da833ea38902de144a48a912": "https://files.catbox.moe/s07d8w.jpg",
"c27d831861ae709568da129758fa6f87": "https://files.catbox.moe/03920g.jpg",
"89c9572e94d5ba8d9edf4fcd37ef2ca6": "https://files.catbox.moe/5obxkn.jpg",
"866be98c60d1d3c064fd31fa6c2c2808": "https://files.catbox.moe/xibz7b.jpg",
"1b23c513692160e12b2827a00919955a": "https://files.catbox.moe/mprl5j.jpg",
"9f91be03ccecc1adc52775eba774cb51": "https://files.catbox.moe/nx5nt9.jpg",
"458f11d73f21c8cc56d7ab55e97698e6": "https://files.catbox.moe/osuuxz.jpg",
"6dea3d9fe1461e301b098a71c2a4208b": "https://files.catbox.moe/c1ydf3.jpg",
"55f17e2af726b06eeb6d1556cbd12689": "https://files.catbox.moe/dty54m.jpg",
"36a61e3055e47bb0c8dcb966ff844713": "https://files.catbox.moe/7wl59l.jpeg",
"44c7a3898d9c1b670444f33b93c7a232": "https://files.catbox.moe/k2csev.jpg",
"15d2dbcea3390ce409f4fc23eaf28101": "https://files.catbox.moe/wulmed.jpg",
"42bd871263c632d345a27c12f8fdeaf6": "https://files.catbox.moe/158uq1.jpg",
"799f4dce9d226e36d22d455dbb3b8f98": "https://files.catbox.moe/xibz7b.jpg",
"4427d655e68b30cae2c4e75dedcaed83": "https://files.catbox.moe/a7gko6.jpg",
"bc93b85e70b106af5ffd6528a1429973": "https://files.catbox.moe/sc59sk.jpg",
"e38972d494df546c474c06690c89b22b": "https://files.catbox.moe/zvc60u.jpg",
"0d8c38bdfe09949f08ddd112e146b057": "https://files.catbox.moe/1zda8q.jpg"
} }
-16
View File
@@ -1,16 +0,0 @@
export default {
// 1. Discord Application Client ID
// Go to https://discord.com/developers/applications, create an application,
// and get the Client ID from the "OAuth2" page.
discordClientId: 'DISCORD_CLIENT_ID_HERE',
// 2. Jellyfin Server URL
// The public URL of your Jellyfin server.
jellyfinServerUrl: 'https://your.jellyfinserver.here',
// 3. Jellyfin API Key
// In Jellyfin, go to Dashboard > API Keys, and create a new key.
jellyfinApiKey: 'YOUR_JELLYFIN_API_KEY_HERE',
// 4. Jellyfin User ID
// In Jellyfin, go to Dashboard > Users, click on your user,
// and copy the user ID from the URL.
jellyfinUserId: 'YOUR_JELLYFIN_USER_ID_HERE',
};
+50 -31
View File
@@ -107,10 +107,13 @@ async function getJellyfinSession() {
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 }
}); });
const allowedTypes = new Set(['Audio', 'Movie', 'Episode', 'Video']);
return response.data.find(s => return response.data.find(s =>
s.UserId === config.jellyfinUserId && s.UserId === config.jellyfinUserId &&
s.NowPlayingItem && s.NowPlayingItem &&
s.NowPlayingItem.Type === 'Audio' allowedTypes.has(s.NowPlayingItem.Type)
); );
} catch (error) { } catch (error) {
if (error.code !== 'ECONNREFUSED') { if (error.code !== 'ECONNREFUSED') {
@@ -145,51 +148,67 @@ async function getAlbumArtUrl(albumId, trackId) {
async function updatePresence() { async function updatePresence() {
const session = await getJellyfinSession(); const session = await getJellyfinSession();
if (session && session.NowPlayingItem) { if (session && session.NowPlayingItem && session.PlayState && !session.PlayState.IsPaused) {
const track = session.NowPlayingItem; const item = session.NowPlayingItem;
const trackId = track.Id; const itemType = item.Type;
const albumId = track.AlbumId; const currentId = `${itemType}:${item.Id}`;
if (trackId !== lastTrackId) { if (currentId !== lastTrackId) {
lastTrackId = trackId; lastTrackId = currentId;
console.log(`[Discord] Now listening to: ${track.Artists.join(', ')} - ${track.Name}`);
if (itemType === 'Audio') {
console.log(`[Discord] Now listening to: ${(item.Artists || []).join(', ')} - ${item.Name}`);
} else if (itemType === 'Movie') {
console.log(`[Discord] Now watching movie: ${item.Name}`);
} else if (itemType === 'Episode') {
console.log(`[Discord] Now watching TV: ${item.SeriesName || 'Unknown Show'} - ${item.Name}`);
} else {
console.log(`[Discord] Now playing: ${item.Name || itemType}`);
}
} }
// pass trackid for singles let details = item.Name || 'Playing media';
const largeImageUrl = await getAlbumArtUrl(albumId, trackId); let state = '';
let largeImageText = 'Jellyfin';
let artAlbumId = null;
let artTrackId = item.Id;
/*const presence = new Presence() if (itemType === 'Audio') {
.setDetails(`${track.Name}`) details = item.Name;
.setState(`by ${track.Artists.join(', ')}`) state = `by ${(item.Artists || []).join(', ') || 'Unknown Artist'}`;
.setLargeImage(`${config.jellyfinServerUrl}/Items/${track.AlbumId}/Images/Primary`) largeImageText = `on ${item.Album || 'Unknown Album'}`;
.setLargeText(`on ${track.Album}`) artAlbumId = item.AlbumId;
.setSmallImage('jellyfin_logo') } else if (itemType === 'Movie') {
.setSmallText('Jellyfin') details = item.Name;
.setStartTimestamp(Date.now() - Math.floor(session.PlayState.PositionTicks / 10000)) state = `Watching movie`;
.addButton('Listen on Jellyfin', `${config.jellyfinServerUrl}/web/index.html#!/details?id=${trackId}`); largeImageText = item.ProductionYear ? `${item.ProductionYear}` : 'Movie';
artAlbumId = item.ParentId || null;
} else if (itemType === 'Episode') {
details = item.Name;
state = `${item.SeriesName || 'TV Show'}${item.ParentIndexNumber != null && item.IndexNumber != null
? ` • S${String(item.ParentIndexNumber).padStart(2, '0')}E${String(item.IndexNumber).padStart(2, '0')}`
: ''}`;
largeImageText = item.SeriesName || 'TV Show';
artAlbumId = item.SeriesId || item.SeasonId || null;
}
presence.data.type = 2; // Type 2 is for "Listening"*/ const largeImageUrl = await getAlbumArtUrl(artAlbumId, artTrackId);
client.user?.setActivity({ client.user?.setActivity({
details: `${track.Name}`, details,
state: `by ${track.Artists.join(', ')}`, state,
largeImageKey: largeImageUrl, largeImageKey: largeImageUrl,
largeImageText: `on ${track.Album}`, 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),
buttons: [ endTimestamp: Date.now() + Math.floor((item.RunTimeTicks - session.PlayState.PositionTicks) / 10000),
{ type: itemType === 'Audio' ? 2 : 3
label: 'Listen on Jellyfin',
url: `${config.jellyfinServerUrl}/web/index.html#!/details?id=${trackId}`
}
],
type: 2 // Type 2 is for "Listening"
}); });
} else { } else {
if (lastTrackId !== null) { if (lastTrackId !== null) {
console.log('[Discord] Playback stopped. Clearing presence.'); console.log('[Discord] Playback stopped or paused. Clearing presence.');
lastTrackId = null; lastTrackId = null;
client.user?.clearActivity(); client.user?.clearActivity();
} }
+308
View File
@@ -0,0 +1,308 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
'@xhayper/discord-rpc':
specifier: ^1.1.2
version: 1.3.3
axios:
specifier: ^1.7.2
version: 1.14.0
form-data:
specifier: ^4.0.4
version: 4.0.5
packages:
'@discordjs/collection@2.1.1':
resolution: {integrity: sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==}
engines: {node: '>=18'}
'@discordjs/rest@2.6.1':
resolution: {integrity: sha512-wwQdgjeaoYFiaG+atbqx6aJDpqW7JHAo0HrQkBTbYzM3/PJ3GweQIpgElNcGZ26DCUOXMyawYd0YF7vtr+fZXg==}
engines: {node: '>=18'}
'@discordjs/util@1.2.0':
resolution: {integrity: sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg==}
engines: {node: '>=18'}
'@sapphire/async-queue@1.5.5':
resolution: {integrity: sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==}
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
'@sapphire/snowflake@3.5.5':
resolution: {integrity: sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==}
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
'@vladfrangu/async_event_emitter@2.4.7':
resolution: {integrity: sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==}
engines: {node: '>=v14.0.0', npm: '>=7.0.0'}
'@xhayper/discord-rpc@1.3.3':
resolution: {integrity: sha512-Ih48GHiua7TtZgKO+f0uZPhCeQqb84fY2qUys/oMh8UbUfiUkUJLVCmd/v2AK0/pV33euh0aqSXo7+9LiPSwGw==}
engines: {node: '>=20'}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
axios@1.14.0:
resolution: {integrity: sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==}
call-bind-apply-helpers@1.0.2:
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
engines: {node: '>= 0.4'}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
discord-api-types@0.38.44:
resolution: {integrity: sha512-q91MgBzP/gRaCLIbQTaOrOhbD8uVIaPKxpgX2sfFB2nZ9nSiTYM9P3NFQ7cbO6NCxctI6ODttc67MI+YhIfILg==}
dunder-proto@1.0.1:
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
engines: {node: '>= 0.4'}
es-define-property@1.0.1:
resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
engines: {node: '>= 0.4'}
es-errors@1.3.0:
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
engines: {node: '>= 0.4'}
es-object-atoms@1.1.1:
resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
engines: {node: '>= 0.4'}
es-set-tostringtag@2.1.0:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
follow-redirects@1.15.11:
resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
form-data@4.0.5:
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
engines: {node: '>= 6'}
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
get-intrinsic@1.3.0:
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
engines: {node: '>= 0.4'}
get-proto@1.0.1:
resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
engines: {node: '>= 0.4'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
has-symbols@1.1.0:
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
engines: {node: '>= 0.4'}
has-tostringtag@1.0.2:
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
engines: {node: '>= 0.4'}
hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
magic-bytes.js@1.13.0:
resolution: {integrity: sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==}
math-intrinsics@1.1.0:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
proxy-from-env@2.1.0:
resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==}
engines: {node: '>=10'}
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
undici@6.24.1:
resolution: {integrity: sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==}
engines: {node: '>=18.17'}
ws@8.20.0:
resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
snapshots:
'@discordjs/collection@2.1.1': {}
'@discordjs/rest@2.6.1':
dependencies:
'@discordjs/collection': 2.1.1
'@discordjs/util': 1.2.0
'@sapphire/async-queue': 1.5.5
'@sapphire/snowflake': 3.5.5
'@vladfrangu/async_event_emitter': 2.4.7
discord-api-types: 0.38.44
magic-bytes.js: 1.13.0
tslib: 2.8.1
undici: 6.24.1
'@discordjs/util@1.2.0':
dependencies:
discord-api-types: 0.38.44
'@sapphire/async-queue@1.5.5': {}
'@sapphire/snowflake@3.5.5': {}
'@vladfrangu/async_event_emitter@2.4.7': {}
'@xhayper/discord-rpc@1.3.3':
dependencies:
'@discordjs/rest': 2.6.1
'@vladfrangu/async_event_emitter': 2.4.7
discord-api-types: 0.38.44
ws: 8.20.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
asynckit@0.4.0: {}
axios@1.14.0:
dependencies:
follow-redirects: 1.15.11
form-data: 4.0.5
proxy-from-env: 2.1.0
transitivePeerDependencies:
- debug
call-bind-apply-helpers@1.0.2:
dependencies:
es-errors: 1.3.0
function-bind: 1.1.2
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
delayed-stream@1.0.0: {}
discord-api-types@0.38.44: {}
dunder-proto@1.0.1:
dependencies:
call-bind-apply-helpers: 1.0.2
es-errors: 1.3.0
gopd: 1.2.0
es-define-property@1.0.1: {}
es-errors@1.3.0: {}
es-object-atoms@1.1.1:
dependencies:
es-errors: 1.3.0
es-set-tostringtag@2.1.0:
dependencies:
es-errors: 1.3.0
get-intrinsic: 1.3.0
has-tostringtag: 1.0.2
hasown: 2.0.2
follow-redirects@1.15.11: {}
form-data@4.0.5:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
es-set-tostringtag: 2.1.0
hasown: 2.0.2
mime-types: 2.1.35
function-bind@1.1.2: {}
get-intrinsic@1.3.0:
dependencies:
call-bind-apply-helpers: 1.0.2
es-define-property: 1.0.1
es-errors: 1.3.0
es-object-atoms: 1.1.1
function-bind: 1.1.2
get-proto: 1.0.1
gopd: 1.2.0
has-symbols: 1.1.0
hasown: 2.0.2
math-intrinsics: 1.1.0
get-proto@1.0.1:
dependencies:
dunder-proto: 1.0.1
es-object-atoms: 1.1.1
gopd@1.2.0: {}
has-symbols@1.1.0: {}
has-tostringtag@1.0.2:
dependencies:
has-symbols: 1.1.0
hasown@2.0.2:
dependencies:
function-bind: 1.1.2
magic-bytes.js@1.13.0: {}
math-intrinsics@1.1.0: {}
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
proxy-from-env@2.1.0: {}
tslib@2.8.1: {}
undici@6.24.1: {}
ws@8.20.0: {}