From f21cd89a74a4b3f4da4bab949fb0c05c38992081 Mon Sep 17 00:00:00 2001 From: Jamie Date: Fri, 24 Nov 2017 13:53:51 +0000 Subject: [PATCH] Added the SickRage API integration --- src/Ombi.Api.SickRage/ISickRageApi.cs | 17 +++ src/Ombi.Api.SickRage/Models/SickRageBase.cs | 10 ++ .../Models/SickRageEpisodeStatus.cs | 18 +++ src/Ombi.Api.SickRage/Models/SickRagePing.cs | 11 ++ .../Models/SickRageSeasonList.cs | 11 ++ src/Ombi.Api.SickRage/Models/SickRageShows.cs | 14 ++ .../Models/SickRageStatus.cs | 9 ++ src/Ombi.Api.SickRage/Models/SickRageTvAdd.cs | 11 ++ .../Ombi.Api.SickRage.csproj | 11 ++ src/Ombi.Api.SickRage/SickRageApi.cs | 143 ++++++++++++++++++ src/Ombi.DependencyInjection/IocExtensions.cs | 2 + .../Ombi.DependencyInjection.csproj | 1 + src/Ombi.Helpers/JsonConvertHelper.cs | 27 ++++ src/Ombi.sln | 13 +- 14 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 src/Ombi.Api.SickRage/ISickRageApi.cs create mode 100644 src/Ombi.Api.SickRage/Models/SickRageBase.cs create mode 100644 src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs create mode 100644 src/Ombi.Api.SickRage/Models/SickRagePing.cs create mode 100644 src/Ombi.Api.SickRage/Models/SickRageSeasonList.cs create mode 100644 src/Ombi.Api.SickRage/Models/SickRageShows.cs create mode 100644 src/Ombi.Api.SickRage/Models/SickRageStatus.cs create mode 100644 src/Ombi.Api.SickRage/Models/SickRageTvAdd.cs create mode 100644 src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj create mode 100644 src/Ombi.Api.SickRage/SickRageApi.cs create mode 100644 src/Ombi.Helpers/JsonConvertHelper.cs diff --git a/src/Ombi.Api.SickRage/ISickRageApi.cs b/src/Ombi.Api.SickRage/ISickRageApi.cs new file mode 100644 index 000000000..55bd0222a --- /dev/null +++ b/src/Ombi.Api.SickRage/ISickRageApi.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; +using Ombi.Api.SickRage.Models; + +namespace Ombi.Api.SickRage +{ + public interface ISickRageApi + { + Task AddSeason(int tvdbId, int season, string apiKey, string baseUrl); + Task AddSeries(int tvdbId, int seasonCount, int[] seasons, string quality, string apiKey, string baseUrl); + Task GetShows(string apiKey, string baseUrl); + Task Ping(string apiKey, string baseUrl); + Task VerifyShowHasLoaded(int tvdbId, string apiKey, string baseUrl); + + Task SetEpisodeStatus(string apiKey, string baseUrl, int tvdbid, string status, + int season, int episode = -1); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Models/SickRageBase.cs b/src/Ombi.Api.SickRage/Models/SickRageBase.cs new file mode 100644 index 000000000..6ec1254b2 --- /dev/null +++ b/src/Ombi.Api.SickRage/Models/SickRageBase.cs @@ -0,0 +1,10 @@ +namespace Ombi.Api.SickRage.Models +{ + public abstract class SickRageBase + { + public T data { get; set; } + public string message { get; set; } + public string result { get; set; } + } +} +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs b/src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs new file mode 100644 index 000000000..6fa76adb2 --- /dev/null +++ b/src/Ombi.Api.SickRage/Models/SickRageEpisodeStatus.cs @@ -0,0 +1,18 @@ +namespace Ombi.Api.SickRage.Models +{ + public class SickRageEpisodeStatus + { + public Data[] data { get; set; } + public string message { get; set; } + public string result { get; set; } + } + + public class Data + { + public int episode { get; set; } + public string message { get; set; } + public string result { get; set; } + public int season { get; set; } + public string status { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Models/SickRagePing.cs b/src/Ombi.Api.SickRage/Models/SickRagePing.cs new file mode 100644 index 000000000..f43e08af0 --- /dev/null +++ b/src/Ombi.Api.SickRage/Models/SickRagePing.cs @@ -0,0 +1,11 @@ +namespace Ombi.Api.SickRage.Models +{ + public class SickRagePingData + { + public int pid { get; set; } + } + + public class SickRagePing : SickRageBase + { + } +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Models/SickRageSeasonList.cs b/src/Ombi.Api.SickRage/Models/SickRageSeasonList.cs new file mode 100644 index 000000000..a00ceb5cf --- /dev/null +++ b/src/Ombi.Api.SickRage/Models/SickRageSeasonList.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; +using Ombi.Helpers; + +namespace Ombi.Api.SickRage.Models +{ + public class SickRageSeasonList : SickRageBase + { + [JsonIgnore] + public int[] Data => JsonConvertHelper.ParseObjectToArray(data); + } +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Models/SickRageShows.cs b/src/Ombi.Api.SickRage/Models/SickRageShows.cs new file mode 100644 index 000000000..8616c2d1a --- /dev/null +++ b/src/Ombi.Api.SickRage/Models/SickRageShows.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace Ombi.Api.SickRage.Models +{ + public class SickRageShows : SickRageBase> + { + + } + + public class Item + { + public int tvdbid { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Models/SickRageStatus.cs b/src/Ombi.Api.SickRage/Models/SickRageStatus.cs new file mode 100644 index 000000000..fcfd53310 --- /dev/null +++ b/src/Ombi.Api.SickRage/Models/SickRageStatus.cs @@ -0,0 +1,9 @@ +namespace Ombi.Api.SickRage.Models +{ + public static class SickRageStatus + { + public const string Wanted = "wanted"; + public const string Skipped = "skipped"; + public const string Ignored = "Ignored"; + } +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Models/SickRageTvAdd.cs b/src/Ombi.Api.SickRage/Models/SickRageTvAdd.cs new file mode 100644 index 000000000..800034435 --- /dev/null +++ b/src/Ombi.Api.SickRage/Models/SickRageTvAdd.cs @@ -0,0 +1,11 @@ +namespace Ombi.Api.SickRage.Models +{ + public class SickRageTvAddData + { + public string name { get; set; } + } + + public class SickRageTvAdd : SickRageBase + { + } +} \ No newline at end of file diff --git a/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj b/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj new file mode 100644 index 000000000..a3651df3c --- /dev/null +++ b/src/Ombi.Api.SickRage/Ombi.Api.SickRage.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/src/Ombi.Api.SickRage/SickRageApi.cs b/src/Ombi.Api.SickRage/SickRageApi.cs new file mode 100644 index 000000000..e178479b8 --- /dev/null +++ b/src/Ombi.Api.SickRage/SickRageApi.cs @@ -0,0 +1,143 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Ombi.Api.SickRage.Models; + +namespace Ombi.Api.SickRage +{ + public class SickRageApi : ISickRageApi + { + public SickRageApi(IApi api, ILogger log) + { + _api = api; + _log = log; + } + + private readonly IApi _api; + private readonly ILogger _log; + + public async Task VerifyShowHasLoaded(int tvdbId, string apiKey, string baseUrl) + { + var request = new Request($"/api/{apiKey}/?cmd=show.seasonlist", baseUrl, HttpMethod.Get); + request.AddQueryString("tvdbid", tvdbId.ToString()); + + return await _api.Request(request); + } + + public async Task AddSeries(int tvdbId, int seasonCount, int[] seasons, string quality, string apiKey, string baseUrl) + { + var futureStatus = seasons.Length > 0 && seasons.All(x => x != seasonCount) ? SickRageStatus.Skipped : SickRageStatus.Wanted; + var status = seasons.Length > 0 ? SickRageStatus.Skipped : SickRageStatus.Wanted; + var request = new Request($"/api/{apiKey}/?cmd=show.addnew", baseUrl, HttpMethod.Get); + + request.AddQueryString("tvdbid", tvdbId.ToString()); + request.AddQueryString("status", status); + request.AddQueryString("future_status", futureStatus); + + if (!quality.Equals("default", StringComparison.CurrentCultureIgnoreCase)) + { + request.AddQueryString("initial", quality); + } + + var obj = await _api.Request(request); + + if (obj.result != "failure") + { + var sw = new Stopwatch(); + sw.Start(); + + var seasonIncrement = 0; + try + { + while (seasonIncrement < seasonCount) + { + var seasonList = await VerifyShowHasLoaded(tvdbId, apiKey, baseUrl); + if (seasonList.result.Equals("failure")) + { + await Task.Delay(3000); + continue; + } + seasonIncrement = seasonList.Data?.Length ?? 0; + + if (sw.ElapsedMilliseconds > 30000) // Break out after 30 seconds, it's not going to get added + { + _log.LogWarning("Couldn't find out if the show had been added after 10 seconds. I doubt we can change the status to wanted."); + break; + } + } + sw.Stop(); + } + catch (Exception e) + { + _log.LogCritical(e, "Exception thrown when getting the seasonList"); + } + } + + try + { + if (seasons.Length > 0) + { + //handle the seasons requested + foreach (var s in seasons) + { + var result = await AddSeason(tvdbId, s, apiKey, baseUrl); + } + } + } + catch (Exception e) + { + _log.LogCritical(e, "Exception when adding seasons:"); + throw; + } + + return obj; + } + + public async Task AddSeason(int tvdbId, int season, string apiKey, string baseUrl) + { + var request = new Request($"/api/{apiKey}/?cmd=episode.setstatus", baseUrl, HttpMethod.Get); + request.AddQueryString("tvdbid", tvdbId.ToString()); + request.AddQueryString("season", season.ToString()); + request.AddQueryString("status", SickRageStatus.Wanted); + + return await _api.Request(request); + } + + public async Task GetShows(string apiKey, string baseUrl) + { + var request = new Request($"/api/{apiKey}/?cmd=shows", baseUrl, HttpMethod.Get); + + return await _api.Request(request); + } + + public async Task Ping(string apiKey, string baseUrl) + { + var request = new Request($"/api/{apiKey}/?cmd=sb.ping", baseUrl, HttpMethod.Get); + + return await _api.Request(request); + } + + /// + /// Sets the epsiode status e.g. wanted + /// The episode number is optional, if not supplied it will set the whole season as the status passed in + /// + /// + public async Task SetEpisodeStatus(string apiKey, string baseUrl, int tvdbid, string status, int season, int episode = -1) + { + var request = new Request($"/api/{apiKey}/?cmd=episode.setstatus", baseUrl, HttpMethod.Get); + request.AddQueryString("tvdbid", tvdbid.ToString()); + request.AddQueryString("status", status); + request.AddQueryString("season", season.ToString()); + + if (episode != -1) + { + request.AddQueryString("episode", episode.ToString()); + } + + return await _api.Request(request); + } + } +} diff --git a/src/Ombi.DependencyInjection/IocExtensions.cs b/src/Ombi.DependencyInjection/IocExtensions.cs index 4ca7722b3..20a94f659 100644 --- a/src/Ombi.DependencyInjection/IocExtensions.cs +++ b/src/Ombi.DependencyInjection/IocExtensions.cs @@ -36,6 +36,7 @@ using Ombi.Api.Mattermost; using Ombi.Api.Pushbullet; using Ombi.Api.Pushover; using Ombi.Api.Service; +using Ombi.Api.SickRage; using Ombi.Api.Slack; using Ombi.Core.Rule.Interfaces; using Ombi.Core.Senders; @@ -102,6 +103,7 @@ namespace Ombi.DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddTransient(); } public static void RegisterStore(this IServiceCollection services) { diff --git a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj index e9e99f535..8a05580a6 100644 --- a/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj +++ b/src/Ombi.DependencyInjection/Ombi.DependencyInjection.csproj @@ -27,6 +27,7 @@ + diff --git a/src/Ombi.Helpers/JsonConvertHelper.cs b/src/Ombi.Helpers/JsonConvertHelper.cs new file mode 100644 index 000000000..8a79fce3f --- /dev/null +++ b/src/Ombi.Helpers/JsonConvertHelper.cs @@ -0,0 +1,27 @@ +using Newtonsoft.Json; + +namespace Ombi.Helpers +{ + public static class JsonConvertHelper + { + public static T[] ParseObjectToArray(object ambiguousObject) + { + var json = ambiguousObject.ToString(); + if (string.IsNullOrWhiteSpace(json)) + { + return new T[0]; // Could return null here instead. + } + if (json.TrimStart().StartsWith("[")) + { + return JsonConvert.DeserializeObject(json); + } + if (json.TrimStart().Equals("{}")) + { + return new T[0]; + } + + return new T[1] { JsonConvert.DeserializeObject(json) }; + + } + } +} \ No newline at end of file diff --git a/src/Ombi.sln b/src/Ombi.sln index d233b727b..494bb6329 100644 --- a/src/Ombi.sln +++ b/src/Ombi.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.15 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi", "Ombi\Ombi.csproj", "{C987AA67-AFE1-468F-ACD3-EAD5A48E1F6A}" EndProject @@ -86,9 +86,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.DogNzb", "Ombi.Api EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Tests", "Ombi.Tests\Ombi.Tests.csproj", "{C12F5276-352A-43CF-8E33-400E768E9757}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Telegram", "Ombi.Api.Telegram\Ombi.Api.Telegram.csproj", "{CB9DD209-8E09-4E01-983E-C77C89592D36}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Telegram", "Ombi.Api.Telegram\Ombi.Api.Telegram.csproj", "{CB9DD209-8E09-4E01-983E-C77C89592D36}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.Github", "Ombi.Api.Github\Ombi.Api.Github.csproj", "{55866DEE-46D1-4AF7-B1A2-62F6190C8EC7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ombi.Api.Github", "Ombi.Api.Github\Ombi.Api.Github.csproj", "{55866DEE-46D1-4AF7-B1A2-62F6190C8EC7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ombi.Api.SickRage", "Ombi.Api.SickRage\Ombi.Api.SickRage.csproj", "{94C9A366-2595-45EA-AABB-8E4A2E90EC5B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -232,6 +234,10 @@ Global {55866DEE-46D1-4AF7-B1A2-62F6190C8EC7}.Debug|Any CPU.Build.0 = Debug|Any CPU {55866DEE-46D1-4AF7-B1A2-62F6190C8EC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {55866DEE-46D1-4AF7-B1A2-62F6190C8EC7}.Release|Any CPU.Build.0 = Release|Any CPU + {94C9A366-2595-45EA-AABB-8E4A2E90EC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {94C9A366-2595-45EA-AABB-8E4A2E90EC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {94C9A366-2595-45EA-AABB-8E4A2E90EC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {94C9A366-2595-45EA-AABB-8E4A2E90EC5B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -265,6 +271,7 @@ Global {C12F5276-352A-43CF-8E33-400E768E9757} = {6F42AB98-9196-44C4-B888-D5E409F415A1} {CB9DD209-8E09-4E01-983E-C77C89592D36} = {9293CA11-360A-4C20-A674-B9E794431BF5} {55866DEE-46D1-4AF7-B1A2-62F6190C8EC7} = {9293CA11-360A-4C20-A674-B9E794431BF5} + {94C9A366-2595-45EA-AABB-8E4A2E90EC5B} = {9293CA11-360A-4C20-A674-B9E794431BF5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {192E9BF8-00B4-45E4-BCCC-4C215725C869}