diff --git a/src/equicordplugins/jellyfinRichPresence/index.tsx b/src/equicordplugins/jellyfinRichPresence/index.tsx index 8a6c301a4..4de24d4f5 100644 --- a/src/equicordplugins/jellyfinRichPresence/index.tsx +++ b/src/equicordplugins/jellyfinRichPresence/index.tsx @@ -19,6 +19,11 @@ interface ActivityAssets { small_text?: string; } +interface ActivityButton { + label: string; + url: string; +} + interface Activity { state: string; details?: string; @@ -26,6 +31,7 @@ interface Activity { start?: number; }; assets?: ActivityAssets; + buttons?: Array; name: string; application_id: string; metadata?: { @@ -50,8 +56,6 @@ interface MediaData { position?: number; } - - const settings = definePluginSettings({ serverUrl: { description: "Jellyfin server URL (e.g., https://jellyfin.example.com)", @@ -78,6 +82,11 @@ const settings = definePluginSettings({ description: "Custom Rich Presence name (only used if 'Custom' is selected).\nOptions: {name}, {series}, {season}, {episode}, {artist}, {album}, {year}", type: OptionType.STRING, }, + showTMDBButton: { + description: "Show TheMovieDB button in Rich Presence", + type: OptionType.BOOLEAN, + default: true, + }, overrideRichPresenceType: { description: "Override the rich presence type", type: OptionType.SELECT, @@ -123,6 +132,27 @@ function setActivity(activity: Activity | null) { }); } +async function fetchTmdbData(query: string) { + try { + const res = await fetch(`https://api.vmohammad.dev/tmdb/search/multi?q=${encodeURIComponent(query)}`); + if (!res.ok) throw `${res.status} ${res.statusText}`; + const data = await res.json(); + if (data.results && data.results.length > 0) { + const topResult = data.results[0]; + return { + url: `https://www.themoviedb.org/${topResult.media_type}/${topResult.id}`, + posterPath: topResult.poster_path + ? `https://image.tmdb.org/t/p/original${topResult.poster_path}` + : null + }; + } + return null; + } catch (e) { + console.error("Failed to fetch TMDb data:", e); + return null; + } +} + export default definePlugin({ name: "JellyfinRichPresence", description: "Rich presence for Jellyfin media server", @@ -274,12 +304,26 @@ export default definePlugin({ break; } - const largeImage = mediaData.imageUrl; + let tmdbData: { url: string; posterPath?: string | null } | null = null; + if (settings.store.showTMDBButton) { + tmdbData = await fetchTmdbData(mediaData.seriesName || mediaData.name); + } + const assets: ActivityAssets = { - large_image: largeImage ? await getApplicationAsset(largeImage) : await getApplicationAsset("jellyfin"), - large_text: mediaData.album || mediaData.seriesName || undefined, + large_image: tmdbData?.posterPath ? await getApplicationAsset(tmdbData.posterPath) : mediaData.imageUrl ? await getApplicationAsset(mediaData.imageUrl) : undefined, + large_text: mediaData.seriesName || mediaData.album || undefined, }; + const buttons: ActivityButton[] = []; + if (settings.store.showTMDBButton) { + const result = await fetchTmdbData(mediaData.seriesName || mediaData.name); + if (result?.url) tmdbData = { url: result.url }; + buttons.push({ + label: "View on TheMovieDB", + url: `${tmdbData?.url}` + }); + } + const getDetails = () => { if (mediaData.type === "Episode" && mediaData.seriesName) { return mediaData.seriesName; @@ -291,7 +335,7 @@ export default definePlugin({ if (mediaData.type === "Episode" && mediaData.seriesName) { const season = mediaData.seasonNumber ? `S${mediaData.seasonNumber}` : ""; const episode = mediaData.episodeNumber ? `E${mediaData.episodeNumber}` : ""; - return `${mediaData.name} ${season} - ${episode}`.trim(); + return `${mediaData.name} (${season} - ${episode})`.trim(); } return mediaData.artist || (mediaData.year ? `(${mediaData.year})` : undefined); }; @@ -309,8 +353,12 @@ export default definePlugin({ assets, timestamps, + buttons: buttons.length ? buttons.map(v => v.label) : undefined, + metadata: { + button_urls: buttons.map(v => v.url), + }, type: richPresenceType, - flags: 1, + flags: 1 }; } });