diff --git a/Jellyfin.Plugin.KodiSyncQueue/API/KodiSyncQueueController.cs b/Jellyfin.Plugin.KodiSyncQueue/API/KodiSyncQueueController.cs new file mode 100644 index 0000000..c9e3c16 --- /dev/null +++ b/Jellyfin.Plugin.KodiSyncQueue/API/KodiSyncQueueController.cs @@ -0,0 +1,222 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Mime; +using Jellyfin.Data.Entities; +using Jellyfin.Plugin.KodiSyncQueue.Entities; +using Jellyfin.Plugin.KodiSyncQueue.Utils; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Serialization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Plugin.KodiSyncQueue.API +{ + [ApiController] + [Produces(MediaTypeNames.Application.Json)] + public class KodiSyncQueueController : ControllerBase + { + private readonly ILogger _logger; + private readonly IJsonSerializer _jsonSerializer; + private readonly IUserManager _userManager; + private readonly ILibraryManager _libraryManager; + + public KodiSyncQueueController(ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager) + { + _logger = logger; + _jsonSerializer = jsonSerializer; + _userManager = userManager; + _libraryManager = libraryManager; + } + + /// + /// Gets Items for {UserID} from {UTC DATETIME} formatted as yyyy-MM-ddTHH:mm:ssZ using queryString LastUpdateDT. + /// + /// The user id. + /// UTC DateTime of Last Update, Format yyyy-MM-ddTHH:mm:ssZ. + /// Comma separated list of Collection Types to filter (movies,tvshows,music,musicvideos,boxsets. The filter query must be lowercase in both the name and the items + /// The . + [HttpGet("Jellyfin.Plugin.KodiSyncQueue/{userID}/GetItems")] + public ActionResult GetLibraryItemsQuery( + [FromRoute] string userId, + [FromQuery] string? lastUpdateDt, + [FromQuery] string? filter) + { + _logger.LogInformation("Sync Requested for UserID: '{UserId}' with LastUpdateDT: '{LastUpdateDT}'", userId, lastUpdateDt); + if (string.IsNullOrEmpty(lastUpdateDt)) + lastUpdateDt = "1900-01-01T00:00:00Z"; + + var filters = filter?.ToLower().Split(',').Select(f => + { + Enum.TryParse(f, true, out MediaType mediaType); + return mediaType; + }).ToArray(); + + return PopulateLibraryInfo( + userId, + lastUpdateDt, + filters + ); + } + + /// + /// Gets The Server Time in UTC format as yyyy-MM-ddTHH:mm:ssZ. + /// + /// The server UTC time as yyyy-MM-ddTHH:mm:ssZ. + [HttpGet("Jellyfin.Plugin.KodiSyncQueue/GetServerDateTime")] + public ActionResult GetServerTime() + { + _logger.LogInformation("Server Time Requested..."); + var info = new ServerTimeInfo(); + _logger.LogDebug("Class Variable Created!"); + DateTime dtNow = DateTime.UtcNow; + DateTime retDate; + + if (!int.TryParse(Plugin.Instance.Configuration.RetDays, out var retDays)) + { + retDays = 0; + } + + if (retDays == 0) + { + retDate = new DateTime(1900, 1, 1, 0, 0, 0); + } + else + { + retDate = new DateTime(dtNow.Year, dtNow.Month, dtNow.Day, 0, 0, 0); + retDate = retDate.AddDays(-retDays); + } + + _logger.LogDebug("Getting Ready to Set Variables!"); + info.ServerDateTime = $"{DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)}"; + info.RetentionDateTime = $"{retDate.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)}"; + + _logger.LogDebug("ServerDateTime = {ServerDateTime}, RetentionDateTime = {RetentionDateTime}", info.ServerDateTime, info.RetentionDateTime); + + return info; + } + + /// + /// Get SyncQueue Plugin Settings. + /// + /// The . + [HttpGet("Jellyfin.Plugin.KodiSyncQueue/GetPluginSettings")] + public ActionResult GetKodiPluginSettings() + { + _logger.LogInformation("Plugin Settings Requested..."); + var settings = new PluginSettings(); + _logger.LogDebug("Class Variable Created!"); + + _logger.LogDebug("Creating Settings Object Variables!"); + + if (!int.TryParse(Plugin.Instance.Configuration.RetDays, out var retDays)) + { + retDays = 0; + } + + settings.RetentionDays = retDays; + settings.IsEnabled = Plugin.Instance.Configuration.IsEnabled; + settings.TrackMovies = Plugin.Instance.Configuration.tkMovies; + settings.TrackTVShows = Plugin.Instance.Configuration.tkTVShows; + settings.TrackBoxSets = Plugin.Instance.Configuration.tkBoxSets; + settings.TrackMusic = Plugin.Instance.Configuration.tkMusic; + settings.TrackMusicVideos = Plugin.Instance.Configuration.tkMusicVideos; + + _logger.LogDebug("Sending Settings Object Back."); + + return settings; + } + + /// + /// Create a dynamic strm. + /// + /// The type. + /// The id. + /// The parent id. + /// The Season. + /// The kodi id. + /// The handler. + /// The name. + /// The strm string. + [HttpGet("Kodi/{type}/{id}/file.strm")] + [HttpGet("Kodi/{type}/{parentId}/{id}/file.strm")] + [HttpGet("Kodi/{type}/{parentId}/{season}/{id}/file.strm")] + public ActionResult GetStrmFile( + [FromRoute] string type, + [FromRoute] string id, + [FromRoute] string? parentId, + [FromRoute] string? season, + [FromQuery] string? kodiId, + [FromQuery] string? handler, + [FromQuery] string? name) + { + if (string.IsNullOrEmpty(handler)) + { + handler = "plugin://plugin.video.jellyfin"; + } + + string strm = handler + "?mode=play&id=" + id; + + if (!string.IsNullOrEmpty(kodiId)) + { + strm += "&dbid=" + kodiId; + } + + if (!string.IsNullOrEmpty(name)) + { + strm += "&filename=" + name; + } + + _logger.LogInformation("returning strm: {0}", strm); + return strm; + } + + private List GetAddedOrUpdatedItems(User user, IEnumerable ids) + { + var items = ids + .Select(id => _libraryManager.GetItemById(id)) + .Where(item => item != null) + .ToList(); + + var result = items.SelectMany(i => ApiUserCheck.TranslatePhysicalItemToUserLibrary(i, user, _libraryManager)).Select(i => i.Id.ToString("N")).Distinct().ToList(); + return result; + } + + private SyncUpdateInfo PopulateLibraryInfo( + string userId, + string lastRequestedDt, + IReadOnlyCollection filters) + { + var startTime = DateTime.UtcNow; + + _logger.LogDebug("Starting PopulateLibraryInfo..."); + + var info = new SyncUpdateInfo(); + + var userDt = DateTime.Parse(lastRequestedDt, CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal); + var dtl = (long)userDt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; + var user = _userManager.GetUserById(Guid.Parse(userId)); + + var itemsAdded = Plugin.Instance.DbRepo.GetItems(dtl, ItemStatus.Added, filters); + var itemsRemoved = Plugin.Instance.DbRepo.GetItems(dtl, ItemStatus.Removed, filters); + var itemsUpdated = Plugin.Instance.DbRepo.GetItems(dtl, ItemStatus.Updated, filters); + var userDataChanged = Plugin.Instance.DbRepo.GetUserInfos(dtl, userId, filters); + + info.ItemsAdded = GetAddedOrUpdatedItems(user, itemsAdded); + info.ItemsRemoved = itemsRemoved.Select(id => id.ToString("N")).ToList(); + info.ItemsUpdated = GetAddedOrUpdatedItems(user, itemsUpdated); + info.UserDataChanged = userDataChanged.Select(i => _jsonSerializer.DeserializeFromString(i.JsonData)).ToList(); + + _logger.LogInformation("Added: {AddedCount}, Removed: {RemovedCount}, Updated: {UpdatedCount}, Changed User Data: {ChangedUserDataCount}", + info.ItemsAdded.Count, info.ItemsRemoved.Count, info.ItemsUpdated.Count, info.UserDataChanged.Count); + TimeSpan diffDate = DateTime.UtcNow - startTime; + _logger.LogInformation("Request Finished Taking {TimeTaken}", diffDate.ToString("c")); + + return info; + } + } +} diff --git a/Jellyfin.Plugin.KodiSyncQueue/API/PluginSettingsAPI.cs b/Jellyfin.Plugin.KodiSyncQueue/API/PluginSettingsAPI.cs deleted file mode 100644 index a2631d7..0000000 --- a/Jellyfin.Plugin.KodiSyncQueue/API/PluginSettingsAPI.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Jellyfin.Plugin.KodiSyncQueue.Entities; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Plugin.KodiSyncQueue.API -{ - public class PluginSettingsAPI : IService - { - private readonly ILogger _logger; - - public PluginSettingsAPI(ILogger logger) - { - _logger = logger; - } - - public PluginSettings Get(GetPluginSettings request) - { - _logger.LogInformation("Plugin Settings Requested..."); - var settings = new PluginSettings(); - _logger.LogDebug("Class Variable Created!"); - - _logger.LogDebug("Creating Settings Object Variables!"); - - if (!int.TryParse(Plugin.Instance.Configuration.RetDays, out var retDays)) - { - retDays = 0; - } - - settings.RetentionDays = retDays; - settings.IsEnabled = Plugin.Instance.Configuration.IsEnabled; - settings.TrackMovies = Plugin.Instance.Configuration.tkMovies; - settings.TrackTVShows = Plugin.Instance.Configuration.tkTVShows; - settings.TrackBoxSets = Plugin.Instance.Configuration.tkBoxSets; - settings.TrackMusic = Plugin.Instance.Configuration.tkMusic; - settings.TrackMusicVideos = Plugin.Instance.Configuration.tkMusicVideos; - - _logger.LogDebug("Sending Settings Object Back."); - - return settings; - } - } -} diff --git a/Jellyfin.Plugin.KodiSyncQueue/API/RoutesAPI.cs b/Jellyfin.Plugin.KodiSyncQueue/API/RoutesAPI.cs deleted file mode 100644 index 84aeff3..0000000 --- a/Jellyfin.Plugin.KodiSyncQueue/API/RoutesAPI.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Jellyfin.Plugin.KodiSyncQueue.Entities; -using MediaBrowser.Model.Services; - -namespace Jellyfin.Plugin.KodiSyncQueue.API -{ - [Route("/Jellyfin.Plugin.KodiSyncQueue/{UserID}/GetItems", "GET", Summary = "Gets Items for {UserID} from {UTC DATETIME} formatted as yyyy-MM-ddTHH:mm:ssZ using queryString LastUpdateDT")] - public class GetLibraryItemsQuery : IReturn - { - [ApiMember(Name = "UserID", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - [ApiMember(Name = "LastUpdateDT", Description = "UTC DateTime of Last Update, Format yyyy-MM-ddTHH:mm:ssZ", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - [ApiMember(Name = "filter", Description = "Comma separated list of Collection Types to filter (movies,tvshows,music,musicvideos,boxsets", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string UserID { get; set; } - public string LastUpdateDT { get; set; } - public string filter { get; set; } - } - - [Route("/Jellyfin.Plugin.KodiSyncQueue/GetServerDateTime", "GET", Summary = "Gets The Server Time in UTC format as yyyy-MM-ddTHH:mm:ssZ")] - public class GetServerTime : IReturn - { - } - - [Route("/Jellyfin.Plugin.KodiSyncQueue/GetPluginSettings", "GET", Summary = "Get SyncQueue Plugin Settings")] - public class GetPluginSettings : IReturn - { - } - - [Route("/Kodi/{Type}/{Id}/file.strm", "GET", Summary = "Create a dynamic strm")] - [Route("/Kodi/{Type}/{ParentId}/{Id}/file.strm", "GET", Summary = "Create a dynamic strm")] - [Route("/Kodi/{Type}/{ParentId}/{Season}/{Id}/file.strm", "GET", Summary = "Create a dynamic strm")] - public class GetStrmFile - { - [ApiMember(Name = "Type", Description = "Media type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Type { get; set; } - - [ApiMember(Name = "Id", Description = "Item id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Id { get; set; } - - [ApiMember(Name = "KodiId", Description = "Kodi item id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string KodiId { get; set; } - - [ApiMember(Name = "Name", Description = "Strm name", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Name { get; set; } - - [ApiMember(Name = "Handler", Description = "Optional handler", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] - public string Handler { get; set; } - - [ApiMember(Name = "ParentId", Description = "Parent id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string ParentId { get; set; } - - [ApiMember(Name = "Season", Description = "Season number", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public string Season { get; set; } - } -} diff --git a/Jellyfin.Plugin.KodiSyncQueue/API/ServerTimeAPI.cs b/Jellyfin.Plugin.KodiSyncQueue/API/ServerTimeAPI.cs deleted file mode 100644 index ed54051..0000000 --- a/Jellyfin.Plugin.KodiSyncQueue/API/ServerTimeAPI.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Jellyfin.Plugin.KodiSyncQueue.Entities; -using System; -using System.Globalization; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Plugin.KodiSyncQueue.API -{ - public class ServerTimeAPI : IService - { - private readonly ILogger _logger; - - public ServerTimeAPI(ILogger logger) - { - _logger = logger; - } - - public ServerTimeInfo Get(GetServerTime request) - { - _logger.LogInformation("Server Time Requested..."); - var info = new ServerTimeInfo(); - _logger.LogDebug("Class Variable Created!"); - DateTime dtNow = DateTime.UtcNow; - DateTime retDate; - - if (!int.TryParse(Plugin.Instance.Configuration.RetDays, out var retDays)) - { - retDays = 0; - } - - if (retDays == 0) - { - retDate = new DateTime(1900, 1, 1, 0, 0, 0); - } - else - { - retDate = new DateTime(dtNow.Year, dtNow.Month, dtNow.Day, 0, 0, 0); - retDate = retDate.AddDays(-retDays); - } - - _logger.LogDebug("Getting Ready to Set Variables!"); - info.ServerDateTime = $"{DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)}"; - info.RetentionDateTime = $"{retDate.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)}"; - - _logger.LogDebug("ServerDateTime = {ServerDateTime}, RetentionDateTime = {RetentionDateTime}", info.ServerDateTime, info.RetentionDateTime); - - return info; - } - } -} diff --git a/Jellyfin.Plugin.KodiSyncQueue/API/StrmAPI.cs b/Jellyfin.Plugin.KodiSyncQueue/API/StrmAPI.cs deleted file mode 100644 index 2270c25..0000000 --- a/Jellyfin.Plugin.KodiSyncQueue/API/StrmAPI.cs +++ /dev/null @@ -1,38 +0,0 @@ -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Plugin.KodiSyncQueue.API -{ - class StrmAPI : IService - { - private readonly ILogger _logger; - - public StrmAPI(ILogger logger) - { - _logger = logger; - } - - public object Get(GetStrmFile request) - { - if (string.IsNullOrEmpty(request.Handler)) - { - request.Handler = "plugin://plugin.video.jellyfin"; - } - - string strm = request.Handler + "?mode=play&id=" + request.Id; - - if (!string.IsNullOrEmpty(request.KodiId)) - { - strm += "&dbid=" + request.KodiId; - } - - if (!string.IsNullOrEmpty(request.Name)) - { - strm += "&filename=" + request.Name; - } - - _logger.LogInformation("returning strm: {0}", strm); - return strm; - } - } -} diff --git a/Jellyfin.Plugin.KodiSyncQueue/API/SyncAPI.cs b/Jellyfin.Plugin.KodiSyncQueue/API/SyncAPI.cs deleted file mode 100644 index 72b05cf..0000000 --- a/Jellyfin.Plugin.KodiSyncQueue/API/SyncAPI.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Serialization; -using Jellyfin.Plugin.KodiSyncQueue.Entities; -using System.Globalization; -using Jellyfin.Data.Entities; -using Jellyfin.Plugin.KodiSyncQueue.Utils; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Services; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Plugin.KodiSyncQueue.API -{ - public class SyncAPI : IService - { - private readonly ILogger _logger; - private readonly IJsonSerializer _jsonSerializer; - private readonly IUserManager _userManager; - private readonly ILibraryManager _libraryManager; - - public SyncAPI(ILogger logger, IJsonSerializer jsonSerializer, IUserManager userManager, ILibraryManager libraryManager) - { - _logger = logger; - _jsonSerializer = jsonSerializer; - _userManager = userManager; - _libraryManager = libraryManager; - - _logger.LogInformation("SyncAPI Created and Listening at \"/Jellyfin.Plugin.KodiSyncQueue/{UserID}/GetItems?LastUpdateDT={LastUpdateDT}&format=json\" - {LastUpdateDT} must be a UTC DateTime formatted as yyyy-MM-ddTHH:mm:ssZ"); - _logger.LogInformation("The following parameters also exist to filter the results:"); - _logger.LogInformation("filter=movies,tvshows,music,musicvideos,boxsets"); - _logger.LogInformation("Results will be included by default and only filtered if added to the filter query..."); - _logger.LogInformation("the filter query must be lowercase in both the name and the items..."); - } - - public SyncUpdateInfo Get(GetLibraryItemsQuery request) - { - _logger.LogInformation("Sync Requested for UserID: '{UserId}' with LastUpdateDT: '{LastUpdateDT}'", request.UserID, request.LastUpdateDT); - if (string.IsNullOrEmpty(request.LastUpdateDT)) - request.LastUpdateDT = "1900-01-01T00:00:00Z"; - - var filters = request.filter?.ToLower().Split(',').Select(f => - { - Enum.TryParse(f, true, out MediaType mediaType); - return mediaType; - }).ToArray(); - - return PopulateLibraryInfo( - request.UserID, - request.LastUpdateDT, - filters - ); - } - - private List GetAddedOrUpdatedItems(User user, IEnumerable ids) - { - var items = ids - .Select(id => _libraryManager.GetItemById(id)) - .Where(item => item != null) - .ToList(); - - var result = items.SelectMany(i => ApiUserCheck.TranslatePhysicalItemToUserLibrary(i, user, _libraryManager)).Select(i => i.Id.ToString("N")).Distinct().ToList(); - return result; - } - - private SyncUpdateInfo PopulateLibraryInfo(string userId, string lastRequestedDt, - IReadOnlyCollection filters) - { - var startTime = DateTime.UtcNow; - - _logger.LogDebug("Starting PopulateLibraryInfo..."); - - var info = new SyncUpdateInfo(); - - var userDt = DateTime.Parse(lastRequestedDt, CultureInfo.CurrentCulture, DateTimeStyles.AssumeUniversal); - var dtl = (long)userDt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; - var user = _userManager.GetUserById(Guid.Parse(userId)); - - var itemsAdded = Plugin.Instance.DbRepo.GetItems(dtl, ItemStatus.Added, filters); - var itemsRemoved = Plugin.Instance.DbRepo.GetItems(dtl, ItemStatus.Removed, filters); - var itemsUpdated = Plugin.Instance.DbRepo.GetItems(dtl, ItemStatus.Updated, filters); - var userDataChanged = Plugin.Instance.DbRepo.GetUserInfos(dtl, userId, filters); - - info.ItemsAdded = GetAddedOrUpdatedItems(user, itemsAdded); - info.ItemsRemoved = itemsRemoved.Select(id => id.ToString("N")).ToList(); - info.ItemsUpdated = GetAddedOrUpdatedItems(user, itemsUpdated); - info.UserDataChanged = userDataChanged.Select(i => _jsonSerializer.DeserializeFromString(i.JsonData)).ToList(); - - _logger.LogInformation("Added: {AddedCount}, Removed: {RemovedCount}, Updated: {UpdatedCount}, Changed User Data: {ChangedUserDataCount}", - info.ItemsAdded.Count, info.ItemsRemoved.Count, info.ItemsUpdated.Count, info.UserDataChanged.Count); - TimeSpan diffDate = DateTime.UtcNow - startTime; - _logger.LogInformation("Request Finished Taking {TimeTaken}", diffDate.ToString("c")); - - return info; - } - } -} diff --git a/Jellyfin.Plugin.KodiSyncQueue/Jellyfin.Plugin.KodiSyncQueue.csproj b/Jellyfin.Plugin.KodiSyncQueue/Jellyfin.Plugin.KodiSyncQueue.csproj index b50af43..c31555a 100644 --- a/Jellyfin.Plugin.KodiSyncQueue/Jellyfin.Plugin.KodiSyncQueue.csproj +++ b/Jellyfin.Plugin.KodiSyncQueue/Jellyfin.Plugin.KodiSyncQueue.csproj @@ -15,6 +15,7 @@ +