Started on the queue for requests #483 TV Requests with missing information has been completed

pull/687/head
Jamie.Rees 8 years ago
parent 5a5f7f5610
commit 50dec5f530

@ -137,6 +137,8 @@
<Compile Include="StatusChecker\AppveyorArtifactResult.cs" /> <Compile Include="StatusChecker\AppveyorArtifactResult.cs" />
<Compile Include="StatusChecker\StatusChecker.cs" /> <Compile Include="StatusChecker\StatusChecker.cs" />
<Compile Include="StatusChecker\AppveyorBranchResult.cs" /> <Compile Include="StatusChecker\AppveyorBranchResult.cs" />
<Compile Include="TvSender.cs" />
<Compile Include="TvSenderOld.cs" />
<Compile Include="UserIdentity.cs" /> <Compile Include="UserIdentity.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UserMapper.cs" /> <Compile Include="UserMapper.cs" />

@ -11,7 +11,7 @@ namespace PlexRequests.Core.Queue
Task DequeueAsync(); Task DequeueAsync();
IEnumerable<RequestQueue> GetQueue(); IEnumerable<RequestQueue> GetQueue();
Task<IEnumerable<RequestQueue>> GetQueueAsync(); Task<IEnumerable<RequestQueue>> GetQueueAsync();
void QueueItem(RequestedModel request, RequestType type, FaultType faultType); void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType);
Task QueueItemAsync(RequestedModel request, RequestType type, FaultType faultType); Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType);
} }
} }

@ -46,14 +46,14 @@ namespace PlexRequests.Core.Queue
private IRepository<RequestQueue> RequestQueue { get; } private IRepository<RequestQueue> RequestQueue { get; }
public void QueueItem(RequestedModel request, RequestType type, FaultType faultType) public void QueueItem(RequestedModel request, string id, RequestType type, FaultType faultType)
{ {
//Ensure there is not a duplicate queued item //Ensure there is not a duplicate queued item
var existingItem = RequestQueue.Custom( var existingItem = RequestQueue.Custom(
connection => connection =>
{ {
connection.Open(); connection.Open();
var result = connection.Query<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = request.ProviderId }); var result = connection.Query<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id });
return result; return result;
}).FirstOrDefault(); }).FirstOrDefault();
@ -68,19 +68,19 @@ namespace PlexRequests.Core.Queue
{ {
Type = type, Type = type,
Content = ByteConverterHelper.ReturnBytes(request), Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = request.ProviderId, PrimaryIdentifier = id,
FaultType = faultType FaultType = faultType
}; };
RequestQueue.Insert(queue); RequestQueue.Insert(queue);
} }
public async Task QueueItemAsync(RequestedModel request, RequestType type, FaultType faultType) public async Task QueueItemAsync(RequestedModel request, string id, RequestType type, FaultType faultType)
{ {
//Ensure there is not a duplicate queued item //Ensure there is not a duplicate queued item
var existingItem = await RequestQueue.CustomAsync(async connection => var existingItem = await RequestQueue.CustomAsync(async connection =>
{ {
connection.Open(); connection.Open();
var result = await connection.QueryAsync<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = request.ProviderId }); var result = await connection.QueryAsync<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = id });
return result; return result;
}); });
@ -95,7 +95,7 @@ namespace PlexRequests.Core.Queue
{ {
Type = type, Type = type,
Content = ByteConverterHelper.ReturnBytes(request), Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = request.ProviderId, PrimaryIdentifier = id,
FaultType = faultType FaultType = faultType
}; };
await RequestQueue.InsertAsync(queue); await RequestQueue.InsertAsync(queue);

@ -42,5 +42,6 @@ namespace PlexRequests.Core.SettingModels
[Obsolete("We use the CRON job now")] [Obsolete("We use the CRON job now")]
public int RecentlyAdded { get; set; } public int RecentlyAdded { get; set; }
public string RecentlyAddedCron { get; set; } public string RecentlyAddedCron { get; set; }
public int FaultQueueHandler { get; set; }
} }
} }

