using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using System.Reflection; using System.Security.Cryptography; using Newtonsoft.Json; using System.IO; namespace JellyfinRPC { class JellyfinAPI { public static async Task Jellyfin() { while (true) { try { var playingInfo = await GetCurrentlyPlaying(); if (playingInfo != null) { JToken nowPlaying = playingInfo.NowPlayingItem; string largeImageKey = playingInfo.IsMusic ? GetAlbumCover(nowPlaying) : await GetJellyfinLogo(); string largeImageText = playingInfo.IsMusic ? nowPlaying["Album"]?.ToString() ?? "Unknown Album" : "Jellyfin"; string details = playingInfo.IsMusic ? $"{playingInfo.Title}" : nowPlaying["SeriesName"] != null ? $"Watching {nowPlaying["SeriesName"]} - {playingInfo.Title}" : $"Watching {playingInfo.Title}"; string state = playingInfo.IsMusic ? $"{playingInfo.Artist}" : nowPlaying["SeriesName"] != null ? $"Season {playingInfo.Season}, Episode {playingInfo.Episode}" : ""; DateTime duration = playingInfo.Duration; DateTime progress = playingInfo.Progress; return $"{largeImageKey}|{largeImageText}|{details}|{state}|{duration}|{progress}"; } } catch (Exception ex) { ConsoleManager.WriteToConsole($"Error fetching Jellyfin data: {ex.Message}"); } } } public static string AssemblyVersion { get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); } } private static async Task GetJellyfinLogo() { using var httpClient = new HttpClient(); try { if (ConfigManager.GetEntry("JellyfinToken") == "") { var response = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/System/Info?api_key={ConfigManager.GetEntry("APIKey")}"); response.EnsureSuccessStatusCode(); var jsonResponse = await response.Content.ReadAsStringAsync(); var json = JArray.Parse(jsonResponse); var logoUrl = json["LogoUrl"]?.ToString(); return logoUrl ?? "jellyfin_logo"; } else { return "jellyfin_logo"; } } catch (Exception ex) { ConsoleManager.WriteToConsole($"Error fetching Jellyfin logo: {ex.Message}"); return "jellyfin_logo"; } } private static async Task GetCurrentlyPlaying() { using var httpClient = new HttpClient(); try { if (ConfigManager.GetEntry("JellyfinToken") == "") { var response = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/Sessions?api_key={ConfigManager.GetEntry("APIKey")}"); response.EnsureSuccessStatusCode(); var jsonResponse = await response.Content.ReadAsStringAsync(); var sessions = JArray.Parse(jsonResponse); foreach (var session in sessions) { if (session["UserId"]?.ToString() == ConfigManager.GetEntry("UserID") && 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 = GetAlbumCover(nowPlaying); var artists = nowPlaying["Artists"]?.ToObject(); 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 = new DateTime((long)session["PlayState"]["PositionTicks"]), //Progress = TimeSpan.FromTicks((long)session["PlayState"]["PositionTicks"]), Duration = new DateTime((long)nowPlaying["RunTimeTicks"]), //Duration = TimeSpan.FromTicks((long)nowPlaying["RunTimeTicks"]), IsMusic = isMusic, NowPlayingItem = nowPlaying }; } } } } catch (Exception ex) { ConsoleManager.WriteToConsole($"Error fetching Jellyfin data: {ex.Message}"); } return null; } private static string GetAlbumCover(JToken nowPlaying) { var mediaStreams = nowPlaying["MediaStreams"]?.ToObject(); if (mediaStreams != null) { foreach (var stream in mediaStreams) { var imageTag = stream["ImageTag"]?.ToString(); if (!string.IsNullOrEmpty(imageTag)) { return imageTag; } } } return "album_cover"; } public static async Task SendQuickConnectRequest() { using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", $"MediaBrowser Client=\"JellyfinRPC\", Device=\"{Environment.OSVersion}\", DeviceId=\"{ConfigManager.GetEntry("DeviceID")}\", Version=\"{AssemblyVersion}\""); if (ConfigManager.GetEntry("ServerURL") != "") { var response = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/QuickConnect/Initiate"); if (response.StatusCode is (System.Net.HttpStatusCode)401) { System.Windows.Forms.MessageBox.Show("This server does not have Quick Connect enabled.", "Quick Connect Unavailable.", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error, System.Windows.Forms.MessageBoxDefaultButton.Button1); return "Error: Server does not have Quick Connect Enabled."; } else { try { response.EnsureSuccessStatusCode(); var jsonResponse = await response.Content.ReadAsStringAsync(); var jsonObject = JObject.Parse(jsonResponse); if (!jsonObject.HasValues) { return "Error: Server did not respond."; } else { string quickConnectCode = jsonObject.Value("Code"); string quickConnectToken = jsonObject.Value("Secret"); return quickConnectCode; } } catch (Exception ex) { return $"{ex.Message.Trim()}"; } } } else { return "Error: No Server URL Set."; } } public static string DeviceID() { using RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); byte[] randomNumber = new byte[4]; rng.GetBytes(randomNumber); int value = BitConverter.ToInt32(randomNumber, 0); byte[] valueBytes = System.Text.Encoding.UTF8.GetBytes(value.ToString()); return System.Convert.ToBase64String(valueBytes); } public static async Task GetTokenFromUsernameAndPassword(string Username, string Password) { using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", $"MediaBrowser Client=\"JellyfinRPC {AssemblyVersion}, \", Device=\"{System.Net.Dns.GetHostName()}\", DeviceId=\"{ConfigManager.GetEntry("DeviceID")}\", Version=\"running on {Environment.OSVersion}\""); if (ConfigManager.GetEntry("ServerURL") != "") { string username = Username; string password = Password; var loginRequest = new Login() { username = username, pw = password }; var loginJson = JsonConvert.SerializeObject(loginRequest); HttpResponseMessage response = await httpClient.PostAsync($"{ConfigManager.GetEntry("ServerURL")}/Users/AuthenticateByName", new StringContent(loginJson, Encoding.UTF8, "application/json")); if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) { System.Windows.Forms.MessageBox.Show("The Username or Password is incorrect.", "Authentication Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error, System.Windows.Forms.MessageBoxDefaultButton.Button1); } else { response.EnsureSuccessStatusCode(); var jsonResponse = await response.Content.ReadAsStringAsync(); var jsonShit = JObject.Parse(jsonResponse); string thatToken = jsonShit.Value("AccessToken"); if (thatToken != null && thatToken != " ") { ConfigManager.SetEntry("JellyfinToken", thatToken); } } } } } public class Login { #pragma warning disable IDE1006 // Naming Styles public string username { get; set; } public string pw { get; set; } #pragma warning restore IDE1006 // Naming Styles } public class QuickConnect { [JsonProperty("Authenticated")] public bool Authenticated { get; set; } [JsonProperty("Secret")] public string Secret { get; set; } [JsonProperty("Code")] public string Code { get; set; } [JsonProperty("DeviceId")] public string DeviceId { get; set; } [JsonProperty("DeviceName")] public string DeviceName { get; set; } [JsonProperty("AppName")] public string AppName { get; set; } [JsonProperty("AppVersion")] public string AppVersion { get; set; } [JsonProperty("DateAdded")] public DateTime DateAdded { get; set; } } }