- Now displays "Jellyfin Discord RPC - Your Jellyfin Servers URL" in the title - Now only shows "Rich Presence updated!" once
265 lines
No EOL
9.9 KiB
C#
265 lines
No EOL
9.9 KiB
C#
using DiscordRPC;
|
|
using System;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
class Program
|
|
{
|
|
private const string ConfigFilePath = "config.json";
|
|
private static DiscordRpcClient _discordClient;
|
|
private static Config _config;
|
|
|
|
// Hard-coded the Discord Client ID
|
|
private const string DiscordClientId = "1312264302601834578";
|
|
|
|
static bool presenceUpdated = false; // Flag to track initial rich presence update
|
|
|
|
static async Task Main(string[] args)
|
|
{
|
|
// Load or create configuration first
|
|
LoadOrCreateConfig(); // Initialize _config first
|
|
|
|
// Now we can safely access _config for the console title
|
|
Console.WriteLine("Starting Jellyfin Discord Rich Presence...");
|
|
Console.Title = $"Jellyfin Discord RPC - {_config.JellyfinBaseUrl}";
|
|
|
|
// Initialize Discord Rich Presence
|
|
_discordClient = new DiscordRpcClient(DiscordClientId);
|
|
_discordClient.OnReady += (sender, e) =>
|
|
{
|
|
Console.WriteLine("Connected to Discord RPC!");
|
|
|
|
// Log message when connection to Discord is established
|
|
if (!presenceUpdated)
|
|
{
|
|
Console.WriteLine("Rich Presence updated!"); // This will only show once
|
|
presenceUpdated = true; // Set the flag to true to avoid logging it again
|
|
}
|
|
};
|
|
|
|
// Handle presence updates only once at the start
|
|
_discordClient.OnPresenceUpdate += (sender, e) =>
|
|
{
|
|
if (!presenceUpdated)
|
|
{
|
|
Console.WriteLine("Rich Presence updated!");
|
|
presenceUpdated = true; // Set the flag to true so it doesn't display again
|
|
}
|
|
};
|
|
|
|
_discordClient.Initialize();
|
|
|
|
// Poll Jellyfin API for currently playing media
|
|
var updateTask = UpdateRichPresence(); // Start the rich presence update task
|
|
|
|
await updateTask; // Wait for the rich presence update task to finish
|
|
}
|
|
|
|
|
|
private static async Task UpdateRichPresence()
|
|
{
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
var playingInfo = await GetCurrentlyPlaying().ConfigureAwait(false);
|
|
if (playingInfo != null)
|
|
{
|
|
JToken nowPlaying = playingInfo.NowPlayingItem;
|
|
|
|
string largeImageKey = playingInfo.IsMusic
|
|
? await GetAlbumCover(nowPlaying).ConfigureAwait(false)
|
|
: await GetJellyfinLogo().ConfigureAwait(false);
|
|
string largeImageText = playingInfo.IsMusic
|
|
? nowPlaying["Album"]?.ToString() ?? "Unknown Album"
|
|
: "Jellyfin Media Player";
|
|
|
|
string details = playingInfo.IsMusic
|
|
? $"{playingInfo.Title}" // Show song name
|
|
: nowPlaying["SeriesName"] != null
|
|
? $"Watching: {nowPlaying["SeriesName"]} - {playingInfo.Title}" // Show series and episode name
|
|
: $"Watching: {playingInfo.Title}"; // For movies, show only the title
|
|
|
|
string state = playingInfo.IsMusic
|
|
? $"{playingInfo.Artist}" // Show artist name
|
|
: nowPlaying["SeriesName"] != null
|
|
? $"Season {playingInfo.Season}, Episode {playingInfo.Episode}" // Show season and episode for series
|
|
: ""; // Leave state empty for movies
|
|
|
|
Console.WriteLine($"Updating presence with details: {details}, state: {state}");
|
|
|
|
_discordClient.SetPresence(new RichPresence
|
|
{
|
|
Details = details,
|
|
State = string.IsNullOrWhiteSpace(state) ? null : state, // Avoid sending empty state to Discord
|
|
Timestamps = new Timestamps
|
|
{
|
|
Start = DateTime.UtcNow - playingInfo.Progress,
|
|
End = DateTime.UtcNow + (playingInfo.Duration - playingInfo.Progress)
|
|
},
|
|
Assets = new Assets
|
|
{
|
|
LargeImageKey = largeImageKey,
|
|
LargeImageText = largeImageText
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
_discordClient.ClearPresence();
|
|
Console.WriteLine("No media playing, clearing presence.");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error fetching Jellyfin data: {ex.Message}");
|
|
}
|
|
|
|
// Wait before polling again
|
|
await Task.Delay(5000).ConfigureAwait(false);
|
|
}
|
|
}
|
|
|
|
private static async Task<string> GetJellyfinLogo()
|
|
{
|
|
using var httpClient = new HttpClient();
|
|
try
|
|
{
|
|
var response = await httpClient.GetAsync($"{_config.JellyfinBaseUrl}/System/Info?api_key={_config.JellyfinApiKey}").ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
|
var json = JObject.Parse(jsonResponse);
|
|
|
|
var logoUrl = json["LogoUrl"]?.ToString();
|
|
return logoUrl ?? "jellyfin_logo";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error fetching Jellyfin logo: {ex.Message}");
|
|
return "jellyfin_logo"; // Default fallback
|
|
}
|
|
}
|
|
|
|
private static void LoadOrCreateConfig()
|
|
{
|
|
if (File.Exists(ConfigFilePath))
|
|
{
|
|
var configJson = File.ReadAllText(ConfigFilePath);
|
|
_config = JsonSerializer.Deserialize<Config>(configJson);
|
|
}
|
|
else
|
|
{
|
|
_config = new Config();
|
|
Console.Write("Enter Jellyfin Server URL: ");
|
|
_config.JellyfinBaseUrl = Console.ReadLine();
|
|
Console.Write("Enter Jellyfin API Key: ");
|
|
_config.JellyfinApiKey = Console.ReadLine();
|
|
Console.Write("Enter Jellyfin User ID: ");
|
|
_config.JellyfinUserId = Console.ReadLine();
|
|
|
|
var configJson = JsonSerializer.Serialize(_config, new JsonSerializerOptions { WriteIndented = true });
|
|
File.WriteAllText(ConfigFilePath, configJson);
|
|
Console.WriteLine("Configuration saved to config.json.");
|
|
}
|
|
}
|
|
|
|
private static async Task<PlayingInfo?> GetCurrentlyPlaying()
|
|
{
|
|
using var httpClient = new HttpClient();
|
|
try
|
|
{
|
|
var response = await httpClient.GetAsync($"{_config.JellyfinBaseUrl}/Sessions?api_key={_config.JellyfinApiKey}").ConfigureAwait(false);
|
|
response.EnsureSuccessStatusCode();
|
|
|
|
var jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
|
var sessions = JArray.Parse(jsonResponse);
|
|
|
|
foreach (var session in sessions)
|
|
{
|
|
if (session["UserId"]?.ToString() == _config.JellyfinUserId && session["NowPlayingItem"] != null)
|
|
{
|
|
var nowPlaying = session["NowPlayingItem"];
|
|
var mediaType = nowPlaying["Type"]?.ToString();
|
|
bool isMusic = mediaType?.ToLower() == "audio";
|
|
|
|
string albumCover = "";
|
|
string artist = "Unknown Artist";
|
|
|
|
if (isMusic)
|
|
{
|
|
albumCover = await GetAlbumCover(nowPlaying).ConfigureAwait(false);
|
|
var artists = nowPlaying["Artists"]?.ToObject<JArray>();
|
|
if (artists != null && artists.Count > 0)
|
|
{
|
|
artist = artists[0].ToString();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
artist = "Unknown Artist";
|
|
}
|
|
|
|
return new PlayingInfo
|
|
{
|
|
Title = nowPlaying["Name"]?.ToString(),
|
|
Artist = artist,
|
|
AlbumCover = albumCover,
|
|
Season = nowPlaying["ParentIndexNumber"]?.ToString() ?? "N/A",
|
|
Episode = nowPlaying["IndexNumber"]?.ToString() ?? "N/A",
|
|
Progress = TimeSpan.FromTicks((long)session["PlayState"]["PositionTicks"]),
|
|
Duration = TimeSpan.FromTicks((long)nowPlaying["RunTimeTicks"]),
|
|
IsMusic = isMusic,
|
|
NowPlayingItem = nowPlaying
|
|
};
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error fetching Jellyfin data: {ex.Message}");
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static async Task<string> GetAlbumCover(JToken nowPlaying)
|
|
{
|
|
var mediaStreams = nowPlaying["MediaStreams"]?.ToObject<JArray>();
|
|
if (mediaStreams != null)
|
|
{
|
|
foreach (var stream in mediaStreams)
|
|
{
|
|
var imageTag = stream["ImageTag"]?.ToString();
|
|
if (!string.IsNullOrEmpty(imageTag))
|
|
{
|
|
return imageTag; // Return the album cover image tag
|
|
}
|
|
}
|
|
}
|
|
return "album_cover";
|
|
}
|
|
}
|
|
|
|
class Config
|
|
{
|
|
public string JellyfinBaseUrl { get; set; }
|
|
public string JellyfinApiKey { get; set; }
|
|
public string JellyfinUserId { get; set; }
|
|
}
|
|
|
|
class PlayingInfo
|
|
{
|
|
public string Title { get; set; }
|
|
public string Artist { get; set; }
|
|
public string AlbumCover { get; set; }
|
|
public string Season { get; set; }
|
|
public string Episode { get; set; }
|
|
public TimeSpan Progress { get; set; }
|
|
public TimeSpan Duration { get; set; }
|
|
public bool IsMusic { get; set; }
|
|
public JToken NowPlayingItem { get; set; }
|
|
} |