@ -24,18 +24,19 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NLog; using NLog;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.SickRage; using PlexRequests.Api.Models.SickRage;
using PlexRequests.Api.Models.Sonarr; using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Store; using PlexRequests.Store;
using System.Linq;
using System.Threading.Tasks;
namespace PlexRequests.UI.Helpers namespace PlexRequests.Core
{ {
public class TvSender public class TvSender
{ {

@ -36,7 +36,7 @@ using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Store; using PlexRequests.Store;
namespace PlexRequests.UI.Helpers namespace PlexRequests.Core
{ {
public class TvSenderOld public class TvSenderOld
{ {

@ -0,0 +1,214 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: UserRequestLimitResetter.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 System.Linq;
using NLog;
using PlexRequests.Api;
using PlexRequests.Api.Interfaces;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels;
using PlexRequests.Helpers;
using PlexRequests.Services.Interfaces;
using PlexRequests.Store;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
using Quartz;
namespace PlexRequests.Services.Jobs
{
public class FaultQueueHandler : IJob
{
private static readonly Logger Log = LogManager.GetCurrentClassLogger();
public FaultQueueHandler(IJobRecord record, IRepository<RequestQueue> repo, ISonarrApi sonarrApi,
ISickRageApi srApi,
ISettingsService<SonarrSettings> sonarrSettings, ISettingsService<SickRageSettings> srSettings)
{
Record = record;
Repo = repo;
SonarrApi = sonarrApi;
SrApi = srApi;
SickrageSettings = srSettings;
SonarrSettings = sonarrSettings;
}
private IRepository<RequestQueue> Repo { get; }
private IJobRecord Record { get; }
private ISonarrApi SonarrApi { get; }
private ISickRageApi SrApi { get; }
private ISettingsService<SonarrSettings> SonarrSettings { get; set; }
private ISettingsService<SickRageSettings> SickrageSettings { get; set; }
public void Execute(IJobExecutionContext context)
{
try
{
var faultedRequests = Repo.GetAll().ToList();
var missingInfo = faultedRequests.Where(x => x.FaultType == FaultType.MissingInformation).ToList();
ProcessMissingInformation(missingInfo);
var transientErrors = faultedRequests.Where(x => x.FaultType == FaultType.RequestFault).ToList();
ProcessTransientErrors(transientErrors);
}
catch (Exception e)
{
Log.Error(e);
}
finally
{
Record.Record(JobNames.RequestLimitReset);
}
}
private void ProcessMissingInformation(List<RequestQueue> requests)
{
var sonarrSettings = SonarrSettings.GetSettings();
var sickrageSettings = SickrageSettings.GetSettings();
if (!requests.Any())
{
return;
}
var tv = requests.Where(x => x.Type == RequestType.TvShow);
// TV
var tvApi = new TvMazeApi();
foreach (var t in tv)
{
var providerId = int.Parse(t.PrimaryIdentifier);
var showInfo = tvApi.ShowLookupByTheTvDbId(providerId);
if (showInfo.externals?.thetvdb != null)
{
// We now have the info
var tvModel = ByteConverterHelper.ReturnObject<RequestedModel>(t.Content);
tvModel.ProviderId = showInfo.externals.thetvdb.Value;
var result = ProcessTvShow(tvModel, sonarrSettings, sickrageSettings);
if (!result)
{
// we now have the info but couldn't add it, so add it back into the queue but with a different fault
t.Content = ByteConverterHelper.ReturnBytes(tvModel);
t.FaultType = FaultType.RequestFault;
t.LastRetry = DateTime.UtcNow;
Repo.Update(t);
}
else
{
// Successful, remove from the fault queue
Repo.Delete(t);
}
}
}
}
private bool ProcessTvShow(RequestedModel tvModel, SonarrSettings sonarr, SickRageSettings sickrage)
{
try
{
var sender = new TvSenderOld(SonarrApi, SrApi);
if (sonarr.Enabled)
{
var task = sender.SendToSonarr(sonarr, tvModel, sonarr.QualityProfile);
var a = task.Result;
if (string.IsNullOrEmpty(a?.title))
{
// Couldn't send it
return false;
}
return true;
}
if (sickrage.Enabled)
{
var result = sender.SendToSickRage(sickrage, tvModel);
if (result?.result != "success")
{
// Couldn't send it
return false;
}
return true;
}
return false;
}
catch (Exception e)
{
Log.Error(e);
return false; // It fails so it will get added back into the queue
}
}
private void ProcessTransientErrors(List<RequestQueue> requests)
{
var sonarrSettings = SonarrSettings.GetSettings();
var sickrageSettings = SickrageSettings.GetSettings();
if (!requests.Any())
{
return;
}
var tv = requests.Where(x => x.Type == RequestType.TvShow);
var movie = requests.Where(x => x.Type == RequestType.Movie);
var album = requests.Where(x => x.Type == RequestType.Album);
foreach (var t in tv)
{
var tvModel = ByteConverterHelper.ReturnObject<RequestedModel>(t.Content);
var result = ProcessTvShow(tvModel, sonarrSettings, sickrageSettings);
if (!result)
{
// we now have the info but couldn't add it, so do nothing now.
t.LastRetry = DateTime.UtcNow;
Repo.Update(t);
}
else
{
// Successful, remove from the fault queue
Repo.Delete(t);
}
}
}
}
}

@ -93,6 +93,7 @@
<Compile Include="Jobs\SickRageCacher.cs" /> <Compile Include="Jobs\SickRageCacher.cs" />
<Compile Include="Jobs\SonarrCacher.cs" /> <Compile Include="Jobs\SonarrCacher.cs" />
<Compile Include="Jobs\Templates\RecentlyAddedTemplate.cs" /> <Compile Include="Jobs\Templates\RecentlyAddedTemplate.cs" />
<Compile Include="Jobs\FaultQueueHandler.cs" />
<Compile Include="Jobs\UserRequestLimitResetter.cs" /> <Compile Include="Jobs\UserRequestLimitResetter.cs" />
<Compile Include="Models\PlexAlbum.cs" /> <Compile Include="Models\PlexAlbum.cs" />
<Compile Include="Models\PlexEpisodeModel.cs" /> <Compile Include="Models\PlexEpisodeModel.cs" />

@ -25,6 +25,7 @@
// ************************************************************************/ // ************************************************************************/
#endregion #endregion
using System;
using Dapper.Contrib.Extensions; using Dapper.Contrib.Extensions;
namespace PlexRequests.Store.Models namespace PlexRequests.Store.Models
@ -32,13 +33,14 @@ namespace PlexRequests.Store.Models
[Table("RequestQueue")] [Table("RequestQueue")]
public class RequestQueue : Entity public class RequestQueue : Entity
{ {
public int PrimaryIdentifier { get; set; } public string PrimaryIdentifier { get; set; }
public RequestType Type { get; set; } public RequestType Type { get; set; }
public byte[] Content { get; set; } public byte[] Content { get; set; }
public FaultType FaultType { get; set; } public FaultType FaultType { get; set; }
public DateTime? LastRetry { get; set; }
} }
public enum FaultType public enum FaultType

@ -136,10 +136,11 @@ CREATE INDEX IF NOT EXISTS PlexEpisodes_ProviderId ON PlexEpisodes (ProviderId);
CREATE TABLE IF NOT EXISTS RequestQueue CREATE TABLE IF NOT EXISTS RequestQueue
( (
Id INTEGER PRIMARY KEY AUTOINCREMENT, Id INTEGER PRIMARY KEY AUTOINCREMENT,
PrimaryIdentifier INTEGER NOT NULL, PrimaryIdentifier VARCHAR(100) NOT NULL,
Type INTEGER NOT NULL, Type INTEGER NOT NULL,
FaultType INTEGER NOT NULL, FaultType INTEGER NOT NULL,
Content BLOB NOT NULL Content BLOB NOT NULL,
LastRetry VARCHAR(100)
); );
CREATE UNIQUE INDEX IF NOT EXISTS PlexUsers_Id ON PlexUsers (Id); CREATE UNIQUE INDEX IF NOT EXISTS PlexUsers_Id ON PlexUsers (Id);

@ -35,6 +35,7 @@ using NUnit.Framework;
using PlexRequests.Api.Interfaces; using PlexRequests.Api.Interfaces;
using PlexRequests.Api.Models.Sonarr; using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Core;
using PlexRequests.Core.SettingModels; using PlexRequests.Core.SettingModels;
using PlexRequests.Store; using PlexRequests.Store;
using PlexRequests.UI.Helpers; using PlexRequests.UI.Helpers;
@ -146,32 +147,6 @@ namespace PlexRequests.UI.Tests
true, It.IsAny<bool>()), Times.Once); true, It.IsAny<bool>()), Times.Once);
} }
[Test]
public async Task RequestEpisodesWithExistingSeriesTest()
{
var episodesReturned = new List<SonarrEpisodes>
{
new SonarrEpisodes {episodeNumber = 1, seasonNumber = 2, monitored = false, id=22}
};
SonarrMock.Setup(x => x.GetEpisodes(It.IsAny<string>(), It.IsAny<string>(),
It.IsAny<Uri>())).Returns(episodesReturned);
SonarrMock.Setup(x => x.GetEpisode("22", It.IsAny<string>(), It.IsAny<Uri>())).Returns(new SonarrEpisode {id=22});
Sender = new TvSender(SonarrMock.Object, SickrageMock.Object);
var model = new RequestedModel
{
Episodes = new List<EpisodesModel> { new EpisodesModel { EpisodeNumber = 1, SeasonNumber = 2 } }
};
var series = new Series();
await Sender.RequestEpisodesWithExistingSeries(model, series, GetSonarrSettings());
SonarrMock.Verify(x => x.UpdateEpisode(It.Is<SonarrEpisode>(e => e.monitored), It.IsAny<string>(), It.IsAny<Uri>()));
SonarrMock.Verify(x => x.GetEpisode("22", It.IsAny<string>(), It.IsAny<Uri>()),Times.Once);
SonarrMock.Verify(x => x.SearchForEpisodes(It.IsAny<int[]>(), It.IsAny<string>(), It.IsAny<Uri>()), Times.Once);
}
private SonarrSettings GetSonarrSettings() private SonarrSettings GetSonarrSettings()
{ {

@ -69,11 +69,10 @@ namespace PlexRequests.UI.Jobs
JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(), JobBuilder.Create<StoreBackup>().WithIdentity("StoreBackup", "Database").Build(),
JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(), JobBuilder.Create<StoreCleanup>().WithIdentity("StoreCleanup", "Database").Build(),
JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(), JobBuilder.Create<UserRequestLimitResetter>().WithIdentity("UserRequestLimiter", "Request").Build(),
JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAddedModel", "Email").Build() JobBuilder.Create<RecentlyAdded>().WithIdentity("RecentlyAddedModel", "Email").Build(),
JobBuilder.Create<FaultQueueHandler>().WithIdentity("FaultQueueHandler", "Fault").Build(),
}; };
jobs.AddRange(jobList); jobs.AddRange(jobList);
return jobs; return jobs;
@ -151,7 +150,10 @@ namespace PlexRequests.UI.Jobs
{ {
s.UserRequestLimitResetter = 12; s.UserRequestLimitResetter = 12;
} }
if (s.FaultQueueHandler == 0)
{
s.FaultQueueHandler = 6;
}
var triggers = new List<ITrigger>(); var triggers = new List<ITrigger>();
@ -221,6 +223,13 @@ namespace PlexRequests.UI.Jobs
.WithSimpleSchedule(x => x.WithIntervalInHours(2).RepeatForever()) .WithSimpleSchedule(x => x.WithIntervalInHours(2).RepeatForever())
.Build(); .Build();
var fault =
TriggerBuilder.Create()
.WithIdentity("FaultQueueHandler", "Fault")
.StartAt(DateBuilder.FutureDate(10, IntervalUnit.Minute))
.WithSimpleSchedule(x => x.WithIntervalInHours(s.FaultQueueHandler).RepeatForever())
.Build();
triggers.Add(rencentlyAdded); triggers.Add(rencentlyAdded);
triggers.Add(plexAvailabilityChecker); triggers.Add(plexAvailabilityChecker);
triggers.Add(srCacher); triggers.Add(srCacher);
@ -230,6 +239,7 @@ namespace PlexRequests.UI.Jobs
triggers.Add(storeCleanup); triggers.Add(storeCleanup);
triggers.Add(userRequestLimiter); triggers.Add(userRequestLimiter);
triggers.Add(plexEpCacher); triggers.Add(plexEpCacher);
triggers.Add(fault);
return triggers; return triggers;
} }

