diff --git a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs index f3d43fb3c..9a32bb2fe 100644 --- a/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs +++ b/src/Ombi.Schedule/Jobs/Plex/PlexAvailabilityChecker.cs @@ -1,149 +1,146 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Hangfire; -using Microsoft.EntityFrameworkCore; -using Ombi.Core.Notifications; -using Ombi.Helpers; -using Ombi.Notifications.Models; -using Ombi.Store.Entities; -using Ombi.Store.Repository; -using Ombi.Store.Repository.Requests; - -namespace Ombi.Schedule.Jobs.Plex -{ - public class PlexAvailabilityChecker : IPlexAvailabilityChecker - { - public PlexAvailabilityChecker(IPlexContentRepository repo, ITvRequestRepository tvRequest, IMovieRequestRepository movies, - INotificationService notification, IBackgroundJobClient background) - { - _tvRepo = tvRequest; - _repo = repo; - _movieRepo = movies; - _notificationService = notification; - _backgroundJobClient = background; - } - - private readonly ITvRequestRepository _tvRepo; - private readonly IMovieRequestRepository _movieRepo; - private readonly IPlexContentRepository _repo; - private readonly INotificationService _notificationService; - private readonly IBackgroundJobClient _backgroundJobClient; - - public async Task Start() - { - await ProcessMovies(); - await ProcessTv(); - } - - private async Task ProcessTv() - { - var tv = _tvRepo.GetChild().Where(x => !x.Available); - var plexEpisodes = _repo.GetAllEpisodes().Include(x => x.Series); - - foreach (var child in tv) - { - - PlexServerContent item = null; - var useImdb = false; - var useTvDb = false; - if (child.ParentRequest.ImdbId.HasValue()) - { - useImdb = true; - } - - if (child.ParentRequest.TvDbId.ToString().HasValue()) - { - useTvDb = true; - } - - var tvDbId = child.ParentRequest.TvDbId; - var imdbId = child.ParentRequest.ImdbId; - IQueryable seriesEpisodes = null; - if (useImdb) - { - seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); - } - if (useTvDb) - { - seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); - } - foreach (var season in child.SeasonRequests) - { - foreach (var episode in season.Episodes) - { - var foundEp = await seriesEpisodes.FirstOrDefaultAsync( - x => x.EpisodeNumber == episode.EpisodeNumber && - x.SeasonNumber == episode.Season.SeasonNumber); - - if (foundEp != null) - { - episode.Available = true; - } - } - } - - // Check to see if all of the episodes in all seasons are available for this request - var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available)); - if (allAvailable) - { - // We have fulfulled this request! - child.Available = true; - _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions - { - DateTime = DateTime.Now, - NotificationType = NotificationType.RequestAvailable, - RequestId = child.ParentRequestId, - RequestType = RequestType.TvShow, - Recipient = child.RequestedUser.Email - })); - } - } - - await _tvRepo.Save(); - } - - private async Task ProcessMovies() - { - // Get all non available - var movies = _movieRepo.GetAll().Where(x => !x.Available); - - foreach (var movie in movies) - { - PlexServerContent item = null; - if (movie.ImdbId.HasValue()) - { - item = await _repo.Get(movie.ImdbId); - } - if (item == null) - { - if (movie.TheMovieDbId.ToString().HasValue()) - { - item = await _repo.Get(movie.TheMovieDbId.ToString()); - } - } - if (item == null) - { - // We don't yet have this - continue; - } - - movie.Available = true; - if (movie.Available) - { - _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions - { - DateTime = DateTime.Now, - NotificationType = NotificationType.RequestAvailable, - RequestId = movie.Id, - RequestType = RequestType.Movie, - Recipient = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty - })); - } - } - - await _movieRepo.Save(); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Hangfire; +using Microsoft.EntityFrameworkCore; +using Ombi.Core.Notifications; +using Ombi.Helpers; +using Ombi.Notifications.Models; +using Ombi.Store.Entities; +using Ombi.Store.Repository; +using Ombi.Store.Repository.Requests; + +namespace Ombi.Schedule.Jobs.Plex +{ + public class PlexAvailabilityChecker : IPlexAvailabilityChecker + { + public PlexAvailabilityChecker(IPlexContentRepository repo, ITvRequestRepository tvRequest, IMovieRequestRepository movies, + INotificationService notification, IBackgroundJobClient background) + { + _tvRepo = tvRequest; + _repo = repo; + _movieRepo = movies; + _notificationService = notification; + _backgroundJobClient = background; + } + + private readonly ITvRequestRepository _tvRepo; + private readonly IMovieRequestRepository _movieRepo; + private readonly IPlexContentRepository _repo; + private readonly INotificationService _notificationService; + private readonly IBackgroundJobClient _backgroundJobClient; + + public async Task Start() + { + await ProcessMovies(); + await ProcessTv(); + } + + private async Task ProcessTv() + { + var tv = _tvRepo.GetChild().Where(x => !x.Available); + var plexEpisodes = _repo.GetAllEpisodes().Include(x => x.Series); + + foreach (var child in tv) + { var useImdb = false; + var useTvDb = false; + if (child.ParentRequest.ImdbId.HasValue()) + { + useImdb = true; + } + + if (child.ParentRequest.TvDbId.ToString().HasValue()) + { + useTvDb = true; + } + + var tvDbId = child.ParentRequest.TvDbId; + var imdbId = child.ParentRequest.ImdbId; + IQueryable seriesEpisodes = null; + if (useImdb) + { + seriesEpisodes = plexEpisodes.Where(x => x.Series.ImdbId == imdbId.ToString()); + } + if (useTvDb) + { + seriesEpisodes = plexEpisodes.Where(x => x.Series.TvDbId == tvDbId.ToString()); + } + foreach (var season in child.SeasonRequests) + { + foreach (var episode in season.Episodes) + { + var foundEp = await seriesEpisodes.FirstOrDefaultAsync( + x => x.EpisodeNumber == episode.EpisodeNumber && + x.SeasonNumber == episode.Season.SeasonNumber); + + if (foundEp != null) + { + episode.Available = true; + } + } + } + + // Check to see if all of the episodes in all seasons are available for this request + var allAvailable = child.SeasonRequests.All(x => x.Episodes.All(c => c.Available)); + if (allAvailable) + { + // We have fulfulled this request! + child.Available = true; + _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions + { + DateTime = DateTime.Now, + NotificationType = NotificationType.RequestAvailable, + RequestId = child.ParentRequestId, + RequestType = RequestType.TvShow, + Recipient = child.RequestedUser.Email + })); + } + } + + await _tvRepo.Save(); + } + + private async Task ProcessMovies() + { + // Get all non available + var movies = _movieRepo.GetAll().Where(x => !x.Available); + + foreach (var movie in movies) + { + PlexServerContent item = null; + if (movie.ImdbId.HasValue()) + { + item = await _repo.Get(movie.ImdbId); + } + if (item == null) + { + if (movie.TheMovieDbId.ToString().HasValue()) + { + item = await _repo.Get(movie.TheMovieDbId.ToString()); + } + } + if (item == null) + { + // We don't yet have this + continue; + } + + movie.Available = true; + if (movie.Available) + { + _backgroundJobClient.Enqueue(() => _notificationService.Publish(new NotificationOptions + { + DateTime = DateTime.Now, + NotificationType = NotificationType.RequestAvailable, + RequestId = movie.Id, + RequestType = RequestType.Movie, + Recipient = movie.RequestedUser != null ? movie.RequestedUser.Email : string.Empty + })); + } + } + + await _movieRepo.Save(); + } + } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/app/services/identity.service.ts b/src/Ombi/ClientApp/app/services/identity.service.ts index 9f2d6cf15..56f2eae01 100644 --- a/src/Ombi/ClientApp/app/services/identity.service.ts +++ b/src/Ombi/ClientApp/app/services/identity.service.ts @@ -13,54 +13,54 @@ export class IdentityService extends ServiceAuthHelpers { super(http, "/api/v1/Identity/", platformLocation); } public createWizardUser(user: ICreateWizardUser): Observable { - return this.regularHttp.post(`${this.url}Wizard/`, JSON.stringify(user), { headers: this.headers }).map(this.extractData); + return this.regularHttp.post(`${this.url}Wizard/`, JSON.stringify(user), { headers: this.headers }).map(this.extractData).catch(this.handleError); } public getUser(): Observable { - return this.http.get(this.url).map(this.extractData); + return this.http.get(this.url).map(this.extractData).catch(this.handleError); } public getUserById(id: string): Observable { - return this.http.get(`${this.url}User/${id}`).map(this.extractData); + return this.http.get(`${this.url}User/${id}`).map(this.extractData).catch(this.handleError); } public getUsers(): Observable { - return this.http.get(`${this.url}Users`).map(this.extractData); + return this.http.get(`${this.url}Users`).map(this.extractData).catch(this.handleError); } public getAllAvailableClaims(): Observable { - return this.http.get(`${this.url}Claims`).map(this.extractData); + return this.http.get(`${this.url}Claims`).map(this.extractData).catch(this.handleError); } public createUser(user: IUser): Observable { - return this.http.post(this.url, JSON.stringify(user), { headers: this.headers }).map(this.extractData); + return this.http.post(this.url, JSON.stringify(user), { headers: this.headers }).map(this.extractData).catch(this.handleError); } public updateUser(user: IUser): Observable { - return this.http.put(this.url, JSON.stringify(user), { headers: this.headers }).map(this.extractData); + return this.http.put(this.url, JSON.stringify(user), { headers: this.headers }).map(this.extractData).catch(this.handleError); } public updateLocalUser(user: IUpdateLocalUser): Observable { - return this.http.put(this.url + "local", JSON.stringify(user), { headers: this.headers }).map(this.extractData); + return this.http.put(this.url + "local", JSON.stringify(user), { headers: this.headers }).map(this.extractData).catch(this.handleError); } public deleteUser(user: IUser): Observable { - return this.http.delete(`${this.url}${user.id}`, { headers: this.headers }).map(this.extractData); + return this.http.delete(`${this.url}${user.id}`, { headers: this.headers }).map(this.extractData).catch(this.handleError); } public hasUserRequested(userId: string): Observable { - return this.http.get(`${this.url}userhasrequest/${userId}`).map(this.extractData); + return this.http.get(`${this.url}userhasrequest/${userId}`).map(this.extractData).catch(this.handleError); } public submitResetPassword(email: string): Observable { - return this.regularHttp.post(this.url + "reset", JSON.stringify({email}), { headers: this.headers }).map(this.extractData); + return this.regularHttp.post(this.url + "reset", JSON.stringify({email}), { headers: this.headers }).map(this.extractData).catch(this.handleError); } public resetPassword(token: IResetPasswordToken): Observable { - return this.regularHttp.post(this.url + "resetpassword", JSON.stringify(token), { headers: this.headers }).map(this.extractData); + return this.regularHttp.post(this.url + "resetpassword", JSON.stringify(token), { headers: this.headers }).map(this.extractData).catch(this.handleError); } public sendWelcomeEmail(user: IUser): Observable { - return this.http.post(`${this.url}welcomeEmail`, JSON.stringify(user), { headers: this.headers }).map(this.extractData); + return this.http.post(`${this.url}welcomeEmail`, JSON.stringify(user), { headers: this.headers }).map(this.extractData).catch(this.handleError); } public hasRole(role: string): boolean { diff --git a/src/Ombi/ClientApp/app/services/service.helpers.ts b/src/Ombi/ClientApp/app/services/service.helpers.ts index b6c12f7cb..78f58e638 100644 --- a/src/Ombi/ClientApp/app/services/service.helpers.ts +++ b/src/Ombi/ClientApp/app/services/service.helpers.ts @@ -1,7 +1,6 @@ import { PlatformLocation } from "@angular/common"; import { Headers, Http, Response } from "@angular/http"; import "rxjs/add/observable/throw"; -import { Observable } from "rxjs/Observable"; import { AuthHttp } from "angular2-jwt"; @@ -24,12 +23,17 @@ export class ServiceHelpers { return body; } - protected handleError(error: any) { - // In a real world app, we might use a remote logging infrastructure - // We'd also dig deeper into the error to get a better message - const errMsg = (error.message) ? error.message : - error.status ? `${error.status} - ${error.statusText}` : "Server error"; - return Observable.throw(errMsg); + protected handleError(error: Response | any) { + let errMsg: string; + if (error instanceof Response) { + const body = error.json() || ""; + const err = body.error || JSON.stringify(body); + errMsg = `${error.status} - ${error.statusText || ""} ${err}`; + } else { + errMsg = error.message ? error.message : error.toString(); + } + console.error(errMsg); + return errMsg; } } @@ -63,11 +67,16 @@ export class ServiceAuthHelpers { } } - protected handleError(error: any) { - // In a real world app, we might use a remote logging infrastructure - // We'd also dig deeper into the error to get a better message - const errMsg = (error.message) ? error.message : - error.status ? `${error.status} - ${error.statusText}` : "Server error"; - return Observable.throw(errMsg); + protected handleError(error: Response | any) { + let errMsg: string; + if (error instanceof Response) { + const body = error.json() || ""; + const err = body.error || JSON.stringify(body); + errMsg = `${error.status} - ${error.statusText || ""} ${err}`; + } else { + errMsg = error.Message ? error.message : error.toString(); + } + console.error(errMsg); + return errMsg; } } diff --git a/src/Ombi/ErrorHandlingMiddlewear.cs b/src/Ombi/ErrorHandlingMiddlewear.cs new file mode 100644 index 000000000..756a3f5b6 --- /dev/null +++ b/src/Ombi/ErrorHandlingMiddlewear.cs @@ -0,0 +1,43 @@ +using System; +using System.Net; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Newtonsoft.Json; + +namespace Ombi +{ + public class ErrorHandlingMiddleware + { + private readonly RequestDelegate next; + + public ErrorHandlingMiddleware(RequestDelegate next) + { + this.next = next; + } + + public async Task Invoke(HttpContext context /* other scoped dependencies */) + { + try + { + await next(context); + } + catch (Exception ex) + { + await HandleExceptionAsync(context, ex); + } + } + + private static Task HandleExceptionAsync(HttpContext context, Exception exception) + { + var code = HttpStatusCode.InternalServerError; // 500 if unexpected + + //if (exception is NotFoundException) code = HttpStatusCode.NotFound; + if (exception is UnauthorizedAccessException) code = HttpStatusCode.Unauthorized; + + var result = JsonConvert.SerializeObject(new { error = exception.Message }); + context.Response.ContentType = "application/json"; + context.Response.StatusCode = (int)code; + return context.Response.WriteAsync(result); + } + } +} \ No newline at end of file diff --git a/src/Ombi/Startup.cs b/src/Ombi/Startup.cs index 26432017f..396612b4c 100644 --- a/src/Ombi/Startup.cs +++ b/src/Ombi/Startup.cs @@ -202,6 +202,7 @@ namespace Ombi c.ShowJsonEditor(); }); + app.UseMiddleware(); app.UseMvc(routes => { routes.MapRoute(