jellyfin-rich-presence/WindowsFormsApplication2/JellyfinAPI.cs

466 lines
20 KiB
C#

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;
using System.Threading;
namespace JellyfinRPC
{
class JellyfinAPI
{
public static async Task<string> 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<string> 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<PlayingInfo?> GetCurrentlyPlaying()
{
using var httpClient = new HttpClient();
try
{
if (ConfigManager.GetEntry("JellyfinToken") == "")
{
var playingInfoResponse = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/Sessions?api_key={ConfigManager.GetEntry("APIKey")}");
playingInfoResponse.EnsureSuccessStatusCode();
var jsonResponse = await playingInfoResponse.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<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 = 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<JArray>();
if (mediaStreams != null)
{
foreach (var stream in mediaStreams)
{
var imageTag = stream["ImageTag"]?.ToString();
if (!string.IsNullOrEmpty(imageTag))
{
return imageTag;
}
}
}
return "album_cover";
}
public static string quickConnectToken;
public static bool formClosed;
public static bool authSuccess;
public static async Task AuthWithQuickConnect()
{
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") != "")
{
var quickConnectInitResponse = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/QuickConnect/Initiate");
authSuccess = false;
if (quickConnectInitResponse.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);
}
else
{
try
{
quickConnectInitResponse.EnsureSuccessStatusCode();
var jsonResponse = await quickConnectInitResponse.Content.ReadAsStringAsync();
var jsonObject = JObject.Parse(jsonResponse);
if (!jsonObject.HasValues)
{
}
else
{
string quickConnectCode = jsonObject.Value<string>("Code");
if (jsonObject.Value<string>("Secret") != null && jsonObject.Value<string>("Secret") != "")
{
quickConnectToken = jsonObject.Value<string>("Secret");
}
else
{
quickConnectToken = null;
}
(System.Windows.Forms.Application.OpenForms["ConfigForm"] as ConfigForm).label9.Text = quickConnectCode;
while (authSuccess == false && formClosed == false)
{
await CheckQuickConnectStatus();
}
}
}
catch (Exception ex)
{
#if DEBUG
System.Windows.Forms.MessageBox.Show(ex.ToString());
#endif
}
}
}
else
{
System.Windows.Forms.MessageBox.Show("No Server URL is set.", "Quick Connect Error", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
}
}
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 loginWithUsernameandPassResponse = await httpClient.PostAsync($"{ConfigManager.GetEntry("ServerURL")}/Users/AuthenticateByName", new StringContent(loginJson, Encoding.UTF8, "application/json"));
if (loginWithUsernameandPassResponse.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
{
loginWithUsernameandPassResponse.EnsureSuccessStatusCode();
var jsonResponse = await loginWithUsernameandPassResponse.Content.ReadAsStringAsync();
var jsonShit = JObject.Parse(jsonResponse);
string thatToken = jsonShit.Value<string>("AccessToken");
if (thatToken != null && thatToken != " ")
{
ConfigManager.SetEntry("JellyfinToken", thatToken);
}
}
}
}
public static async Task CheckQuickConnectStatus()
{
using var httpClient = new HttpClient();
if (quickConnectToken != null && quickConnectToken != "")
{
TaskEx.Delay(1000);
string secret = quickConnectToken.Replace('"', ' ').Trim();
HttpResponseMessage QuickConnectStatusResponse = await httpClient.GetAsync($"{ConfigManager.GetEntry("ServerURL")}/QuickConnect/Connect?Secret={quickConnectToken}");
QuickConnectStatusResponse.EnsureSuccessStatusCode();
var responseAsString = await QuickConnectStatusResponse.Content.ReadAsStringAsync();
var responseJson = JObject.Parse(responseAsString);
if (responseJson.Value<bool>("Authenticated") == true)
{
var quickConnectLoginSecret = new JellyfinAuth() { AccessToken = quickConnectToken };
var quickConnectLoginJson = JsonConvert.SerializeObject(quickConnectLoginSecret);
HttpResponseMessage quickConnectLoginResponse = await httpClient.PostAsync($"{ConfigManager.GetEntry("ServerURL")}/Users/AuthenticateWithQuickConnect", new StringContent(quickConnectLoginJson, Encoding.UTF8, "application/json"));
quickConnectLoginResponse.EnsureSuccessStatusCode();
var quickConnectLoginResponseAsString = await quickConnectLoginResponse.Content.ReadAsStringAsync();
var quickConnectLoginResponseJson = JObject.Parse(quickConnectLoginResponseAsString);
if (quickConnectLoginResponseJson.HasValues)
{
if (quickConnectLoginResponseJson.Value<string>("AccessToken") != null && quickConnectLoginResponseJson.Value<string>("AccessToken") != "")
{
ConfigManager.SetEntry("JellyfinToken", quickConnectLoginResponseJson.Value<string>("AccessToken"));
authSuccess = true;
(System.Windows.Forms.Application.OpenForms["ConfigForm"] as ConfigForm).label9.Text = "Authentication Successful.";
}
else
{
throw new Exception("The server did not return an access token, this shouldn't happen.");
}
}
else
{
System.Windows.Forms.MessageBox.Show("The server didn't respond for authentication.", "Authentication Error.", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Exclamation);
}
}
else
{
authSuccess = false;
}
}
}
}
// don't touch!
#region classes
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 JellyfinAuth
{
public User User { get; }
public string AccessToken { get; set; }
public string ServerId { get; }
}
public class User
{
public string Name { get; }
public string ServerId { get; }
public string Id { get; }
public string PrimaryImageTag { get; }
public bool HasPassword { get; }
public bool HasConfiguredPassword { get; }
public bool HasConfiguredEasyPassword { get; }
public bool EnableAutoLogin { get; }
public string LastLoginDate { get; }
public string LastActivityDate { get; }
public Configuration Configuration { get; }
public Policy Policy { get; }
public int PrimaryImageAspectRatio { get; }
}
public class SessionInfo
{
public JArray AdditionalUsers { get; }
public string RemoteEndPoint { get; }
public JArray PlayableMediaTypes { get; }
public string Id { get; }
public string UserId { get; }
public string UserName { get; }
public string Client { get; }
public string LastActivityDate { get; }
public string LastPlaybackCheckIn { get; }
public string LastPausedDate { get; }
public string DeviceName { get; }
public string DeviceType { get; }
public string DeviceId { get; }
public string ApplicationVersion { get; }
public bool IsActive { get; }
public bool SuportsMediaControl { get; }
public bool SupportsRemoteControl { get; }
public JArray NowPlayingQueue { get; }
public JArray NowPlayingQueueFullItems { get; }
public bool HasCustomDeviceName { get; }
public string PlaylistItemId { get; }
public string ServerId { get; }
public string UserPrimaryImageTag { get; }
public JArray SupportedCommands { get; }
}
public class Configuration
{
public string AudioLanguagePreference { get; }
public bool PlayDefaultAudioTrack { get; }
public string SubtitleLanguagePreference { get; }
public bool DisplayMissingEpisodes { get; }
public JArray GroupedFolders { get; }
public string SubtitleMode { get; }
public bool DisplayCollectionsView { get; }
public bool EnableLocalPassword { get; }
public JArray OrderedViews { get; }
public JArray LatestItemsExcludes { get; }
public JArray MyMediaExcludes { get; }
public bool HidePlayedInLatest { get; }
public bool RememberSubtitleSelections { get; }
public bool EnableNextEpisodeAutoPlay { get; }
public string CastRecieverId { get; }
}
public class Policy
{
// we don't actualy care about the policies, but i'm defining them in here just in case
public bool IsAdministrator { get; }
public bool IsHidden { get; }
public bool EnableCollectionManagement { get; }
public bool EnableSubtitleManagement { get; }
public bool EnableLyricManagement { get; }
public bool IsDisabled { get; }
public int MaxParentalRating { get; }
public JArray BlockedTags { get; }
public JArray AllowedTags { get; }
public bool EnableUserPreferenceAccess { get; }
public JArray AccessSchedules { get; }
public JArray BlockUnratedItems { get; }
public bool EnableRemoteControlOfOtherUsers { get; }
public bool EnableSharedDeviceControl { get; }
public bool EnableRemoteAccess { get; }
public bool EnableLiveTvManagement { get; }
public bool EnableLiveTvAccess { get; }
public bool EnableMediaPlayback { get; }
public bool EnableAudioPlaybackTranscoding { get; }
public bool EnableVideoPlaybackTranscoding { get; }
public bool EnablePlaybackRemuxing { get; }
public bool ForceRemoteSourceTranscoding { get; }
public bool EnableConetentDeletion { get; }
public JArray EnableContentDeletionFromFolders { get; }
public bool EnableContentDownloading { get; }
public bool EnableSyncTranscoding { get; }
public bool EnableMediaConverison { get; }
public JArray EnabledDevices { get; }
public bool EnableAllDevices { get; }
public JArray EnabledChannels { get; }
public bool EnableAllChannels { get; }
public JArray EnabledFolders { get; }
public bool EnableAllFolders { get; }
public int InvalidLoginAttemptCount { get; }
public int LoginAttemptsBeforeLockout { get; }
public int MaxActiveSessions { get; }
public bool EnablePublicSharing { get; }
public JArray BlockedMediaFolders { get; }
public JArray BlockedChannels { get; }
public int RemoteClientBitrateLimit { get; }
public string AuthenticationProviderId { get; }
public string PasswordResetProviderId { get; }
public string SyncPlayAccess { get; }
}
#endregion
}