diff --git a/PlexRequests.Api/PlexApi.cs b/PlexRequests.Api/PlexApi.cs index 1084cc40b..19c8b59b9 100644 --- a/PlexRequests.Api/PlexApi.cs +++ b/PlexRequests.Api/PlexApi.cs @@ -34,7 +34,6 @@ using NLog; using PlexRequests.Api.Interfaces; using PlexRequests.Api.Models.Plex; using PlexRequests.Helpers; -using PlexRequests.Helpers.Exceptions; using RestSharp; @@ -81,7 +80,7 @@ namespace PlexRequests.Api request.AddJsonBody(userModel); var obj = RetryHandler.Execute(() => Api.Execute (request, new Uri(SignInUri)), - (exception, timespan) => Log.Error (exception, "Exception when calling SignIn for Plex, Retrying {0}", timespan), null); + (exception, timespan) => Log.Error (exception, "Exception when calling SignIn for Plex, Retrying {0}", timespan)); return obj; } diff --git a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs index a39a570c2..4feefc3cc 100644 --- a/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs +++ b/PlexRequests.Core/SettingModels/ScheduledJobsSettings.cs @@ -37,7 +37,7 @@ namespace PlexRequests.Core.SettingModels StoreBackup = 24; StoreCleanup = 24; UserRequestLimitResetter = 12; - PlexEpisodeCacher = 20; + PlexEpisodeCacher = 2; } public int PlexAvailabilityChecker { get; set; } diff --git a/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj b/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj index a1eb95671..4b4627c34 100644 --- a/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj +++ b/PlexRequests.Helpers.Tests/PlexRequests.Helpers.Tests.csproj @@ -79,6 +79,8 @@ + + @@ -86,10 +88,18 @@ + + {DD7DC444-D3BF-4027-8AB9-EFC71F5EC581} + PlexRequests.Core + {1252336d-42a3-482a-804c-836e60173dfa} PlexRequests.Helpers + + {92433867-2B7B-477B-A566-96C382427525} + PlexRequests.Store + diff --git a/PlexRequests.UI.Tests/StringHelperTests.cs b/PlexRequests.Helpers.Tests/StringHelperTests.cs similarity index 71% rename from PlexRequests.UI.Tests/StringHelperTests.cs rename to PlexRequests.Helpers.Tests/StringHelperTests.cs index 546c8ee3c..48fcbb534 100644 --- a/PlexRequests.UI.Tests/StringHelperTests.cs +++ b/PlexRequests.Helpers.Tests/StringHelperTests.cs @@ -29,9 +29,8 @@ using System.Collections.Generic; using NUnit.Framework; using PlexRequests.Core.Models; -using PlexRequests.UI.Helpers; -namespace PlexRequests.UI.Tests +namespace PlexRequests.Helpers.Tests { [TestFixture] public class StringHelperTests @@ -48,6 +47,12 @@ namespace PlexRequests.UI.Tests return input.ToCamelCaseWords(); } + [TestCaseSource(nameof(PrefixData))] + public string AddPrefix(string[] input, string prefix, string separator) + { + return input.AddPrefix(prefix, separator); + } + private static IEnumerable StringData { get @@ -71,5 +76,19 @@ namespace PlexRequests.UI.Tests yield return new TestCaseData(IssueStatus.ResolvedIssue.ToString()).Returns("Resolved Issue").SetName("enum resolved"); } } + + private static IEnumerable PrefixData + { + get + { + yield return new TestCaseData(new[] {"abc","def","ghi"}, "@", ",").Returns("@abc,@def,@ghi").SetName("Happy Path"); + yield return new TestCaseData(new[] {"abc","def","ghi"}, "!!", "").Returns("!!abc!!def!!ghi").SetName("Different Separator Path"); + yield return new TestCaseData(new[] {"abc"}, "", "").Returns("abc").SetName("Single Item"); + yield return new TestCaseData(new string[0], "", "").Returns(string.Empty).SetName("Empty Array"); + yield return new TestCaseData(new [] {"abc","aaaa"}, null, ",").Returns("abc,aaaa").SetName("Null prefix"); + yield return new TestCaseData(new [] {"abc","aaaa"}, "@", null).Returns("@abc@aaaa").SetName("Null separator test"); + yield return new TestCaseData(new [] {"abc","aaaa"}, null, null).Returns("abcaaaa").SetName("Null separator and prefix"); + } + } } } \ No newline at end of file diff --git a/PlexRequests.Helpers.Tests/TypeHelperTests.cs b/PlexRequests.Helpers.Tests/TypeHelperTests.cs new file mode 100644 index 000000000..295b955ad --- /dev/null +++ b/PlexRequests.Helpers.Tests/TypeHelperTests.cs @@ -0,0 +1,73 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: StringHelperTests.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System; +using System.Collections.Generic; + +using NUnit.Framework; + +using PlexRequests.Store; + +namespace PlexRequests.Helpers.Tests +{ + [TestFixture] + public class TypeHelperTests + { + [TestCaseSource(nameof(TypeData))] + public string[] FirstCharToUpperTest(Type input) + { + return input.GetPropertyNames(); + } + + + private static IEnumerable TypeData + { + get + { + yield return new TestCaseData(typeof(TestClass1)).Returns(new[] { "Test1", "Test2", "Test3" }).SetName("Simple Class"); + yield return new TestCaseData(typeof(int)).Returns(new string[0]).SetName("NoPropeties Class"); + yield return new TestCaseData(typeof(IEnumerable<>)).Returns(new string[0]).SetName("Interface"); + yield return new TestCaseData(typeof(string)).Returns(new[] { "Chars", "Length" }).SetName("String"); + yield return new TestCaseData(typeof(RequestedModel)).Returns( + new[] + { + "ProviderId", "ImdbId", "TvDbId", "Overview", "Title", "PosterPath", "ReleaseDate", "Type", + "Status", "Approved", "RequestedBy", "RequestedDate", "Available", "Issues", "OtherMessage", "AdminNote", + "SeasonList", "SeasonCount", "SeasonsRequested", "MusicBrainzId", "RequestedUsers","ArtistName", + "ArtistId","IssueId","Episodes","AllUsers","CanApprove","Id" + }).SetName("Requested Model"); + } + } + + + private sealed class TestClass1 + { + public string Test1 { get; set; } + public int Test2 { get; set; } + public long[] Test3 { get; set; } + } + } +} \ No newline at end of file diff --git a/PlexRequests.Helpers/PlexRequests.Helpers.csproj b/PlexRequests.Helpers/PlexRequests.Helpers.csproj index 084310c77..91bf2e709 100644 --- a/PlexRequests.Helpers/PlexRequests.Helpers.csproj +++ b/PlexRequests.Helpers/PlexRequests.Helpers.csproj @@ -82,6 +82,8 @@ + + diff --git a/PlexRequests.UI/Helpers/StringHelper.cs b/PlexRequests.Helpers/StringHelper.cs similarity index 77% rename from PlexRequests.UI/Helpers/StringHelper.cs rename to PlexRequests.Helpers/StringHelper.cs index b095bc62e..10ec8c1c2 100644 --- a/PlexRequests.UI/Helpers/StringHelper.cs +++ b/PlexRequests.Helpers/StringHelper.cs @@ -25,9 +25,10 @@ // ************************************************************************/ #endregion using System.Linq; +using System.Text; using System.Text.RegularExpressions; -namespace PlexRequests.UI.Helpers +namespace PlexRequests.Helpers { public static class StringHelper { @@ -46,5 +47,22 @@ namespace PlexRequests.UI.Helpers return input; return Regex.Replace(input.FirstCharToUpper(), "([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", "$1 "); } + + public static string AddPrefix(this string[] values, string prefix, string separator) + { + var sb = new StringBuilder(); + var len = values.Length; + for (var i = 0; i < len; i++) + { + sb.Append(prefix).Append(values[i]); + + // If it's not the last item in the collection, then add a separator + if (i < len - 1) + { + sb.Append(separator); + } + } + return sb.ToString(); + } } } \ No newline at end of file diff --git a/PlexRequests.Helpers/TypeHelper.cs b/PlexRequests.Helpers/TypeHelper.cs new file mode 100644 index 000000000..27a108bef --- /dev/null +++ b/PlexRequests.Helpers/TypeHelper.cs @@ -0,0 +1,39 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2016 Jamie Rees +// File: TypeHelper.cs +// Created By: Jamie Rees +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// ************************************************************************/ +#endregion +using System; +using System.Linq; + +namespace PlexRequests.Helpers +{ + public static class TypeHelper + { + public static string[] GetPropertyNames(this Type t) + { + return t.GetProperties().Select(x => x.Name).ToArray(); + } + } +} \ No newline at end of file diff --git a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs index da1e5935a..abe90364c 100644 --- a/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs +++ b/PlexRequests.Services.Tests/PlexAvailabilityCheckerTests.cs @@ -53,6 +53,7 @@ namespace PlexRequests.Services.Tests private Mock NotificationMock { get; set; } private Mock JobRec { get; set; } private Mock> NotifyUsers { get; set; } + private Mock> PlexEpisodes { get; set; } [SetUp] public void Setup() @@ -64,8 +65,9 @@ namespace PlexRequests.Services.Tests NotificationMock = new Mock(); CacheMock = new Mock(); NotifyUsers = new Mock>(); + PlexEpisodes = new Mock>(); JobRec = new Mock(); - Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object); + Checker = new PlexAvailabilityChecker(SettingsMock.Object, RequestMock.Object, PlexMock.Object, CacheMock.Object, NotificationMock.Object, JobRec.Object, NotifyUsers.Object, PlexEpisodes.Object); } diff --git a/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs b/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs index fd0434e00..cbab32602 100644 --- a/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs +++ b/PlexRequests.Services/Interfaces/IAvailabilityChecker.cs @@ -26,6 +26,9 @@ #endregion using PlexRequests.Services.Models; using System.Collections.Generic; +using System.Threading.Tasks; + +using PlexRequests.Store.Models; namespace PlexRequests.Services.Interfaces { @@ -43,12 +46,12 @@ namespace PlexRequests.Services.Interfaces /// Gets the episode's stored in the cache. /// /// - HashSet GetEpisodeCache(); + Task> GetEpisodes(); /// /// Gets the episode's stored in the cache and then filters on the TheTvDBId. /// /// The tv database identifier. /// - IEnumerable GetEpisodeCache(int theTvDbId); + Task> GetEpisodes(int theTvDbId); } } \ No newline at end of file diff --git a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs index 569babb4e..270ae7d0c 100644 --- a/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs +++ b/PlexRequests.Services/Jobs/PlexAvailabilityChecker.cs @@ -29,6 +29,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Dapper; + using NLog; using PlexRequests.Api.Interfaces; @@ -53,7 +55,7 @@ namespace PlexRequests.Services.Jobs public class PlexAvailabilityChecker : IJob, IAvailabilityChecker { public PlexAvailabilityChecker(ISettingsService plexSettings, IRequestService request, IPlexApi plex, ICacheProvider cache, - INotificationService notify, IJobRecord rec, IRepository users) + INotificationService notify, IJobRecord rec, IRepository users, IRepository repo) { Plex = plexSettings; RequestService = request; @@ -62,9 +64,11 @@ namespace PlexRequests.Services.Jobs Notification = notify; Job = rec; UserNotifyRepo = users; + EpisodeRepo = repo; } private ISettingsService Plex { get; } + private IRepository EpisodeRepo { get; } private IRequestService RequestService { get; } private static Logger Log = LogManager.GetCurrentClassLogger(); private IPlexApi PlexApi { get; } @@ -241,15 +245,23 @@ namespace PlexRequests.Services.Jobs public bool IsEpisodeAvailable(string theTvDbId, int season, int episode) { - var episodes = Cache.Get>(CacheKeys.PlexEpisodes); - if (episodes == null) + var ep = EpisodeRepo.Custom( + connection => + { + connection.Open(); + var result = connection.Query("select * from PlexEpisodes where ProviderId = @ProviderId", new { ProviderId = theTvDbId}); + + return result; + }).ToList(); + + if (!ep.Any()) { Log.Info("Episode cache info is not available. tvdbid: {0}, season: {1}, episode: {2}",theTvDbId, season, episode); return false; } - foreach (var result in episodes) + foreach (var result in ep) { - if (result.Episodes.ProviderId.Equals(theTvDbId) && result.Episodes.EpisodeNumber == episode && result.Episodes.SeasonNumber == season) + if (result.ProviderId.Equals(theTvDbId) && result.EpisodeNumber == episode && result.SeasonNumber == season) { return true; } @@ -258,35 +270,43 @@ namespace PlexRequests.Services.Jobs } /// - /// Gets the episode's stored in the cache. + /// Gets the episode's db in the cache. /// /// - public HashSet GetEpisodeCache() + public async Task> GetEpisodes() { - var episodes = Cache.Get>(CacheKeys.PlexEpisodes); + var episodes = await EpisodeRepo.GetAllAsync(); if (episodes == null) { Log.Info("Episode cache info is not available."); - return new HashSet(); + return new HashSet(); } return episodes; } /// - /// Gets the episode's stored in the cache and then filters on the TheTvDBId. + /// Gets the episode's stored in the db and then filters on the TheTvDBId. /// /// The tv database identifier. /// - public IEnumerable GetEpisodeCache(int theTvDbId) + public async Task> GetEpisodes(int theTvDbId) { - var episodes = Cache.Get>(CacheKeys.PlexEpisodes); - if (episodes == null) + var ep = await EpisodeRepo.CustomAsync(async connection => + { + connection.Open(); + var result = await connection.QueryAsync("select * from PlexEpisodes where ProviderId = @ProviderId", new { ProviderId = theTvDbId }); + + return result; + }); + + var plexEpisodeses = ep as PlexEpisodes[] ?? ep.ToArray(); + if (!plexEpisodeses.Any()) { - Log.Info("Episode cache info is not available."); - return new List(); + Log.Info("Episode db info is not available."); + return new List(); } - return episodes.Where(x => x.Episodes.ProviderId == theTvDbId.ToString()); + return plexEpisodeses; } public List GetPlexAlbums() diff --git a/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs b/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs index f3a52579a..847fb2a5e 100644 --- a/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs +++ b/PlexRequests.Services/Jobs/PlexEpisodeCacher.cs @@ -37,6 +37,8 @@ using PlexRequests.Core.SettingModels; using PlexRequests.Helpers; using PlexRequests.Services.Interfaces; using PlexRequests.Services.Models; +using PlexRequests.Store.Models; +using PlexRequests.Store.Repository; using Quartz; @@ -45,12 +47,13 @@ namespace PlexRequests.Services.Jobs public class PlexEpisodeCacher : IJob { public PlexEpisodeCacher(ISettingsService plexSettings, IPlexApi plex, ICacheProvider cache, - IJobRecord rec) + IJobRecord rec, IRepository repo) { Plex = plexSettings; PlexApi = plex; Cache = cache; Job = rec; + Repo = repo; } private ISettingsService Plex { get; } @@ -58,19 +61,23 @@ namespace PlexRequests.Services.Jobs private IPlexApi PlexApi { get; } private ICacheProvider Cache { get; } private IJobRecord Job { get; } + private IRepository Repo { get; } private const int ResultCount = 25; private const string PlexType = "episode"; + private const string TableName = "PlexEpisodes"; public void CacheEpisodes() { var videoHashset = new HashSet