Finished #633 (First part of the queuing)

pull/687/head
Jamie.Rees 8 years ago
parent 1c7fb2e93e
commit 2bd7ece9d0

@ -34,6 +34,7 @@ namespace PlexRequests.Core.Models
RequestApproved,
AdminNote,
Test,
RequestDeclined,
ItemAddedToFaultQueue
}
}

@ -39,6 +39,10 @@
<HintPath>..\packages\Common.Logging.Core.3.0.0\lib\net40\Common.Logging.Core.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Dapper, Version=1.50.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Dapper.1.50.0-beta8\lib\net45\Dapper.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Data.Sqlite">
<HintPath>..\Assemblies\Mono.Data.Sqlite.dll</HintPath>
</Reference>
@ -105,6 +109,7 @@
<Compile Include="Notification\Templates\IEmailBasicTemplate.cs" />
<Compile Include="Notification\TransportType.cs" />
<Compile Include="PlexReadOnlyDatabase.cs" />
<Compile Include="Queue\ITransientFaultQueue.cs" />
<Compile Include="Queue\TransientFaultQueue.cs" />
<Compile Include="SettingModels\AuthenticationSettings.cs" />
<Compile Include="SettingModels\ExternalSettings.cs" />

@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using PlexRequests.Store;
using PlexRequests.Store.Models;
namespace PlexRequests.Core.Queue
{
public interface ITransientFaultQueue
{
void Dequeue();
Task DequeueAsync();
IEnumerable<RequestQueue> GetQueue();
Task<IEnumerable<RequestQueue>> GetQueueAsync();
void QueueItem(RequestedModel request, RequestType type, FaultType faultType);
Task QueueItemAsync(RequestedModel request, RequestType type, FaultType faultType);
}
}

@ -26,7 +26,9 @@
#endregion
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Dapper;
using PlexRequests.Helpers;
using PlexRequests.Store;
using PlexRequests.Store.Models;
@ -34,7 +36,7 @@ using PlexRequests.Store.Repository;
namespace PlexRequests.Core.Queue
{
public class TransientFaultQueue
public class TransientFaultQueue : ITransientFaultQueue
{
public TransientFaultQueue(IRepository<RequestQueue> queue)
{
@ -44,44 +46,84 @@ namespace PlexRequests.Core.Queue
private IRepository<RequestQueue> RequestQueue { get; }
public void QueueItem(RequestedModel request, RequestType type)
public void QueueItem(RequestedModel request, RequestType type, FaultType faultType)
{
//Ensure there is not a duplicate queued item
var existingItem = RequestQueue.Custom(
connection =>
{
connection.Open();
var result = connection.Query<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = request.ProviderId });
return result;
}).FirstOrDefault();
if (existingItem != null)
{
// It's already in the queue
return;
}
var queue = new RequestQueue
{
Type = type,
Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = request.ProviderId
PrimaryIdentifier = request.ProviderId,
FaultType = faultType
};
RequestQueue.Insert(queue);
}
public async Task QueueItemAsync(RequestedModel request, RequestType type)
public async Task QueueItemAsync(RequestedModel request, RequestType type, FaultType faultType)
{
//Ensure there is not a duplicate queued item
var existingItem = await RequestQueue.CustomAsync(async connection =>
{
connection.Open();
var result = await connection.QueryAsync<RequestQueue>("select * from RequestQueue where PrimaryIdentifier = @ProviderId", new { ProviderId = request.ProviderId });
return result;
});
if (existingItem.FirstOrDefault() != null)
{
// It's already in the queue
return;
}
var queue = new RequestQueue
{
Type = type,
Content = ByteConverterHelper.ReturnBytes(request),
PrimaryIdentifier = request.ProviderId
PrimaryIdentifier = request.ProviderId,
FaultType = faultType
};
await RequestQueue.InsertAsync(queue);
}
public IEnumerable<RequestQueue> Dequeue()
public IEnumerable<RequestQueue> GetQueue()
{
var items = RequestQueue.GetAll();
RequestQueue.DeleteAll("RequestQueue");
return items;
}
public async Task<IEnumerable<RequestQueue>> DequeueAsync()
public async Task<IEnumerable<RequestQueue>> GetQueueAsync()
{
var items = RequestQueue.GetAllAsync();
return await items;
}
await RequestQueue.DeleteAllAsync("RequestQueue");
public void Dequeue()
{
RequestQueue.DeleteAll("RequestQueue");
}
return await items;
public async Task DequeueAsync()
{
await RequestQueue.DeleteAllAsync("RequestQueue");
}
}
}