@ -589,7 +589,7 @@ namespace PlexRequests.UI.Modules
catch (Exception e) catch (Exception e)
{ {
Log.Fatal(e); Log.Fatal(e);
await FaultQueue.QueueItemAsync(model, RequestType.Movie, FaultType.RequestFault); await FaultQueue.QueueItemAsync(model, movieInfo.Id.ToString(), RequestType.Movie, FaultType.RequestFault);
await NotificationService.Publish(new NotificationModel await NotificationService.Publish(new NotificationModel
{ {
@ -690,26 +690,6 @@ namespace PlexRequests.UI.Modules
TvDbId = showId.ToString() TvDbId = showId.ToString()
}; };
if (showInfo.externals?.thetvdb == null)
{
await FaultQueue.QueueItemAsync(model, RequestType.TvShow, FaultType.MissingInformation);
await NotificationService.Publish(new NotificationModel
{
DateTime = DateTime.Now,
User = Username,
RequestType = RequestType.TvShow,
Title = model.Title,
NotificationType = NotificationType.ItemAddedToFaultQueue
});
return Response.AsJson(new JsonResponseModel
{
Result = true,
Message = $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"
});
}
model.ProviderId = showInfo.externals?.thetvdb ?? 0;
var seasonsList = new List<int>(); var seasonsList = new List<int>();
switch (seasons) switch (seasons)
{ {
@ -876,6 +856,26 @@ namespace PlexRequests.UI.Modules
}); });
} }
if (showInfo.externals?.thetvdb == null)
{
await FaultQueue.QueueItemAsync(model, showInfo.id.ToString(), RequestType.TvShow, FaultType.MissingInformation);
await NotificationService.Publish(new NotificationModel
{
DateTime = DateTime.Now,
User = Username,
RequestType = RequestType.TvShow,
Title = model.Title,
NotificationType = NotificationType.ItemAddedToFaultQueue
});
return Response.AsJson(new JsonResponseModel
{
Result = true,
Message = $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"
});
}
model.ProviderId = showInfo.externals?.thetvdb ?? 0;
try try
{ {
if (ShouldAutoApprove(RequestType.TvShow, settings)) if (ShouldAutoApprove(RequestType.TvShow, settings))
@ -936,7 +936,7 @@ namespace PlexRequests.UI.Modules
} }
catch (Exception e) catch (Exception e)
{ {
await FaultQueue.QueueItemAsync(model, RequestType.TvShow, FaultType.RequestFault); await FaultQueue.QueueItemAsync(model, showInfo.id.ToString(), RequestType.TvShow, FaultType.RequestFault);
await NotificationService.Publish(new NotificationModel await NotificationService.Publish(new NotificationModel
{ {
DateTime = DateTime.Now, DateTime = DateTime.Now,
@ -1102,7 +1102,7 @@ namespace PlexRequests.UI.Modules
catch (Exception e) catch (Exception e)
{ {
Log.Error(e); Log.Error(e);
await FaultQueue.QueueItemAsync(model, RequestType.Movie, FaultType.RequestFault); await FaultQueue.QueueItemAsync(model, albumInfo.id, RequestType.Movie, FaultType.RequestFault);
await NotificationService.Publish(new NotificationModel await NotificationService.Publish(new NotificationModel
{ {

@ -216,8 +216,6 @@
<Compile Include="Helpers\SecurityExtensions.cs" /> <Compile Include="Helpers\SecurityExtensions.cs" />
<Compile Include="Helpers\ServiceLocator.cs" /> <Compile Include="Helpers\ServiceLocator.cs" />
<Compile Include="Helpers\Themes.cs" /> <Compile Include="Helpers\Themes.cs" />
<Compile Include="Helpers\TvSender.cs" />
<Compile Include="Helpers\TvSenderOld.cs" />
<Compile Include="Helpers\ValidationHelper.cs" /> <Compile Include="Helpers\ValidationHelper.cs" />
<Compile Include="Jobs\CustomJobFactory.cs" /> <Compile Include="Jobs\CustomJobFactory.cs" />
<Compile Include="Jobs\Scheduler.cs" /> <Compile Include="Jobs\Scheduler.cs" />

Loading…
Cancel
Save