diff --git a/Jellyfin.Plugin.Simkl.sln b/Jellyfin.Plugin.Simkl.sln deleted file mode 100644 index 7e3ce7a..0000000 --- a/Jellyfin.Plugin.Simkl.sln +++ /dev/null @@ -1,16 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Plugin.Simkl", "Jellyfin.Plugin.Simkl\Jellyfin.Plugin.Simkl.csproj", "{2C1895EC-A7E1-48B6-A2E0-3E847D1FB748}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2C1895EC-A7E1-48B6-A2E0-3E847D1FB748}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2C1895EC-A7E1-48B6-A2E0-3E847D1FB748}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2C1895EC-A7E1-48B6-A2E0-3E847D1FB748}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2C1895EC-A7E1-48B6-A2E0-3E847D1FB748}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/Jellyfin.Plugin.Simkl/API/Endpoints.cs b/Jellyfin.Plugin.Simkl/API/Endpoints.cs deleted file mode 100644 index 00a8582..0000000 --- a/Jellyfin.Plugin.Simkl/API/Endpoints.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System; -using System.Threading.Tasks; -using Jellyfin.Plugin.Simkl.API.Objects; -using Jellyfin.Plugin.Simkl.API.Responses; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace Jellyfin.Plugin.Simkl.API -{ - /// - /// The simkl endpoints. - /// - [ApiController] - [Authorize(Policy = "DefaultAuthorization")] - [Route("Simkl")] - public class Endpoints : ControllerBase - { - private readonly SimklApi _simklApi; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the . - public Endpoints(SimklApi simklApi) - { - _simklApi = simklApi; - } - - /// - /// Gets the oauth pin. - /// - /// The oauth pin. - [HttpGet("oauth/pin")] - public async Task> GetPin() - { - return await _simklApi.GetCode(); - } - - /// - /// Gets the status for the code. - /// - /// The user auth code. - /// The code status response. - [HttpGet("oauth/pin/{userCode}")] - public async Task> GetPinStatus([FromRoute] string userCode) - { - return await _simklApi.GetCodeStatus(userCode); - } - - /// - /// Gets the settings for the user. - /// - /// The user id. - /// The user settings. - [HttpGet("users/settings/{userId}")] - public async Task> GetUserSettings([FromRoute] Guid userId) - { - var userConfiguration = SimklPlugin.Instance?.Configuration.GetByGuid(userId); - if (userConfiguration == null) - { - return NotFound(); - } - - return await _simklApi.GetUserSettings(userConfiguration.UserToken); - } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Exceptions/InvalidTokenException.cs b/Jellyfin.Plugin.Simkl/API/Exceptions/InvalidTokenException.cs deleted file mode 100644 index 9ff88d8..0000000 --- a/Jellyfin.Plugin.Simkl/API/Exceptions/InvalidTokenException.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; - -namespace Jellyfin.Plugin.Simkl.API.Exceptions -{ - /// - public class InvalidTokenException : Exception - { - /// - /// Initializes a new instance of the class. - /// - public InvalidTokenException() - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - public InvalidTokenException(string msg) - : base(msg) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The message. - /// The inner exception. - public InvalidTokenException(string msg, Exception inner) - : base(msg, inner) - { - } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/Season.cs b/Jellyfin.Plugin.Simkl/API/Objects/Season.cs deleted file mode 100644 index 2b74912..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/Season.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Season. - /// - public class Season - { - /// - /// Gets or sets the season number. - /// - [JsonPropertyName("number")] - public int? Number { get; set; } - - /// - /// Gets or sets the episodes. - /// - [JsonPropertyName("episodes")] - public IReadOnlyList Episodes { get; set; } = Array.Empty(); - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/ShowEpisode.cs b/Jellyfin.Plugin.Simkl/API/Objects/ShowEpisode.cs deleted file mode 100644 index b5f7f8b..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/ShowEpisode.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Show episode. - /// - public class ShowEpisode - { - /// - /// Gets or sets episode number. - /// - [JsonPropertyName("number")] - public int? Number { get; set; } - // TODO: watched_at - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklEpisode.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklEpisode.cs deleted file mode 100644 index 38d1ffd..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklEpisode.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Text.Json.Serialization; -#pragma warning disable SA1300 - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl episode container. - /// - public class SimklEpisode : SimklMediaObject - { - /// - /// Gets or sets watched at. - /// - [JsonPropertyName("watched_at")] - public DateTime? WatchedAt { get; set; } - - /// - /// Gets or sets the title. - /// - [JsonPropertyName("title")] - public string? Title { get; set; } - - /// - /// Gets or sets the season. - /// - [JsonPropertyName("season")] - public int? Season { get; set; } - - /// - /// Gets or sets the episode. - /// - [JsonPropertyName("episode")] - public int? Episode { get; set; } - - /// - /// Gets or sets multipart. - /// - [JsonPropertyName("multipart")] - public bool? Multipart { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklFile.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklFile.cs deleted file mode 100644 index b79685d..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklFile.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl File. - /// - public class SimklFile - { - /// - /// Gets or sets the file. - /// - [JsonPropertyName("file")] - public string? File { get; set; } - - /// - /// Gets or sets the part. - /// - [JsonPropertyName("part")] - public int? Part { get; set; } - - /// - /// Gets or sets the hash. - /// - [JsonPropertyName("hash")] - public string? Hash { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklHistory.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklHistory.cs deleted file mode 100644 index ff6a5c6..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklHistory.cs +++ /dev/null @@ -1,41 +0,0 @@ -#pragma warning disable CA2227 - -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl history container. - /// - public class SimklHistory - { - /// - /// Initializes a new instance of the class. - /// - public SimklHistory() - { - Movies = new List(); - Shows = new List(); - Episodes = new List(); - } - - /// - /// Gets or sets list of movies. - /// - [JsonPropertyName("movies")] - public List Movies { get; set; } - - /// - /// Gets or sets the list of shows. - /// - [JsonPropertyName("shows")] - public List Shows { get; set; } - - /// - /// Gets or sets the list of episodes. - /// - [JsonPropertyName("episodes")] - public List Episodes { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklIds.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklIds.cs deleted file mode 100644 index fbfef7f..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklIds.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl Ids. - /// - public class SimklIds - { - /// - /// Initializes a new instance of the class. - /// - /// The provider ids. - public SimklIds(Dictionary providerIds) - { - foreach (var (key, value) in providerIds) - { - if (key.Equals(nameof(Simkl), StringComparison.OrdinalIgnoreCase)) - { - Simkl = Convert.ToInt32(value, CultureInfo.InvariantCulture); - } - else if (key.Equals(nameof(Imdb), StringComparison.OrdinalIgnoreCase)) - { - Imdb = value; - } - else if (key.Equals(nameof(Slug), StringComparison.OrdinalIgnoreCase)) - { - Slug = value; - } - else if (key.Equals(nameof(Netflix), StringComparison.OrdinalIgnoreCase)) - { - Netflix = value; - } - else if (key.Equals(nameof(Tmdb), StringComparison.OrdinalIgnoreCase)) - { - Tmdb = value; - } - } - } - - /// - /// Gets or sets the simkl id. - /// - [JsonPropertyName("simkl")] - public int? Simkl { get; set; } - - /// - /// Gets or sets the imdb id. - /// - [JsonPropertyName("imdb")] - public string? Imdb { get; set; } - - /// - /// Gets or sets the slug. - /// - [JsonPropertyName("slug")] - public string? Slug { get; set; } - - /// - /// Gets or sets the netflix id. - /// - [JsonPropertyName("netflix")] - public string? Netflix { get; set; } - - /// - /// Gets or sets the TMDb id. - /// - [JsonPropertyName("tmdb")] - public string? Tmdb { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklMediaObject.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklMediaObject.cs deleted file mode 100644 index bc99412..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklMediaObject.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl media object. - /// - public class SimklMediaObject - { - /// - /// Gets or sets ids. - /// - [JsonPropertyName("ids")] - public SimklIds? Ids { get; set; } - } -} diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklMovie.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklMovie.cs deleted file mode 100644 index 4b41406..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklMovie.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Text.Json.Serialization; -using MediaBrowser.Model.Dto; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl movie. - /// - public class SimklMovie : SimklMediaObject - { - /// - /// Initializes a new instance of the class. - /// - /// The base item dto. - public SimklMovie(BaseItemDto item) - { - Title = item.OriginalTitle; - Year = item.ProductionYear; - Ids = new SimklMovieIds(item.ProviderIds); - WatchedAt = DateTime.UtcNow; - } - - /// - /// Gets or sets the movie title. - /// - [JsonPropertyName("title")] - public string? Title { get; set; } - - /// - /// Gets or sets the year. - /// - [JsonPropertyName("year")] - public int? Year { get; set; } - - /// - /// Gets or sets watched at. - /// - [JsonPropertyName("watched_at")] - public DateTime? WatchedAt { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklMovieIds.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklMovieIds.cs deleted file mode 100644 index 163bdc9..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklMovieIds.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl movie ids. - /// - public class SimklMovieIds : SimklIds - { - /// - /// Initializes a new instance of the class. - /// - /// the provider movie ids. - public SimklMovieIds(Dictionary providerMovieIds) - : base(providerMovieIds) - { - } - - /// - /// Gets or sets the tvdb id. - /// - [JsonPropertyName("tvdb")] - public int? Tvdb { get; set; } - - /// - /// Gets or sets the mal id. - /// - [JsonPropertyName("mal")] - public int? Mal { get; set; } - - /// - /// Gets or sets the anidb id. - /// - [JsonPropertyName("anidb")] - public int? Anidb { get; set; } - - /// - /// Gets or sets the hulu id. - /// - [JsonPropertyName("hulu")] - public int? Hulu { get; set; } - - /// - /// Gets or sets the crunchyroll id. - /// - [JsonPropertyName("crunchyroll")] - public int? Crunchyroll { get; set; } - - /// - /// Gets or sets the movie db id. - /// - [JsonPropertyName("moviedb")] - public string? Moviedb { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklShow.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklShow.cs deleted file mode 100644 index 32cf3d2..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklShow.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; -using MediaBrowser.Model.Dto; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl show. - /// - public class SimklShow : SimklMediaObject - { - /// - /// Initializes a new instance of the class. - /// - /// The media info. - public SimklShow(BaseItemDto mediaInfo) - { - Title = mediaInfo.SeriesName; - Ids = new SimklShowIds(mediaInfo.ProviderIds); - Year = mediaInfo.ProductionYear; - Seasons = new[] - { - new Season - { - Number = mediaInfo.ParentIndexNumber, - Episodes = new[] - { - new ShowEpisode - { - Number = mediaInfo.IndexNumber - } - } - } - }; - } - - /// - /// Gets or sets title. - /// - [JsonPropertyName("title")] - public string Title { get; set; } - - /// - /// Gets or sets year. - /// - [JsonPropertyName("year")] - public int? Year { get; set; } - - /// - /// Gets or sets seasons. - /// - [JsonPropertyName("seasons")] - public IReadOnlyList Seasons { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/SimklShowIds.cs b/Jellyfin.Plugin.Simkl/API/Objects/SimklShowIds.cs deleted file mode 100644 index a5e3b57..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/SimklShowIds.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// Simkl show ids. - /// - public class SimklShowIds : SimklIds - { - /// - /// Initializes a new instance of the class. - /// - /// The provider movie ids. - public SimklShowIds(Dictionary providerMovieIds) - : base(providerMovieIds) - { - } - - /// - /// Gets or sets tvdb. - /// - [JsonPropertyName("tvdb")] - public int? Tvdb { get; set; } - - /// - /// Gets or sets mal. - /// - [JsonPropertyName("mal")] - public int? Mal { get; set; } - - /// - /// Gets or sets anidb. - /// - [JsonPropertyName("anidb")] - public int? Anidb { get; set; } - - /// - /// Gets or sets hulu. - /// - [JsonPropertyName("hulu")] - public int? Hulu { get; set; } - - /// - /// Gets or sets crunchyroll. - /// - [JsonPropertyName("crunchyroll")] - public int? Crunchyroll { get; set; } - - /// - /// Gets or sets zap2it. - /// - [JsonPropertyName("zap2It")] - public string? Zap2It { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/User.cs b/Jellyfin.Plugin.Simkl/API/Objects/User.cs deleted file mode 100644 index 34fc4a8..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/User.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Text.Json.Serialization; - -#pragma warning disable SA1300 - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// User. - /// - public class User - { - /// - /// Gets or sets name. - /// - [JsonPropertyName("name")] - public string? Name { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Objects/UserSettings.cs b/Jellyfin.Plugin.Simkl/API/Objects/UserSettings.cs deleted file mode 100644 index 4ba17fa..0000000 --- a/Jellyfin.Plugin.Simkl/API/Objects/UserSettings.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Jellyfin.Plugin.Simkl.API.Objects -{ - /// - /// User settings. - /// - public class UserSettings - { - /// - /// Gets or sets user. - /// - [JsonPropertyName("user")] - public User? User { get; set; } - - /// - /// Gets or sets error. - /// - [JsonPropertyName("error")] - public string? Error { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Responses/CodeResponse.cs b/Jellyfin.Plugin.Simkl/API/Responses/CodeResponse.cs deleted file mode 100644 index cf94224..0000000 --- a/Jellyfin.Plugin.Simkl/API/Responses/CodeResponse.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Text.Json.Serialization; -#pragma warning disable SA1300 - -namespace Jellyfin.Plugin.Simkl.API.Responses -{ - /// - /// Code response. - /// - public class CodeResponse - { - /// - /// Gets or sets result. - /// - public string? Result { get; set; } - - /// - /// Gets or sets device code. - /// - [JsonPropertyName("device_code")] - public string? DeviceCode { get; set; } - - /// - /// Gets or sets user code. - /// - [JsonPropertyName("user_code")] - public string? UserCode { get; set; } - - /// - /// Gets or sets verification url. - /// - [JsonPropertyName("verification_url")] - public string? VerificationUrl { get; set; } - - /// - /// Gets or sets expires in. - /// - [JsonPropertyName("expires_in")] - public int? ExpiresIn { get; set; } - - /// - /// Gets or sets interval. - /// - public int? Interval { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Responses/CodeStatusResponse.cs b/Jellyfin.Plugin.Simkl/API/Responses/CodeStatusResponse.cs deleted file mode 100644 index baa7e1b..0000000 --- a/Jellyfin.Plugin.Simkl/API/Responses/CodeStatusResponse.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text.Json.Serialization; -#pragma warning disable SA1300 - -namespace Jellyfin.Plugin.Simkl.API.Responses -{ - /// - /// Code status response. - /// - public class CodeStatusResponse - { - /// - /// Gets or sets result. - /// - public string? Result { get; set; } - - /// - /// Gets or sets message. - /// - public string? Message { get; set; } - - /// - /// Gets or sets access token. - /// - [JsonPropertyName("access_token")] - public string? AccessToken { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Responses/SearchFileResponse.cs b/Jellyfin.Plugin.Simkl/API/Responses/SearchFileResponse.cs deleted file mode 100644 index 7f5690c..0000000 --- a/Jellyfin.Plugin.Simkl/API/Responses/SearchFileResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Jellyfin.Plugin.Simkl.API.Objects; - -namespace Jellyfin.Plugin.Simkl.API.Responses -{ - /// - /// Search file response. - /// - public class SearchFileResponse - { - /// - /// Gets or sets type. - /// - public string? Type { get; set; } - - /// - /// Gets or sets episode. - /// - public SimklEpisode? Episode { get; set; } - - /// - /// Gets or sets movie. - /// - public SimklMovie? Movie { get; set; } - - /// - /// Gets or sets show. - /// - public SimklShow? Show { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryNotFound.cs b/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryNotFound.cs deleted file mode 100644 index 67a67e3..0000000 --- a/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryNotFound.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using Jellyfin.Plugin.Simkl.API.Objects; - -namespace Jellyfin.Plugin.Simkl.API.Responses -{ - /// - /// sync history not found. - /// - public class SyncHistoryNotFound - { - /// - /// Gets or sets movies. - /// - public SimklMovie[] Movies { get; set; } = Array.Empty(); - - /// - /// Gets or sets shows. - /// - public SimklShow[] Shows { get; set; } = Array.Empty(); - - /// - /// Gets or sets episodes. - /// - public SimklEpisode[] Episodes { get; set; } = Array.Empty(); - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryResponse.cs b/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryResponse.cs deleted file mode 100644 index 107f053..0000000 --- a/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryResponse.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Text.Json.Serialization; -#pragma warning disable SA1300 - -namespace Jellyfin.Plugin.Simkl.API.Responses -{ - /// - /// Sync history response. - /// - public class SyncHistoryResponse - { - /// - /// Gets or sets added. - /// - public SyncHistoryResponseCount Added { get; set; } = new SyncHistoryResponseCount(); - - /// - /// Gets or sets not found. - /// - [JsonPropertyName("not_found")] - public SyncHistoryNotFound? NotFound { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryResponseCount.cs b/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryResponseCount.cs deleted file mode 100644 index 291433a..0000000 --- a/Jellyfin.Plugin.Simkl/API/Responses/SyncHistoryResponseCount.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Jellyfin.Plugin.Simkl.API.Responses -{ - /// - /// Sync history response count. - /// - public class SyncHistoryResponseCount - { - /// - /// Gets or sets movies. - /// - public int Movies { get; set; } - - /// - /// Gets or sets shows. - /// - public int Shows { get; set; } - - /// - /// Gets or sets episodes. - /// - public int Episodes { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/API/SimklApi.cs b/Jellyfin.Plugin.Simkl/API/SimklApi.cs deleted file mode 100644 index 631b5a7..0000000 --- a/Jellyfin.Plugin.Simkl/API/SimklApi.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using System.IO; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Net.Mime; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Jellyfin.Plugin.Simkl.API.Exceptions; -using Jellyfin.Plugin.Simkl.API.Objects; -using Jellyfin.Plugin.Simkl.API.Responses; -using MediaBrowser.Common.Json; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Dto; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Plugin.Simkl.API -{ - /// - /// Simkl Api. - /// - public class SimklApi - { - /* INTERFACES */ - private readonly ILogger _logger; - private readonly IHttpClientFactory _httpClientFactory; - private readonly JsonSerializerOptions _jsonSerializerOptions; - - /* BASIC API THINGS */ - - /// - /// Base url. - /// - public const string Baseurl = @"https://api.simkl.com"; - - /// - /// Redirect uri. - /// - public const string RedirectUri = @"https://simkl.com/apps/jellyfin/connected/"; - - /// - /// Api key. - /// - public const string Apikey = @"c721b22482097722a84a20ccc579cf9d232be85b9befe7b7805484d0ddbc6781"; - - /// - /// Secret. - /// - public const string Secret = @"87893fc73cdbd2e51a7c63975c6f941ac1c6155c0e20ffa76b83202dd10a507e"; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - public SimklApi(ILogger logger, IHttpClientFactory httpClientFactory) - { - _logger = logger; - _httpClientFactory = httpClientFactory; - _jsonSerializerOptions = JsonDefaults.GetOptions(); - } - - /// - /// Get code. - /// - /// Code response. - public async Task GetCode() - { - var uri = $"/oauth/pin?client_id={Apikey}&redirect={RedirectUri}"; - return await Get(uri); - } - - /// - /// Get code status. - /// - /// User code. - /// Code status. - public async Task GetCodeStatus(string userCode) - { - var uri = $"/oauth/pin/{userCode}?client_id={Apikey}"; - return await Get(uri); - } - - /// - /// Get user settings. - /// - /// User token. - /// User settings. - public async Task GetUserSettings(string userToken) - { - try - { - return await Post("/users/settings/", userToken); - } - catch (HttpRequestException e) when (e.StatusCode == System.Net.HttpStatusCode.Unauthorized) - { - // Wontfix: Custom status codes - // "You don't get to pick your response code" - Luke (System Architect of Emby) - // https://emby.media/community/index.php?/topic/61889-wiki-issue-resultfactorythrowerror/ - return new UserSettings { Error = "user_token_failed" }; - } - } - - /// - /// Mark as watched. - /// - /// Item. - /// User token. - /// Status. - public async Task<(bool Success, BaseItemDto Item)> MarkAsWatched(BaseItemDto item, string userToken) - { - var history = CreateHistoryFromItem(item); - var r = await SyncHistoryAsync(history, userToken); - _logger.LogDebug("Response: {@Response}", r); - if (r != null && history.Movies.Count == r.Added.Movies && history.Shows.Count == r.Added.Shows) - { - return (true, item); - } - - // If we are here, is because the item has not been found - // let's try scrobbling from full path - try - { - (history, item) = await GetHistoryFromFileName(item); - } - catch (InvalidDataException) - { - // Let's try again but this time using only the FILE name - _logger.LogDebug("Couldn't scrobble using full path, trying using only filename"); - (history, item) = await GetHistoryFromFileName(item, false); - } - - r = await SyncHistoryAsync(history, userToken); - return r == null - ? (false, item) - : (history.Movies.Count == r.Added.Movies && history.Shows.Count == r.Added.Shows, item); - } - - /// - /// Get from file. - /// - /// Filename. - /// Search file response. - private async Task GetFromFile(string filename) - { - var f = new SimklFile { File = filename }; - _logger.LogInformation("Posting: {@File}", f); - return await Post("/search/file/", null, f); - } - - /// - /// Get history from file name. - /// - /// Item. - /// Full path. - /// Srobble history. - private async Task<(SimklHistory history, BaseItemDto item)> GetHistoryFromFileName(BaseItemDto item, bool fullpath = true) - { - var fname = fullpath ? item.Path : Path.GetFileName(item.Path); - var mo = await GetFromFile(fname); - if (mo == null) - { - throw new InvalidDataException("Search file response is null"); - } - - var history = new SimklHistory(); - if (mo.Movie != null && - (item.IsMovie == true || string.Equals(item.Type, nameof(Movie), StringComparison.OrdinalIgnoreCase))) - { - if (!string.Equals(mo.Type, "movie", StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidDataException("type != movie (" + mo.Type + ")"); - } - - item.Name = mo.Movie.Title; - item.ProductionYear = mo.Movie.Year; - history.Movies.Add(mo.Movie); - } - else if (mo.Episode != null - && mo.Show != null - && (item.IsSeries == true || string.Equals(item.Type, nameof(Episode), StringComparison.OrdinalIgnoreCase))) - { - if (!string.Equals(mo.Type, "episode", StringComparison.OrdinalIgnoreCase)) - { - throw new InvalidDataException("type != episode (" + mo.Type + ")"); - } - - item.Name = mo.Episode.Title; - item.SeriesName = mo.Show.Title; - item.IndexNumber = mo.Episode.Episode; - item.ParentIndexNumber = mo.Episode.Season; - item.ProductionYear = mo.Show.Year; - history.Episodes.Add(mo.Episode); - } - - return (history, item); - } - - private static HttpRequestMessage GetOptions(string? userToken = null) - { - var requestMessage = new HttpRequestMessage(); - requestMessage.Headers.TryAddWithoutValidation("simkl-api-key", Apikey); - if (!string.IsNullOrEmpty(userToken)) - { - requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", userToken); - } - - return requestMessage; - } - - private static SimklHistory CreateHistoryFromItem(BaseItemDto item) - { - var history = new SimklHistory(); - - if (item.IsMovie == true || string.Equals(item.Type, nameof(Movie), StringComparison.OrdinalIgnoreCase)) - { - history.Movies.Add(new SimklMovie(item)); - } - else if (item.IsSeries == true || string.Equals(item.Type, nameof(Episode), StringComparison.OrdinalIgnoreCase)) - { - // TODO: TV Shows scrobbling (WIP) - history.Shows.Add(new SimklShow(item)); - } - - return history; - } - - /// - /// Implements /sync/history method from simkl. - /// - /// History object. - /// User token. - /// The sync history response. - private async Task SyncHistoryAsync(SimklHistory history, string userToken) - { - try - { - _logger.LogInformation("Syncing History"); - return await Post("/sync/history", userToken, history); - } - catch (HttpRequestException e) when (e.StatusCode == System.Net.HttpStatusCode.Unauthorized) - { - _logger.LogError(e, "Invalid user token {UserToken}, deleting", userToken); - SimklPlugin.Instance?.Configuration.DeleteUserToken(userToken); - throw new InvalidTokenException("Invalid user token " + userToken); - } - } - - /// - /// API's private get method, given RELATIVE url and headers. - /// - /// Relative url. - /// Authentication token. - /// HTTP(s) Stream to be used. - private async Task Get(string url, string? userToken = null) - { - // Todo: If string is not null neither empty - using var options = GetOptions(userToken); - options.RequestUri = new Uri(Baseurl + url); - options.Method = HttpMethod.Get; - var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default) - .SendAsync(options); - return await responseMessage.Content.ReadFromJsonAsync(_jsonSerializerOptions); - } - - /// - /// API's private post method. - /// - /// Relative post url. - /// Authentication token. - /// Object to serialize. - private async Task Post(string url, string? userToken = null, T2? data = null) - where T2 : class - { - using var options = GetOptions(userToken); - options.RequestUri = new Uri(Baseurl + url); - options.Method = HttpMethod.Post; - if (data != null) - { - options.Content = new StringContent( - JsonSerializer.Serialize(data, _jsonSerializerOptions), - Encoding.UTF8, - MediaTypeNames.Application.Json); - } - - var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default) - .SendAsync(options); - return await responseMessage.Content.ReadFromJsonAsync(_jsonSerializerOptions); - } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/Configuration/PluginConfiguration.cs b/Jellyfin.Plugin.Simkl/Configuration/PluginConfiguration.cs deleted file mode 100644 index 89e8b9b..0000000 --- a/Jellyfin.Plugin.Simkl/Configuration/PluginConfiguration.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Linq; -using MediaBrowser.Model.Plugins; - -namespace Jellyfin.Plugin.Simkl.Configuration -{ - /// - /// Class needed to create a Plugin and configure it. - /// - public class PluginConfiguration : BasePluginConfiguration - { - /// - /// Initializes a new instance of the class. - /// - public PluginConfiguration() - { - UserConfigs = Array.Empty(); - } - - /// - /// Gets or sets the list of user configs. - /// - public UserConfig[] UserConfigs { get; set; } - - /// - /// Get config by id. - /// - /// The user id. - /// Stored user config. - public UserConfig? GetByGuid(Guid id) - { - return UserConfigs.FirstOrDefault(c => c.Id == id); - } - - /// - /// Delete user token. - /// - /// User token. - public void DeleteUserToken(string userToken) - { - foreach (var config in UserConfigs) - { - if (config.UserToken == userToken) - { - config.UserToken = string.Empty; - } - } - - SimklPlugin.Instance?.SaveConfiguration(); - } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/Configuration/UserConfig.cs b/Jellyfin.Plugin.Simkl/Configuration/UserConfig.cs deleted file mode 100644 index 24ce4b6..0000000 --- a/Jellyfin.Plugin.Simkl/Configuration/UserConfig.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; - -namespace Jellyfin.Plugin.Simkl.Configuration -{ - /// - /// User config. - /// - public class UserConfig - { - /// - /// Initializes a new instance of the class. - /// - public UserConfig() - { - ScrobbleMovies = true; - ScrobbleShows = true; - ScrobblePercentage = 70; - ScrobbleNowWatchingPercentage = 5; - MinLength = 5; - UserToken = string.Empty; // Todo: check if token is still valid - ScrobbleTimeout = 30; - } - - /// - /// Gets or sets a value indicating whether scrobble movies. - /// - public bool ScrobbleMovies { get; set; } - - /// - /// Gets or sets a value indicating whether scrobble shows. - /// - public bool ScrobbleShows { get; set; } - - /// - /// Gets or sets scrobble percentage. - /// - public int ScrobblePercentage { get; set; } - - /// - /// Gets or sets scrobble now watching percentage. - /// - public int ScrobbleNowWatchingPercentage { get; set; } - - /// - /// Gets or sets min length. - /// - /// - /// Minimum length for scrobbling (in minutes). - /// - public int MinLength { get; set; } - - /// - /// Gets or sets user token. - /// - public string UserToken { get; set; } // Is the user logged in - - /// - /// Gets or sets scrobble timeout. - /// - /// - /// Time between scrobbling tries. - /// - public int ScrobbleTimeout { get; set; } - - /// - /// Gets or sets user id. - /// - public Guid Id { get; set; } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/Configuration/configPage.html b/Jellyfin.Plugin.Simkl/Configuration/configPage.html deleted file mode 100644 index 166ff64..0000000 --- a/Jellyfin.Plugin.Simkl/Configuration/configPage.html +++ /dev/null @@ -1,313 +0,0 @@ - - - - Simkl's TV Tracker - - - - - - Simkl's TV Tracker - - - - - - - - It seems you are not logged in, do you wish to log in? - Log In - Create an account - - - Logging In - - - 900 seconds remaining - Cancel - - - Hello again USERNAME! - - Log Out - Scrobbling options: - - - - Autoscrobbling Movies - - - - - - Autoscrobbling TV Shows - - - - - - Percentage watched needed to scrobble - - - ${Save} - - ${Cancel} - - - - - - - - - \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/Jellyfin.Plugin.Simkl.csproj b/Jellyfin.Plugin.Simkl/Jellyfin.Plugin.Simkl.csproj deleted file mode 100644 index a84f577..0000000 --- a/Jellyfin.Plugin.Simkl/Jellyfin.Plugin.Simkl.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - net5.0 - 1.0.0.0 - 1.0.0.0 - true - true - enable - - - - - - - - - - - - - - - - - - - - - ../jellyfin.ruleset - - - - - - - diff --git a/Jellyfin.Plugin.Simkl/PluginServiceRegistrator.cs b/Jellyfin.Plugin.Simkl/PluginServiceRegistrator.cs deleted file mode 100644 index a96e4d0..0000000 --- a/Jellyfin.Plugin.Simkl/PluginServiceRegistrator.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Jellyfin.Plugin.Simkl.API; -using MediaBrowser.Common.Plugins; -using Microsoft.Extensions.DependencyInjection; - -namespace Jellyfin.Plugin.Simkl -{ - /// - public class PluginServiceRegistrator : IPluginServiceRegistrator - { - /// - public void RegisterServices(IServiceCollection serviceCollection) - { - serviceCollection.AddScoped(); - } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/Services/PlaybackScrobbler.cs b/Jellyfin.Plugin.Simkl/Services/PlaybackScrobbler.cs deleted file mode 100644 index 8bb6613..0000000 --- a/Jellyfin.Plugin.Simkl/Services/PlaybackScrobbler.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading.Tasks; -using Jellyfin.Plugin.Simkl.API; -using Jellyfin.Plugin.Simkl.API.Exceptions; -using Jellyfin.Plugin.Simkl.Configuration; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Dto; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Plugin.Simkl.Services -{ - /// - /// Playback progress scrobbler. - /// - public class PlaybackScrobbler : IServerEntryPoint - { - private readonly ISessionManager _sessionManager; // Needed to set up de startPlayBack and endPlayBack functions - private readonly ILogger _logger; - private readonly Dictionary _lastScrobbled; // Library ID of last scrobbled item - private readonly SimklApi _simklApi; - private DateTime _nextTry; - - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - /// Instance of the . - public PlaybackScrobbler( - ISessionManager sessionManager, - ILogger logger, - SimklApi simklApi) - { - _sessionManager = sessionManager; - _logger = logger; - _simklApi = simklApi; - _lastScrobbled = new Dictionary(); - _nextTry = DateTime.UtcNow; - } - - /// - public Task RunAsync() - { - _sessionManager.PlaybackProgress += OnPlaybackProgress; - _sessionManager.PlaybackStopped += OnPlaybackStopped; - return Task.CompletedTask; - } - - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Dispose. - /// - /// Dispose all resources. - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _sessionManager.PlaybackProgress -= OnPlaybackProgress; - _sessionManager.PlaybackStopped -= OnPlaybackStopped; - } - } - - private static bool CanBeScrobbled(UserConfig config, PlaybackProgressEventArgs playbackProgress) - { - var position = playbackProgress.PlaybackPositionTicks; - var runtime = playbackProgress.MediaInfo.RunTimeTicks; - - if (runtime != null) - { - var percentageWatched = position / (float)runtime * 100f; - - // If percentage watched is below minimum, can't scrobble - if (percentageWatched < config.ScrobblePercentage) - { - return false; - } - } - - // If it's below minimum length, can't scrobble - if (runtime < 60 * 10000 * config.MinLength) - { - return false; - } - - return playbackProgress.MediaInfo.Type switch - { - nameof(Movie) => config.ScrobbleMovies, - nameof(Episode) => config.ScrobbleShows, - _ => false - }; - } - - private async void OnPlaybackProgress(object? sessions, PlaybackProgressEventArgs e) - { - if (DateTime.UtcNow < _nextTry) - { - return; - } - - _nextTry = DateTime.UtcNow.AddSeconds(30); - await ScrobbleSession(e); - } - - private async void OnPlaybackStopped(object? sessions, PlaybackStopEventArgs e) - { - await ScrobbleSession(e); - } - - private async Task ScrobbleSession(PlaybackProgressEventArgs eventArgs) - { - try - { - var userId = eventArgs.Session.UserId; - var userConfig = SimklPlugin.Instance?.Configuration.GetByGuid(userId); - if (userConfig == null || string.IsNullOrEmpty(userConfig.UserToken)) - { - _logger.LogError( - "Can't scrobble: User {UserName} not logged in ({UserConfigStatus})", - eventArgs.Session.UserName, - userConfig == null); - return; - } - - if (!CanBeScrobbled(userConfig, eventArgs)) - { - return; - } - - if (_lastScrobbled.ContainsKey(eventArgs.Session.Id) && _lastScrobbled[eventArgs.Session.Id] == eventArgs.MediaInfo.Id) - { - _logger.LogDebug("Already scrobbled {ItemName} for {UserName}", eventArgs.MediaInfo.Name, eventArgs.Session.UserName); - return; - } - - _logger.LogInformation( - "Trying to scrobble {Name} ({NowPlayingId}) for {UserName} ({UserId}) - {PlayingItemPath} on {SessionId}", - eventArgs.MediaInfo.Name, - eventArgs.MediaInfo.Id, - eventArgs.Session.UserName, - userId, - eventArgs.MediaInfo.Path, - eventArgs.Session.Id); - - var response = await _simklApi.MarkAsWatched(eventArgs.MediaInfo, userConfig.UserToken); - if (response.Success) - { - _logger.LogDebug("Scrobbled without errors"); - _lastScrobbled[eventArgs.Session.Id] = eventArgs.MediaInfo.Id; - } - } - catch (InvalidTokenException) - { - _logger.LogDebug("Deleted user token"); - } - catch (InvalidDataException ex) - { - _logger.LogError(ex, "Couldn't scrobble"); - _lastScrobbled[eventArgs.Session.Id] = eventArgs.MediaInfo.Id; - } - catch (Exception ex) - { - _logger.LogError(ex, "Caught unknown exception while trying to scrobble"); - } - } - } -} \ No newline at end of file diff --git a/Jellyfin.Plugin.Simkl/SimklPlugin.cs b/Jellyfin.Plugin.Simkl/SimklPlugin.cs deleted file mode 100644 index 4b3d0ad..0000000 --- a/Jellyfin.Plugin.Simkl/SimklPlugin.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; -using Jellyfin.Plugin.Simkl.Configuration; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Plugins; -using MediaBrowser.Model.Plugins; -using MediaBrowser.Model.Serialization; - -namespace Jellyfin.Plugin.Simkl -{ - /// - /// SIMKL tracker. - /// - public class SimklPlugin : BasePlugin, IHasWebPages - { - /// - /// Initializes a new instance of the class. - /// - /// Instance of the interface. - /// Instance of the interface. - public SimklPlugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer) - : base(applicationPaths, xmlSerializer) - { - Instance = this; - } - - /// - /// Gets the current instance of the plugin. - /// - public static SimklPlugin? Instance { get; private set; } - - /// - public override Guid Id => new Guid("07CAEF58-A94B-4211-A62C-F9774E04EBDB"); - - /// - public override string Name => "Simkl"; - - /// - public override string Description => "Scrobble your watched Movies, TV Shows and Anime to Simkl and share your progress with friends!"; - - /// - public IEnumerable GetPages() - { - yield return new PluginPageInfo - { - Name = Name, - EmbeddedResourcePath = GetType().Namespace + ".Configuration.configPage.html" - }; - } - } -} \ No newline at end of file diff --git a/build.yaml b/build.yaml index 63af47f..44d303c 100644 --- a/build.yaml +++ b/build.yaml @@ -1,15 +1,14 @@ --- -name: "Simkl" -guid: "07CAEF58-A94B-4211-A62C-F9774E04EBDB" +name: "Anilist Sync" +guid: "18c2a8ea-afa0-4a0b-aa94-072b492ab80b" version: "1.0.0.0" targetAbi: "10.7.0.0" -owner: "crobibero" -overview: "Scrobble to Simkl" +owner: "ARufenach" +overview: "Scrobble to Anilist" description: > - Scrobble your watched Movies, TV Shows and Anime to Simkl and share your progress with friends! + In development category: "General" framework: "net5.0" artifacts: -- "Jellyfin.Plugin.Simkl.dll" +- "Jellyfin.Plugin.AnilistSync.dll" changelog: > - Initial release