@ -2,6 +2,7 @@
<packages>
<package id="Common.Logging" version="3.0.0" targetFramework="net45" />
<package id="Common.Logging.Core" version="3.0.0" targetFramework="net45" />
<package id="Dapper" version="1.50.0-beta8" targetFramework="net45" />
<package id="Nancy" version="1.4.3" targetFramework="net45" />
<package id="Nancy.Authentication.Forms" version="1.4.1" targetFramework="net45" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />

@ -87,6 +87,14 @@ namespace PlexRequests.Services.Notification
case NotificationType.Test:
await EmailTest(model, emailSettings);
break;
case NotificationType.RequestDeclined:
throw new NotImplementedException();
case NotificationType.ItemAddedToFaultQueue:
await EmailAddedToRequestQueue(model, emailSettings);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
@ -129,7 +137,7 @@ namespace PlexRequests.Services.Notification
$"Plex Requests: New {model.RequestType.GetString()?.ToLower()} request for {model.Title}!",
$"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! The user '{model.User}' has requested the {model.RequestType.GetString()?.ToLower()} '{model.Title}'! Please log in to approve this request. Request Date: {model.DateTime.ToString("f")}" };
var message = new MimeMessage
{
@ -150,7 +158,7 @@ namespace PlexRequests.Services.Notification
$"Plex Requests: New issue for {model.Title}!",
$"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! The user '{model.User}' has reported a new issue {model.Body} for the title {model.Title}!"};
var message = new MimeMessage
{
@ -164,6 +172,27 @@ namespace PlexRequests.Services.Notification
await Send(message, settings);
}
private async Task EmailAddedToRequestQueue(NotificationModel model, EmailNotificationSettings settings)
{
var email = new EmailBasicTemplate();
var html = email.LoadTemplate(
"Plex Requests: A request could not be added.",
$"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! The user '{model.User}' has requested {model.Title} but it could not be added. This has been added into the requests queue and will keep retrying" };
var message = new MimeMessage
{
Body = body.ToMessageBody(),
Subject = $"Plex Requests: A request could not be added"
};
message.From.Add(new MailboxAddress(settings.EmailSender, settings.EmailSender));
message.To.Add(new MailboxAddress(settings.RecipientEmail, settings.RecipientEmail));
await Send(message, settings);
}
private async Task EmailAvailableRequest(NotificationModel model, EmailNotificationSettings settings)
{
if (!settings.EnableUserEmailNotifications)
@ -175,7 +204,7 @@ namespace PlexRequests.Services.Notification
$"Plex Requests: {model.Title} is now available!",
$"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)",
model.ImgSrc);
var body = new BodyBuilder { HtmlBody = html, TextBody = "This email is only available on devices that support HTML." };
var body = new BodyBuilder { HtmlBody = html, TextBody = $"Hello! You requested {model.Title} on PlexRequests! This is now available on Plex! :)" };
var message = new MimeMessage
{

@ -38,5 +38,12 @@ namespace PlexRequests.Store.Models
public byte[] Content { get; set; }
public FaultType FaultType { get; set; }
}
public enum FaultType
{
RequestFault,
MissingInformation
}
}

@ -138,6 +138,7 @@ CREATE TABLE IF NOT EXISTS RequestQueue
Id INTEGER PRIMARY KEY AUTOINCREMENT,
PrimaryIdentifier INTEGER NOT NULL,
Type INTEGER NOT NULL,
FaultType INTEGER NOT NULL,
Content BLOB NOT NULL
);
CREATE UNIQUE INDEX IF NOT EXISTS PlexUsers_Id ON PlexUsers (Id);

@ -1,4 +1,5 @@
#region Copyright
// /************************************************************************
// Copyright (c) 2016 Jamie Rees
// File: SearchModule.cs
@ -23,7 +24,9 @@
// 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.Globalization;
@ -54,13 +57,13 @@ using Newtonsoft.Json;
using PlexRequests.Api.Models.Sonarr;
using PlexRequests.Api.Models.Tv;
using PlexRequests.Core.Models;
using PlexRequests.Core.Queue;
using PlexRequests.Helpers.Analytics;
using PlexRequests.Helpers.Permissions;
using PlexRequests.Store.Models;
using PlexRequests.Store.Repository;
using TMDbLib.Objects.General;
using TMDbLib.Objects.Search;
using Action = PlexRequests.Helpers.Analytics.Action;
using EpisodesModel = PlexRequests.Store.EpisodesModel;
@ -73,10 +76,13 @@ namespace PlexRequests.UI.Modules
ISettingsService<PlexRequestSettings> prSettings, IAvailabilityChecker checker,
IRequestService request, ISonarrApi sonarrApi, ISettingsService<SonarrSettings> sonarrSettings,
ISettingsService<SickRageSettings> sickRageService, ICouchPotatoApi cpApi, ISickRageApi srApi,
INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi, ISettingsService<HeadphonesSettings> hpService,
INotificationService notify, IMusicBrainzApi mbApi, IHeadphonesApi hpApi,
ISettingsService<HeadphonesSettings> hpService,
ICouchPotatoCacher cpCacher, ISonarrCacher sonarrCacher, ISickRageCacher sickRageCacher, IPlexApi plexApi,
ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth, IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email,
IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl) : base("search", prSettings)
ISettingsService<PlexSettings> plexService, ISettingsService<AuthenticationSettings> auth,
IRepository<UsersToNotify> u, ISettingsService<EmailNotificationSettings> email,
IIssueService issue, IAnalytics a, IRepository<RequestLimit> rl, ITransientFaultQueue tfQueue)
: base("search", prSettings)
{
Auth = auth;
PlexService = plexService;
@ -104,6 +110,7 @@ namespace PlexRequests.UI.Modules
IssueService = issue;
Analytics = a;
RequestLimitRepo = rl;
FaultQueue = tfQueue;
TvApi = new TvMazeApi();
@ -118,7 +125,8 @@ namespace PlexRequests.UI.Modules
Get["movie/playing", true] = async (x, ct) => await CurrentlyPlayingMovies();
Post["request/movie", true] = async (x, ct) => await RequestMovie((int)Request.Form.movieId);
Post["request/tv", true] = async (x, ct) => await RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons);
Post["request/tv", true] =
async (x, ct) => await RequestTvShow((int)Request.Form.tvId, (string)Request.Form.seasons);
Post["request/tvEpisodes", true] = async (x, ct) => await RequestTvShow(0, "episode");
Post["request/album", true] = async (x, ct) => await RequestAlbum((string)Request.Form.albumId);
@ -128,6 +136,7 @@ namespace PlexRequests.UI.Modules
Get["/seasons"] = x => GetSeasons();
Get["/episodes", true] = async (x, ct) => await GetEpisodes();
}
private TvMazeApi TvApi { get; }
private IPlexApi PlexApi { get; }
private TheMovieDbApi MovieApi { get; }
@ -154,6 +163,7 @@ namespace PlexRequests.UI.Modules
private IRepository<UsersToNotify> UsersToNotifyRepo { get; }
private IIssueService IssueService { get; }
private IAnalytics Analytics { get; }
private ITransientFaultQueue FaultQueue { get; }
private IRepository<RequestLimit> RequestLimitRepo { get; }
private static Logger Log = LogManager.GetCurrentClassLogger();
@ -167,19 +177,22 @@ namespace PlexRequests.UI.Modules
private async Task<Response> UpcomingMovies()
{
Analytics.TrackEventAsync(Category.Search, Action.Movie, "Upcoming", Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.Movie, "Upcoming", Username,
CookieHelper.GetAnalyticClientId(Cookies));
return await ProcessMovies(MovieSearchType.Upcoming, string.Empty);
}
private async Task<Response> CurrentlyPlayingMovies()
{
Analytics.TrackEventAsync(Category.Search, Action.Movie, "CurrentlyPlaying", Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.Movie, "CurrentlyPlaying", Username,
CookieHelper.GetAnalyticClientId(Cookies));
return await ProcessMovies(MovieSearchType.CurrentlyPlaying, string.Empty);
}
private async Task<Response> SearchMovie(string searchTerm)
{
Analytics.TrackEventAsync(Category.Search, Action.Movie, searchTerm, Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.Movie, searchTerm, Username,
CookieHelper.GetAnalyticClientId(Cookies));
return await ProcessMovies(MovieSearchType.Search, searchTerm);
}
@ -192,24 +205,24 @@ namespace PlexRequests.UI.Modules
case MovieSearchType.Search:
var movies = await MovieApi.SearchMovie(searchTerm).ConfigureAwait(false);
apiMovies = movies.Select(x =>
new MovieResult
{
Adult = x.Adult,
BackdropPath = x.BackdropPath,
GenreIds = x.GenreIds,
Id = x.Id,
OriginalLanguage = x.OriginalLanguage,
OriginalTitle = x.OriginalTitle,
Overview = x.Overview,
Popularity = x.Popularity,
PosterPath = x.PosterPath,
ReleaseDate = x.ReleaseDate,
Title = x.Title,
Video = x.Video,
VoteAverage = x.VoteAverage,
VoteCount = x.VoteCount
})
.ToList();
new MovieResult
{
Adult = x.Adult,
BackdropPath = x.BackdropPath,
GenreIds = x.GenreIds,
Id = x.Id,
OriginalLanguage = x.OriginalLanguage,
OriginalTitle = x.OriginalTitle,
Overview = x.Overview,
Popularity = x.Popularity,
PosterPath = x.PosterPath,
ReleaseDate = x.ReleaseDate,
Title = x.Title,
Video = x.Video,
VoteAverage = x.VoteAverage,
VoteCount = x.VoteCount
})
.ToList();
break;
case MovieSearchType.CurrentlyPlaying:
apiMovies = await MovieApi.GetCurrentPlayingMovies();
@ -239,8 +252,9 @@ namespace PlexRequests.UI.Modules
var imdbId = string.Empty;
if (counter <= 5) // Let's only do it for the first 5 items
{
var movieInfoTask = await MovieApi.GetMovieInformation(movie.Id).ConfigureAwait(false); // TODO needs to be careful about this, it's adding extra time to search...
// https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2
var movieInfoTask = await MovieApi.GetMovieInformation(movie.Id).ConfigureAwait(false);
// TODO needs to be careful about this, it's adding extra time to search...
// https://www.themoviedb.org/talk/5807f4cdc3a36812160041f2
imdbId = movieInfoTask.ImdbId;
counter++;
}
@ -263,7 +277,8 @@ namespace PlexRequests.UI.Modules
VoteCount = movie.VoteCount
};
var canSee = CanUserSeeThisRequest(viewMovie.Id, settings.UsersCanViewOnlyOwnRequests, dbMovies);
var plexMovie = Checker.GetMovie(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString(), imdbId);
var plexMovie = Checker.GetMovie(plexMovies.ToArray(), movie.Title, movie.ReleaseDate?.Year.ToString(),
imdbId);
if (plexMovie != null)
{
viewMovie.Available = true;
@ -288,7 +303,8 @@ namespace PlexRequests.UI.Modules
return Response.AsJson(viewMovies);
}
private bool CanUserSeeThisRequest(int movieId, bool usersCanViewOnlyOwnRequests, Dictionary<int, RequestedModel> moviesInDb)
private bool CanUserSeeThisRequest(int movieId, bool usersCanViewOnlyOwnRequests,
Dictionary<int, RequestedModel> moviesInDb)
{
if (usersCanViewOnlyOwnRequests)
{
@ -302,7 +318,8 @@ namespace PlexRequests.UI.Modules
private async Task<Response> SearchTvShow(string searchTerm)
{
Analytics.TrackEventAsync(Category.Search, Action.TvShow, searchTerm, Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.TvShow, searchTerm, Username,
CookieHelper.GetAnalyticClientId(Cookies));
var plexSettings = await PlexService.GetSettingsAsync();
var prSettings = await PrService.GetSettingsAsync();
var providerId = string.Empty;
@ -360,7 +377,8 @@ namespace PlexRequests.UI.Modules
providerId = viewT.Id.ToString();
}
var plexShow = Checker.GetTvShow(plexTvShows.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4), providerId);
var plexShow = Checker.GetTvShow(plexTvShows.ToArray(), t.show.name, t.show.premiered?.Substring(0, 4),
providerId);
if (plexShow != null)
{
viewT.Available = true;
@ -377,7 +395,8 @@ namespace PlexRequests.UI.Modules
viewT.Episodes = dbt.Episodes.ToList();
viewT.Approved = dbt.Approved;
}
if (sonarrCached.Select(x => x.TvdbId).Contains(tvdbid) || sickRageCache.Contains(tvdbid)) // compare to the sonarr/sickrage db
if (sonarrCached.Select(x => x.TvdbId).Contains(tvdbid) || sickRageCache.Contains(tvdbid))
// compare to the sonarr/sickrage db
{
viewT.Requested = true;
}
@ -391,7 +410,8 @@ namespace PlexRequests.UI.Modules
private async Task<Response> SearchAlbum(string searchTerm)
{
Analytics.TrackEventAsync(Category.Search, Action.Album, searchTerm, Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.Album, searchTerm, Username,
CookieHelper.GetAnalyticClientId(Cookies));
var apiAlbums = new List<Release>();
await Task.Run(() => MusicBrainzApi.SearchAlbum(searchTerm)).ContinueWith((t) =>
{
@ -448,7 +468,7 @@ namespace PlexRequests.UI.Modules
if (Security.DoesNotHavePermissions(Permissions.ReadOnlyUser, User))
{
return
Response.AsJson(new JsonResponseModel()
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = "Sorry, you do not have the correct permissions to request a movie!"
@ -457,12 +477,19 @@ namespace PlexRequests.UI.Modules
var settings = await PrService.GetSettingsAsync();
if (!await CheckRequestLimit(settings, RequestType.Movie))
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "You have reached your weekly request limit for Movies! Please contact your admin." });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = "You have reached your weekly request limit for Movies! Please contact your admin."
});
}
Analytics.TrackEventAsync(Category.Search, Action.Request, "Movie", Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.Request, "Movie", Username,
CookieHelper.GetAnalyticClientId(Cookies));
var movieInfo = await MovieApi.GetMovieInformation(movieId);
var fullMovieName = $"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}";
var fullMovieName =
$"{movieInfo.Title}{(movieInfo.ReleaseDate.HasValue ? $" ({movieInfo.ReleaseDate.Value.Year})" : string.Empty)}";
var existingRequest = await RequestService.CheckRequestAsync(movieId);
if (existingRequest != null)
@ -474,7 +501,15 @@ namespace PlexRequests.UI.Modules
await RequestService.UpdateRequestAsync(existingRequest);
}
return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}" : $"{fullMovieName} {Resources.UI.Search_AlreadyRequested}" });
return
Response.AsJson(new JsonResponseModel
{
Result = true,
Message =
settings.UsersCanViewOnlyOwnRequests
? $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}"
: $"{fullMovieName} {Resources.UI.Search_AlreadyRequested}"
});
}
try
@ -482,13 +517,23 @@ namespace PlexRequests.UI.Modules
var movies = Checker.GetPlexMovies();
if (Checker.IsMovieAvailable(movies.ToArray(), movieInfo.Title, movieInfo.ReleaseDate?.Year.ToString()))
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullMovieName} is already in Plex!" });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"{fullMovieName} is already in Plex!"
});
}
}
catch (Exception e)
{
Log.Error(e);
return Response.AsJson(new JsonResponseModel { Result = false, Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullMovieName) });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullMovieName)
});
}
//#endif
@ -508,41 +553,58 @@ namespace PlexRequests.UI.Modules
Issues = IssueState.None,
};
if (ShouldAutoApprove(RequestType.Movie, settings))
try
{
var cpSettings = await CpService.GetSettingsAsync();
model.Approved = true;
if (cpSettings.Enabled)
if (ShouldAutoApprove(RequestType.Movie, settings))
{
Log.Info("Adding movie to CP (No approval required)");
var result = CouchPotatoApi.AddMovie(model.ImdbId, cpSettings.ApiKey, model.Title,
cpSettings.FullUri, cpSettings.ProfileId);
Log.Debug("Adding movie to CP result {0}", result);
if (result)
var cpSettings = await CpService.GetSettingsAsync();
model.Approved = true;
if (cpSettings.Enabled)
{
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
Log.Info("Adding movie to CP (No approval required)");
var result = CouchPotatoApi.AddMovie(model.ImdbId, cpSettings.ApiKey, model.Title,
cpSettings.FullUri, cpSettings.ProfileId);
Log.Debug("Adding movie to CP result {0}", result);
if (result)
{
return
await
AddRequest(model, settings,
$"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
return Response.AsJson(new JsonResponseModel
{
Result = false,
Message = Resources.UI.Search_CouchPotatoError
});
return Response.AsJson(new JsonResponseModel
{
Result = false,
Message = Resources.UI.Search_CouchPotatoError
});
}
model.Approved = true;
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
model.Approved = true;
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
try
{
return await AddRequest(model, settings, $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}");
}
catch (Exception e)
{
Log.Fatal(e);
await FaultQueue.QueueItemAsync(model, RequestType.Movie, FaultType.RequestFault);
return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_CouchPotatoError });
await NotificationService.Publish(new NotificationModel
{
DateTime = DateTime.Now,
User = Username,
RequestType = RequestType.Movie,
Title = model.Title,
NotificationType = NotificationType.ItemAddedToFaultQueue
});
return Response.AsJson(new JsonResponseModel
{
Result = true,
Message = $"{fullMovieName} {Resources.UI.Search_SuccessfullyAdded}"
});
}
}
@ -576,9 +638,15 @@ namespace PlexRequests.UI.Modules
var settings = await PrService.GetSettingsAsync();
if (!await CheckRequestLimit(settings, RequestType.TvShow))
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_WeeklyRequestLimitTVShow });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = Resources.UI.Search_WeeklyRequestLimitTVShow
});
}
Analytics.TrackEventAsync(Category.Search, Action.Request, "TvShow", Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.Request, "TvShow", Username,
CookieHelper.GetAnalyticClientId(Cookies));
var sonarrSettings = SonarrService.GetSettingsAsync();
@ -590,7 +658,13 @@ namespace PlexRequests.UI.Modules
var s = await sonarrSettings;
if (!s.Enabled)
{
return Response.AsJson(new JsonResponseModel { Message = "This is currently only supported with Sonarr, Please enable Sonarr for this feature", Result = false });
return
Response.AsJson(new JsonResponseModel
{
Message =
"This is currently only supported with Sonarr, Please enable Sonarr for this feature",
Result = false
});
}
}
@ -599,14 +673,8 @@ namespace PlexRequests.UI.Modules
DateTime.TryParse(showInfo.premiered, out firstAir);
string fullShowName = $"{showInfo.name} ({firstAir.Year})";
if (showInfo.externals?.thetvdb == null)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = "Our TV Provider (TVMaze) doesn't have a TheTVDBId for this TV Show :( We cannot add the TV Show automatically sorry! Please report this problem to the server admin so he/she can sort it out!" });
}
var model = new RequestedModel
{
ProviderId = showInfo.externals?.thetvdb ?? 0,
Type = RequestType.TvShow,
Overview = showInfo.summary.RemoveHtml(),
PosterPath = showInfo.image?.medium,
@ -622,6 +690,25 @@ namespace PlexRequests.UI.Modules
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>();
switch (seasons)
@ -642,9 +729,14 @@ namespace PlexRequests.UI.Modules
foreach (var ep in episodeModel?.Episodes ?? new Models.EpisodesModel[0])
{
model.Episodes.Add(new EpisodesModel { EpisodeNumber = ep.EpisodeNumber, SeasonNumber = ep.SeasonNumber });
model.Episodes.Add(new EpisodesModel
{
EpisodeNumber = ep.EpisodeNumber,
SeasonNumber = ep.SeasonNumber
});
}
Analytics.TrackEventAsync(Category.Requests, Action.TvShow, $"Episode request for {model.Title}", Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Requests, Action.TvShow, $"Episode request for {model.Title}",
Username, CookieHelper.GetAnalyticClientId(Cookies));
break;
default:
model.SeasonsRequested = seasons;
@ -691,7 +783,12 @@ namespace PlexRequests.UI.Modules
else
{
// We no episodes to approve
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}" });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
});
}
}
else if (model.SeasonList.Except(existingRequest.SeasonList).Any())
@ -720,9 +817,19 @@ namespace PlexRequests.UI.Modules
var cachedEpisodes = cachedEpisodesTask.ToList();
foreach (var d in difference) // difference is from an existing request
{
if (cachedEpisodes.Any(x => x.SeasonNumber == d.SeasonNumber && x.EpisodeNumber == d.EpisodeNumber && x.ProviderId == providerId))
if (
cachedEpisodes.Any(
x =>
x.SeasonNumber == d.SeasonNumber && x.EpisodeNumber == d.EpisodeNumber &&
x.ProviderId == providerId))
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {Resources.UI.Search_AlreadyInPlex}" });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message =
$"{fullShowName} {d.SeasonNumber} - {d.EpisodeNumber} {Resources.UI.Search_AlreadyInPlex}"
});
}
}
@ -738,66 +845,118 @@ namespace PlexRequests.UI.Modules
var result = Checker.IsEpisodeAvailable(showId.ToString(), s.SeasonNumber, s.EpisodeNumber);
if (result)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}" });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
});
}
}
}
else if (Checker.IsTvShowAvailable(shows.ToArray(), showInfo.name, showInfo.premiered?.Substring(0, 4), providerId, model.SeasonList))
else if (Checker.IsTvShowAvailable(shows.ToArray(), showInfo.name, showInfo.premiered?.Substring(0, 4),
providerId, model.SeasonList))
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}" });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = $"{fullShowName} {Resources.UI.Search_AlreadyInPlex}"
});
}
}
}
catch (Exception)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullShowName) });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = string.Format(Resources.UI.Search_CouldNotCheckPlex, fullShowName)
});
}
if (ShouldAutoApprove(RequestType.TvShow, settings))
try
{
model.Approved = true;
var s = await sonarrSettings;
var sender = new TvSenderOld(SonarrApi, SickrageApi); // TODO put back
if (s.Enabled)
if (ShouldAutoApprove(RequestType.TvShow, settings))
{
var result = await sender.SendToSonarr(s, model);
if (!string.IsNullOrEmpty(result?.title))
model.Approved = true;
var s = await sonarrSettings;
var sender = new TvSenderOld(SonarrApi, SickrageApi); // TODO put back
if (s.Enabled)
{
if (existingRequest != null)
var result = await sender.SendToSonarr(s, model);
if (!string.IsNullOrEmpty(result?.title))
{
return await UpdateRequest(model, settings,
if (existingRequest != null)
{
return await UpdateRequest(model, settings,
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
return
await
AddRequest(model, settings,
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
Log.Debug("Error with sending to sonarr.");
return
Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages ?? new List<string>()));
}
Log.Debug("Error with sending to sonarr.");
return Response.AsJson(ValidationHelper.SendSonarrError(result?.ErrorMessages ?? new List<string>()));
}
var srSettings = SickRageService.GetSettings();
if (srSettings.Enabled)
{
var result = sender.SendToSickRage(srSettings, model);
if (result?.result == "success")
var srSettings = SickRageService.GetSettings();
if (srSettings.Enabled)
{
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
var result = sender.SendToSickRage(srSettings, model);
if (result?.result == "success")
{
return
await
AddRequest(model, settings,
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = result?.message ?? Resources.UI.Search_SickrageError
});
}
return Response.AsJson(new JsonResponseModel { Result = false, Message = result?.message ?? Resources.UI.Search_SickrageError });
}
if (!srSettings.Enabled && !s.Enabled)
{
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
if (!srSettings.Enabled && !s.Enabled)
{
return
await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_TvNotSetUp });
return
Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_TvNotSetUp });
}
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
catch (Exception e)
{
await FaultQueue.QueueItemAsync(model, RequestType.TvShow, FaultType.RequestFault);
await NotificationService.Publish(new NotificationModel
{
DateTime = DateTime.Now,
User = Username,
RequestType = RequestType.TvShow,
Title = model.Title,
NotificationType = NotificationType.ItemAddedToFaultQueue
});
Log.Error(e);
return
Response.AsJson(new JsonResponseModel
{
Result = true,
Message = $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}"
});
}
return await AddRequest(model, settings, $"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
private async Task<Response> AddUserToRequest(RequestedModel existingRequest, PlexRequestSettings settings, string fullShowName, bool episodeReq = false)
private async Task<Response> AddUserToRequest(RequestedModel existingRequest, PlexRequestSettings settings,
string fullShowName, bool episodeReq = false)
{
// check if the current user is already marked as a requester for this show, if not, add them
if (!existingRequest.UserHasRequested(Username))
@ -812,12 +971,15 @@ namespace PlexRequests.UI.Modules
$"{fullShowName} {Resources.UI.Search_SuccessfullyAdded}");
}
return await UpdateRequest(existingRequest, settings, $"{fullShowName} {Resources.UI.Search_AlreadyRequested}");
return
await UpdateRequest(existingRequest, settings, $"{fullShowName} {Resources.UI.Search_AlreadyRequested}");
}
private bool ShouldSendNotification(RequestType type, PlexRequestSettings prSettings)
{
var sendNotification = ShouldAutoApprove(type, prSettings) ? !prSettings.IgnoreNotifyForAutoApprovedRequests : true;
var sendNotification = ShouldAutoApprove(type, prSettings)
? !prSettings.IgnoreNotifyForAutoApprovedRequests
: true;
var claims = Context.CurrentUser?.Claims;
if (claims != null)
{
@ -836,9 +998,15 @@ namespace PlexRequests.UI.Modules
var settings = await PrService.GetSettingsAsync();
if (!await CheckRequestLimit(settings, RequestType.Album))
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_WeeklyRequestLimitAlbums });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = Resources.UI.Search_WeeklyRequestLimitAlbums
});
}
Analytics.TrackEventAsync(Category.Search, Action.Request, "Album", Username, CookieHelper.GetAnalyticClientId(Cookies));
Analytics.TrackEventAsync(Category.Search, Action.Request, "Album", Username,
CookieHelper.GetAnalyticClientId(Cookies));
var existingRequest = await RequestService.CheckRequestAsync(releaseId);
if (existingRequest != null)
@ -848,7 +1016,15 @@ namespace PlexRequests.UI.Modules
existingRequest.RequestedUsers.Add(Username);
await RequestService.UpdateRequestAsync(existingRequest);
}
return Response.AsJson(new JsonResponseModel { Result = true, Message = settings.UsersCanViewOnlyOwnRequests ? $"{existingRequest.Title} {Resources.UI.Search_SuccessfullyAdded}" : $"{existingRequest.Title} {Resources.UI.Search_AlreadyRequested}" });
return
Response.AsJson(new JsonResponseModel
{
Result = true,
Message =
settings.UsersCanViewOnlyOwnRequests
? $"{existingRequest.Title} {Resources.UI.Search_SuccessfullyAdded}"
: $"{existingRequest.Title} {Resources.UI.Search_AlreadyRequested}"
});
}
var albumInfo = MusicBrainzApi.GetAlbum(releaseId);
@ -858,11 +1034,17 @@ namespace PlexRequests.UI.Modules
var artist = albumInfo.ArtistCredits?.FirstOrDefault()?.artist;
if (artist == null)
{
return Response.AsJson(new JsonResponseModel { Result = false, Message = Resources.UI.Search_MusicBrainzError });
return
Response.AsJson(new JsonResponseModel
{
Result = false,
Message = Resources.UI.Search_MusicBrainzError
});
}
var albums = Checker.GetPlexAlbums();
var alreadyInPlex = Checker.IsAlbumAvailable(albums.ToArray(), albumInfo.title, release.ToString("yyyy"), artist.name);
var alreadyInPlex = Checker.IsAlbumAvailable(albums.ToArray(), albumInfo.title, release.ToString("yyyy"),
artist.name);
if (alreadyInPlex)
{
@ -892,28 +1074,46 @@ namespace PlexRequests.UI.Modules
ArtistId = artist.id
};
if (ShouldAutoApprove(RequestType.Album, settings))
try
{
model.Approved = true;
var hpSettings = HeadphonesService.GetSettings();
if (!hpSettings.Enabled)
if (ShouldAutoApprove(RequestType.Album, settings))
{
await RequestService.AddRequestAsync(model);
return
Response.AsJson(new JsonResponseModel
{
Result = true,
Message = $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}"
});
model.Approved = true;
var hpSettings = HeadphonesService.GetSettings();
if (!hpSettings.Enabled)
{
await RequestService.AddRequestAsync(model);
return
Response.AsJson(new JsonResponseModel
{
Result = true,
Message = $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}"
});
}
var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService);
await sender.AddAlbum(model);
return await AddRequest(model, settings, $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}");
}
var sender = new HeadphonesSender(HeadphonesApi, hpSettings, RequestService);
await sender.AddAlbum(model);
return await AddRequest(model, settings, $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}");
}
catch (Exception e)
{
Log.Error(e);
await FaultQueue.QueueItemAsync(model, RequestType.Movie, FaultType.RequestFault);
return await AddRequest(model, settings, $"{model.Title} {Resources.UI.Search_SuccessfullyAdded}");
await NotificationService.Publish(new NotificationModel
{
DateTime = DateTime.Now,
User = Username,
RequestType = RequestType.Album,
Title = model.Title,
NotificationType = NotificationType.ItemAddedToFaultQueue
});
throw;
}
}
private string GetMusicBrainzCoverArt(string id)

@ -25,7 +25,7 @@
// ************************************************************************/
#endregion
using Ninject.Modules;
using PlexRequests.Core.Queue;
using PlexRequests.Helpers.Analytics;
using PlexRequests.Services.Interfaces;
using PlexRequests.Services.Jobs;
@ -51,6 +51,8 @@ namespace PlexRequests.UI.NinjectModules
Bind<IAnalytics>().To<Analytics>();
Bind<ISchedulerFactory>().To<StdSchedulerFactory>();
Bind<IJobScheduler>().To<Scheduler>();
Bind<ITransientFaultQueue>().To<TransientFaultQueue>();
}
}
}
Loading…
Cancel
Save