diff --git a/Ombi/Ombi.Core/Engine/ITvRequestEngine.cs b/Ombi/Ombi.Core/Engine/ITvRequestEngine.cs new file mode 100644 index 000000000..a875e726f --- /dev/null +++ b/Ombi/Ombi.Core/Engine/ITvRequestEngine.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Ombi.Core.Models.Requests; +using Ombi.Core.Models.Search; + +namespace Ombi.Core.Engine +{ + public interface ITvRequestEngine + { + Task> GetTvRequests(int count, int position); + Task RemoveTvRequest(int requestId); + Task RequestTvShow(SearchTvShowViewModel tv); + Task> SearchTvRequest(string search); + Task UpdateTvRequest(TvRequestModel request); + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Core/Engine/Interfaces/IRequestEngine.cs b/Ombi/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs similarity index 93% rename from Ombi/Ombi.Core/Engine/Interfaces/IRequestEngine.cs rename to Ombi/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs index 8f1bf6fc9..3f3af63a1 100644 --- a/Ombi/Ombi.Core/Engine/Interfaces/IRequestEngine.cs +++ b/Ombi/Ombi.Core/Engine/Interfaces/IMovieRequestEngine.cs @@ -6,7 +6,7 @@ using Ombi.Store.Entities; namespace Ombi.Core.Engine { - public interface IRequestEngine + public interface IMovieRequestEngine { Task RequestMovie(SearchMovieViewModel model); bool ShouldAutoApprove(RequestType requestType); diff --git a/Ombi/Ombi.Core/Engine/RequestEngine.cs b/Ombi/Ombi.Core/Engine/MovieRequestEngine.cs similarity index 84% rename from Ombi/Ombi.Core/Engine/RequestEngine.cs rename to Ombi/Ombi.Core/Engine/MovieRequestEngine.cs index 7408c7319..09ba73be4 100644 --- a/Ombi/Ombi.Core/Engine/RequestEngine.cs +++ b/Ombi/Ombi.Core/Engine/MovieRequestEngine.cs @@ -17,17 +17,15 @@ using Ombi.Notifications.Models; namespace Ombi.Core.Engine { - public class RequestEngine : BaseMediaEngine, IRequestEngine + public class MovieRequestEngine : BaseMediaEngine, IMovieRequestEngine { - public RequestEngine(IMovieDbApi movieApi, ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, INotificationService notificationService) : base(user, requestService) + public MovieRequestEngine(IMovieDbApi movieApi, IRequestServiceMain requestService, IPrincipal user, INotificationService notificationService) : base(user, requestService) { MovieApi = movieApi; - TvApi = tvApi; NotificationService = notificationService; } private IMovieDbApi MovieApi { get; } private INotificationService NotificationService { get; } - private ITvMazeApi TvApi { get; } public async Task RequestMovie(SearchMovieViewModel model) { var movieInfo = await MovieApi.GetMovieInformation(model.Id); @@ -161,41 +159,7 @@ namespace Ombi.Core.Engine return null; } - public async Task RequestTvShow(SearchTvShowViewModel tv) - { - - var showInfo = await TvApi.ShowLookupByTheTvDbId(tv.Id); - DateTime.TryParse(showInfo.premiered, out DateTime firstAir); - - string fullShowName = $"{showInfo.name} ({firstAir.Year})"; - // For some reason the poster path is always http - var posterPath = showInfo.image?.medium.Replace("http:", "https:"); - var model = new TvRequestModel - { - Type = RequestType.TvShow, - Overview = showInfo.summary.RemoveHtml(), - PosterPath = posterPath, - Title = showInfo.name, - ReleaseDate = firstAir, - Status = showInfo.status, - RequestedDate = DateTime.UtcNow, - Approved = false, - RequestedUsers = new List { Username }, - Issues = IssueState.None, - ImdbId = showInfo.externals?.imdb ?? string.Empty, - TvDbId = tv.Id.ToString(), - ProviderId = tv.Id, - SeasonsNumbersRequested = tv.SeasonNumbersRequested, - RequestAll = tv.RequestAll - }; - - - var existingRequest = await TvRequestService.CheckRequestAsync(model.Id); - existingRequest?.ChildRequests.Add(model); - - return null; - } - + private IEnumerable GetListDifferences(IEnumerable existing, IEnumerable request) { var newRequest = request diff --git a/Ombi/Ombi.Core/Engine/TvRequestEngine.cs b/Ombi/Ombi.Core/Engine/TvRequestEngine.cs new file mode 100644 index 000000000..14cc90484 --- /dev/null +++ b/Ombi/Ombi.Core/Engine/TvRequestEngine.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Security.Principal; +using System.Threading.Tasks; +using Hangfire; +using Ombi.Api.TvMaze; +using Ombi.Core.Models.Requests; +using Ombi.Core.Models.Search; +using Ombi.Store.Entities; +using Ombi.Helpers; +using Ombi.Notifications; +using Ombi.Notifications.Models; + +namespace Ombi.Core.Engine +{ + public class TvRequestEngine : BaseMediaEngine, ITvRequestEngine + { + public TvRequestEngine(ITvMazeApi tvApi, IRequestServiceMain requestService, IPrincipal user, INotificationService notificationService) : base(user, requestService) + { + TvApi = tvApi; + NotificationService = notificationService; + } + private INotificationService NotificationService { get; } + private ITvMazeApi TvApi { get; } + + public async Task RequestTvShow(SearchTvShowViewModel tv) + { + + var showInfo = await TvApi.ShowLookupByTheTvDbId(tv.Id); + DateTime.TryParse(showInfo.premiered, out DateTime firstAir); + + // For some reason the poster path is always http + var posterPath = showInfo.image?.medium.Replace("http:", "https:"); + var model = new TvRequestModel + { + Type = RequestType.TvShow, + Overview = showInfo.summary.RemoveHtml(), + PosterPath = posterPath, + Title = showInfo.name, + ReleaseDate = firstAir, + Status = showInfo.status, + RequestedDate = DateTime.UtcNow, + Approved = false, + RequestedUsers = new List { Username }, + Issues = IssueState.None, + ImdbId = showInfo.externals?.imdb ?? string.Empty, + TvDbId = tv.Id.ToString(), + ProviderId = tv.Id, + SeasonsNumbersRequested = tv.SeasonNumbersRequested, + RequestAll = tv.RequestAll + }; + + + var existingRequest = await TvRequestService.CheckRequestAsync(model.Id); + if (existingRequest != null) + { + return await AddExistingRequest(model, existingRequest); + } + + // This is a new request + return await AddRequest(model); + } + + public async Task> GetTvRequests(int count, int position) + { + var allRequests = await TvRequestService.GetAllAsync(count, position); + return allRequests; + } + public async Task> SearchTvRequest(string search) + { + var allRequests = await TvRequestService.GetAllAsync(); + var results = allRequests.Where(x => x.Title.Contains(search, CompareOptions.IgnoreCase)); + return results; + } + public async Task UpdateTvRequest(TvRequestModel request) + { + var allRequests = await TvRequestService.GetAllAsync(); + var results = allRequests.FirstOrDefault(x => x.Id == request.Id); + + results.Approved = request.Approved; + results.Available = request.Available; + results.Denied = request.Denied; + results.DeniedReason = request.DeniedReason; + results.AdminNote = request.AdminNote; + results.ImdbId = request.ImdbId; + results.IssueId = request.IssueId; + results.Issues = request.Issues; + results.OtherMessage = request.OtherMessage; + results.Overview = request.Overview; + results.PosterPath = request.PosterPath; + results.RequestedUsers = request.RequestedUsers?.ToList() ?? new List(); + + var model = TvRequestService.UpdateRequest(results); + return model; + } + + public async Task RemoveTvRequest(int requestId) + { + await TvRequestService.DeleteRequestAsync(requestId); + } + + private async Task AddExistingRequest(TvRequestModel newRequest, TvRequestModel existingRequest) + { + var episodeDifference = new List(); + if (existingRequest.HasChildRequests) + { + // Let's check if this has already been requested as a child! + foreach (var children in existingRequest.ChildRequests) + { + var difference = GetListDifferences(children.Episodes, newRequest.Episodes).ToList(); + if (difference.Any()) + { + episodeDifference = difference; + } + } + } + + if (episodeDifference.Any()) + { + // This is where there are some episodes that have been requested, but this list contains the 'new' requests + newRequest.Episodes = episodeDifference; + } + + existingRequest.ChildRequests.Add(newRequest); + + TvRequestService.UpdateRequest(existingRequest); + + if (ShouldAutoApprove(RequestType.TvShow)) + { + // TODO Auto Approval Code + } + return await AddRequest(newRequest); + } + + private IEnumerable GetListDifferences(IEnumerable existing, IEnumerable request) + { + var newRequest = request + .Select(r => + new EpisodesModel + { + SeasonNumber = r.SeasonNumber, + EpisodeNumber = r.EpisodeNumber + }).ToList(); + + return newRequest.Except(existing); + } + + private async Task AddRequest(TvRequestModel model) + { + await TvRequestService.AddRequestAsync(model); + + if (ShouldSendNotification(model.Type)) + { + var notificationModel = new NotificationModel + { + Title = model.Title, + User = Username, + DateTime = DateTime.Now, + NotificationType = NotificationType.NewRequest, + RequestType = model.Type, + ImgSrc = model.PosterPath + }; + + BackgroundJob.Enqueue(() => NotificationService.Publish(notificationModel).Wait()); + } + + //var limit = await RequestLimitRepo.GetAllAsync(); + //var usersLimit = limit.FirstOrDefault(x => x.Username == Username && x.RequestType == model.Type); + //if (usersLimit == null) + //{ + // await RequestLimitRepo.InsertAsync(new RequestLimit + // { + // Username = Username, + // RequestType = model.Type, + // FirstRequestDate = DateTime.UtcNow, + // RequestCount = 1 + // }); + //} + //else + //{ + // usersLimit.RequestCount++; + // await RequestLimitRepo.UpdateAsync(usersLimit); + //} + + return new RequestEngineResult { RequestAdded = true }; + } + + + } +} \ No newline at end of file diff --git a/Ombi/Ombi.Core/Models/Requests/Tv/TvRequestModel.cs b/Ombi/Ombi.Core/Models/Requests/Tv/TvRequestModel.cs index f3c8a28d1..d7a6c997e 100644 --- a/Ombi/Ombi.Core/Models/Requests/Tv/TvRequestModel.cs +++ b/Ombi/Ombi.Core/Models/Requests/Tv/TvRequestModel.cs @@ -12,7 +12,7 @@ namespace Ombi.Core.Models.Requests public string ImdbId { get; set; } public string TvDbId { get; set; } - public bool RequestAll { get; set; } + public bool RequestAll { get; set; } public List SeasonsNumbersRequested { get; set; } public List Episodes { get; set; } diff --git a/Ombi/Ombi.DependencyInjection/IocExtensions.cs b/Ombi/Ombi.DependencyInjection/IocExtensions.cs index 9f6fc7751..0217b11cc 100644 --- a/Ombi/Ombi.DependencyInjection/IocExtensions.cs +++ b/Ombi/Ombi.DependencyInjection/IocExtensions.cs @@ -46,7 +46,8 @@ namespace Ombi.DependencyInjection public static IServiceCollection RegisterEngines(this IServiceCollection services) { services.AddTransient(); - services.AddTransient(); + services.AddTransient(); + services.AddTransient(); services.AddTransient(); return services; } diff --git a/Ombi/Ombi/Controllers/RequestController.cs b/Ombi/Ombi/Controllers/RequestController.cs index 6315af805..eb41fc4eb 100644 --- a/Ombi/Ombi/Controllers/RequestController.cs +++ b/Ombi/Ombi/Controllers/RequestController.cs @@ -11,49 +11,77 @@ namespace Ombi.Controllers [Authorize] public class RequestController : BaseV1ApiController { - public RequestController(IRequestEngine engine) + public RequestController(IMovieRequestEngine engine, ITvRequestEngine tvRequestEngine) { - RequestEngine = engine; + MovieRequestEngine = engine; + TvRequestEngine = tvRequestEngine; } - private IRequestEngine RequestEngine { get; } - + private IMovieRequestEngine MovieRequestEngine { get; } + private ITvRequestEngine TvRequestEngine { get; } - [HttpGet("movie/{count:int}/{position:int}", Name = "GetRequestsByCount")] + + [HttpGet("movie/{count:int}/{position:int}")] public async Task> GetRequests(int count, int position) { - return await RequestEngine.GetMovieRequests(count, position); + return await MovieRequestEngine.GetMovieRequests(count, position); } [HttpPost("movie")] public async Task RequestMovie([FromBody]SearchMovieViewModel movie) { - return await RequestEngine.RequestMovie(movie); + return await MovieRequestEngine.RequestMovie(movie); } - //[HttpPost("tv")] - //public async Task RequestTv([FromBody]SearchTvShowViewModel tv) - //{ - // return await RequestEngine.RequestMovie(); - //} - [HttpGet("movie/search/{searchTerm}")] public async Task> Search(string searchTerm) { - - return await RequestEngine.SearchMovieRequest(searchTerm); + + return await MovieRequestEngine.SearchMovieRequest(searchTerm); } [HttpDelete("movie/{requestId:int}")] public async Task DeleteRequest(int requestId) { - await RequestEngine.RemoveMovieRequest(requestId); + await MovieRequestEngine.RemoveMovieRequest(requestId); } [HttpPut("movie")] public async Task UpdateRequest([FromBody]MovieRequestModel model) { - return await RequestEngine.UpdateMovieRequest(model); + return await MovieRequestEngine.UpdateMovieRequest(model); + } + + [HttpGet("tv/{count:int}/{position:int}")] + public async Task> GetTvRequests(int count, int position) + { + return await TvRequestEngine.GetTvRequests(count, position); + } + + [HttpPost("tv")] + public async Task RequestTv([FromBody]SearchTvShowViewModel tv) + { + return await TvRequestEngine.RequestTvShow(tv); + } + + + [HttpGet("tv/search/{searchTerm}")] + public async Task> SearchTv(string searchTerm) + { + + return await TvRequestEngine.SearchTvRequest(searchTerm); + } + + [HttpDelete("tv/{requestId:int}")] + public async Task DeleteTvRequest(int requestId) + { + await TvRequestEngine.RemoveTvRequest(requestId); + } + + [HttpPut("tv")] + public async Task UpdateRequest([FromBody]TvRequestModel model) + { + return await TvRequestEngine.UpdateTvRequest(model); } } } diff --git a/Ombi/Ombi/Ombi.csproj b/Ombi/Ombi/Ombi.csproj index c9c451623..ca485e117 100644 --- a/Ombi/Ombi/Ombi.csproj +++ b/Ombi/Ombi/Ombi.csproj @@ -55,6 +55,28 @@ PreserveNewest + + PreserveNewest + + + tvrequests.component.ts + + + movierequests - Copy.component.ts + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/Ombi/Ombi/wwwroot/app/app.module.ts b/Ombi/Ombi/wwwroot/app/app.module.ts index 10ecb633f..10b74e9e4 100644 --- a/Ombi/Ombi/wwwroot/app/app.module.ts +++ b/Ombi/Ombi/wwwroot/app/app.module.ts @@ -11,10 +11,16 @@ import { HttpModule } from '@angular/http'; import { InfiniteScrollModule } from 'ngx-infinite-scroll' // Components +// Search import { SearchComponent } from './search/search.component'; import { MovieSearchComponent } from './search/moviesearch.component'; import { TvSearchComponent } from './search/tvsearch.component'; + +// Request import { RequestComponent } from './requests/request.component'; +import { MovieRequestsComponent } from './requests/movierequests.component'; +import { TvRequestsComponent } from './requests/tvrequests.component'; + import { LoginComponent } from './login/login.component'; import { LandingPageComponent } from './landingpage/landingpage.component'; import { UserManagementComponent } from './usermanagement/usermanagement.component'; @@ -76,7 +82,9 @@ const routes: Routes = [ MovieSearchComponent, TvSearchComponent, LandingPageComponent, - UserManagementComponent + UserManagementComponent, + MovieRequestsComponent, + TvRequestsComponent ], providers: [ SearchService, diff --git a/Ombi/Ombi/wwwroot/app/requests/movierequests.component.html b/Ombi/Ombi/wwwroot/app/requests/movierequests.component.html new file mode 100644 index 000000000..767c18ac7 --- /dev/null +++ b/Ombi/Ombi/wwwroot/app/requests/movierequests.component.html @@ -0,0 +1,178 @@ +
+
+ +
+
+
+ +
+ + +
+ + +
+
+ + poster + poster + +
+ +
+ +
+
+ Status: + {{request.status}} +
+ +
+ Request status: + Available + Processing Request + Request Denied + + Pending Approval + +
+
+ Denied: + +
+ + +
Release Date: {{request.releaseDate | date}}
+
+ + +
Requested By: {{user}}
+ +
Requested Date: {{request.requestedDate | date}}
+ +
+
+
+
+
+ +
+ + + +
+ + + +
+ + + + + + + +
+
+ + +
+ + + +
+
+
+
+
+ +
+ +
+ + +
+ + + + +
+ + + + + +
+
+
+ + + + +
+
diff --git a/Ombi/Ombi/wwwroot/app/requests/movierequests.component.ts b/Ombi/Ombi/wwwroot/app/requests/movierequests.component.ts new file mode 100644 index 000000000..b39878468 --- /dev/null +++ b/Ombi/Ombi/wwwroot/app/requests/movierequests.component.ts @@ -0,0 +1,109 @@ +import { Component, OnInit } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/map'; + + +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/map'; + +import { RequestService } from '../services/request.service'; +import { IdentityService } from '../services/identity.service'; + +import { IMovieRequestModel } from '../interfaces/IRequestModel'; + +@Component({ + selector: 'movie-requests', + moduleId: module.id, + templateUrl: './movierequests.component.html' +}) +export class MovieRequestsComponent implements OnInit { + constructor(private requestService: RequestService, private identityService: IdentityService) { + this.searchChanged + .debounceTime(600) // Wait Xms afterthe last event before emitting last event + .distinctUntilChanged() // only emit if value is different from previous value + .subscribe(x => { + this.searchText = x as string; + if (this.searchText === "") { + this.resetSearch(); + return; + } + this.requestService.searchMovieRequests(this.searchText).subscribe(m => this.movieRequests = m); + }); + } + + movieRequests: IMovieRequestModel[]; + + searchChanged: Subject = new Subject(); + searchText: string; + + isAdmin : boolean; + + private currentlyLoaded: number; + private amountToLoad : number; + + ngOnInit() { + this.amountToLoad = 5; + this.currentlyLoaded = 5; + this.loadInit(); + } + + + + loadMore() { + this.requestService.getMovieRequests(this.amountToLoad, this.currentlyLoaded + 1).subscribe(x => { + this.movieRequests.push.apply(this.movieRequests, x); + this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad; + }); + } + + search(text: any) { + this.searchChanged.next(text.target.value); + } + + removeRequest(request: IMovieRequestModel) { + this.requestService.removeMovieRequest(request); + this.removeRequestFromUi(request); + } + + changeAvailability(request: IMovieRequestModel, available: boolean) { + request.available = available; + + this.updateRequest(request); + } + + approve(request: IMovieRequestModel) { + request.approved = true; + request.denied = false; + this.updateRequest(request); + } + + deny(request: IMovieRequestModel) { + request.approved = false; + request.denied = true; + this.updateRequest(request); + } + + private updateRequest(request: IMovieRequestModel) { + this.requestService.updateMovieRequest(request).subscribe(x => request = x); + } + + private loadInit() { + this.requestService.getMovieRequests(this.amountToLoad, 0).subscribe(x => this.movieRequests = x); + this.isAdmin = this.identityService.hasRole("Admin"); + } + + private resetSearch() { + this.currentlyLoaded = 5; + this.loadInit(); + } + + private removeRequestFromUi(key: IMovieRequestModel) { + var index = this.movieRequests.indexOf(key, 0); + if (index > -1) { + this.movieRequests.splice(index, 1); + } + } +} \ No newline at end of file diff --git a/Ombi/Ombi/wwwroot/app/requests/request.component.html b/Ombi/Ombi/wwwroot/app/requests/request.component.html index 3f1a6a92c..8ec06c196 100644 --- a/Ombi/Ombi/wwwroot/app/requests/request.component.html +++ b/Ombi/Ombi/wwwroot/app/requests/request.component.html @@ -1,180 +1,25 @@ 

Requests

Below you can see yours and all other requests, as well as their download and approval status.

-
-
- -
-
-
- -
- - -
- - -
-
- - poster - poster - -
- -
- -
-
- Status: - {{request.status}} -
- -
- Request status: - Available - Processing Request - Request Denied - - Pending Approval - -
-
- Denied: - -
- - -
Release Date: {{request.releaseDate | date}}
-
- - -
Requested By: {{user}}
-
Requested Date: {{request.requestedDate | date}}
- -
-
-
-
-
- -
- - - -
- - - -
- - - - - - - -
-
- - -
- - - -
-
-
-
-
- -
- -
- - -
+ + +
+
+ +
+
+ +
- - - - -
-
-
- - - - -
-
diff --git a/Ombi/Ombi/wwwroot/app/requests/request.component.ts b/Ombi/Ombi/wwwroot/app/requests/request.component.ts index 5bab0207e..36129d7c5 100644 --- a/Ombi/Ombi/wwwroot/app/requests/request.component.ts +++ b/Ombi/Ombi/wwwroot/app/requests/request.component.ts @@ -1,110 +1,18 @@ -import { Component, OnInit } from '@angular/core'; -import { Subject } from 'rxjs/Subject'; -import 'rxjs/add/operator/debounceTime'; -import 'rxjs/add/operator/distinctUntilChanged'; -import 'rxjs/add/operator/map'; - - -import 'rxjs/add/operator/debounceTime'; -import 'rxjs/add/operator/distinctUntilChanged'; -import 'rxjs/add/operator/map'; - -import { RequestService } from '../services/request.service'; -import { IdentityService } from '../services/identity.service'; - -import { IMovieRequestModel, ITvRequestModel } from '../interfaces/IRequestModel'; +import { Component } from '@angular/core'; @Component({ selector: 'ombi', moduleId: module.id, templateUrl: './request.component.html' }) -export class RequestComponent implements OnInit { - constructor(private requestService: RequestService, private identityService: IdentityService) { - this.searchChanged - .debounceTime(600) // Wait Xms afterthe last event before emitting last event - .distinctUntilChanged() // only emit if value is different from previous value - .subscribe(x => { - this.searchText = x as string; - if (this.searchText === "") { - this.resetSearch(); - return; - } - this.requestService.searchRequests(this.searchText).subscribe(x => this.movieRequests = x); - }); - } - - movieRequests: IMovieRequestModel[]; - tvRequests: ITvRequestModel[]; - - searchChanged: Subject = new Subject(); - searchText: string; - - isAdmin : boolean; - - private currentlyLoaded: number; - private amountToLoad : number; - - ngOnInit() { - this.amountToLoad = 5; - this.currentlyLoaded = 5; - this.loadInit(); - } - - - - loadMore() { - this.requestService.getRequests(this.amountToLoad, this.currentlyLoaded + 1).subscribe(x => { - this.movieRequests.push.apply(this.movieRequests, x); - this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad; - }); - } +export class RequestComponent { - search(text: any) { - this.searchChanged.next(text.target.value); - } - - removeRequest(request: IMovieRequestModel) { - this.requestService.removeMovieRequest(request); - this.removeRequestFromUi(request); - } - - changeAvailability(request: IMovieRequestModel, available: boolean) { - request.available = available; - - this.updateRequest(request); - } - - approve(request: IMovieRequestModel) { - request.approved = true; - request.denied = false; - this.updateRequest(request); - } + showMovie = true; + showTv = false; - deny(request: IMovieRequestModel) { - request.approved = false; - request.denied = true; - this.updateRequest(request); + selectTab() { + this.showMovie = !this.showMovie; + this.showTv = !this.showTv; } - private updateRequest(request: IMovieRequestModel) { - this.requestService.updateRequest(request).subscribe(x => request = x); - } - - private loadInit() { - this.requestService.getRequests(this.amountToLoad, 0).subscribe(x => this.movieRequests = x); - this.isAdmin = this.identityService.hasRole("Admin"); - } - - private resetSearch() { - this.currentlyLoaded = 5; - this.loadInit(); - } - - private removeRequestFromUi(key: IMovieRequestModel) { - var index = this.movieRequests.indexOf(key, 0); - if (index > -1) { - this.movieRequests.splice(index, 1); - } - } } \ No newline at end of file diff --git a/Ombi/Ombi/wwwroot/app/requests/tvrequests.component.html b/Ombi/Ombi/wwwroot/app/requests/tvrequests.component.html new file mode 100644 index 000000000..adcba5f6a --- /dev/null +++ b/Ombi/Ombi/wwwroot/app/requests/tvrequests.component.html @@ -0,0 +1,178 @@ +
+
+ +
+
+
+ +
+ + +
+ + +
+
+ + poster + poster + +
+ +
+ +
+
+ Status: + {{request.status}} +
+ +
+ Request status: + Available + Processing Request + Request Denied + + Pending Approval + +
+
+ Denied: + +
+ + +
Release Date: {{request.releaseDate | date}}
+
+ + +
Requested By: {{user}}
+ +
Requested Date: {{request.requestedDate | date}}
+ +
+
+
+
+
+ +
+ + + +
+ + + +
+ + + + + + + +
+
+ + +
+ + + +
+
+
+
+
+ +
+ +
+ + +
+ + + + +
+ + + + + +
+
+
+ + + + +
+
diff --git a/Ombi/Ombi/wwwroot/app/requests/tvrequests.component.ts b/Ombi/Ombi/wwwroot/app/requests/tvrequests.component.ts new file mode 100644 index 000000000..9949b8591 --- /dev/null +++ b/Ombi/Ombi/wwwroot/app/requests/tvrequests.component.ts @@ -0,0 +1,109 @@ +import { Component, OnInit } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/map'; + + +import 'rxjs/add/operator/debounceTime'; +import 'rxjs/add/operator/distinctUntilChanged'; +import 'rxjs/add/operator/map'; + +import { RequestService } from '../services/request.service'; +import { IdentityService } from '../services/identity.service'; + +import { ITvRequestModel } from '../interfaces/IRequestModel'; + +@Component({ + selector: 'tv-requests', + moduleId: module.id, + templateUrl: './tvrequests.component.html' +}) +export class TvRequestsComponent implements OnInit { + constructor(private requestService: RequestService, private identityService: IdentityService) { + this.searchChanged + .debounceTime(600) // Wait Xms afterthe last event before emitting last event + .distinctUntilChanged() // only emit if value is different from previous value + .subscribe(x => { + this.searchText = x as string; + if (this.searchText === "") { + this.resetSearch(); + return; + } + this.requestService.searchTvRequests(this.searchText).subscribe(m => this.tvRequests = m); + }); + } + + tvRequests: ITvRequestModel[]; + + searchChanged = new Subject(); + searchText: string; + + isAdmin : boolean; + + private currentlyLoaded: number; + private amountToLoad : number; + + ngOnInit() { + this.amountToLoad = 5; + this.currentlyLoaded = 5; + this.loadInit(); + } + + + + loadMore() { + this.requestService.getTvRequests(this.amountToLoad, this.currentlyLoaded + 1).subscribe(x => { + this.tvRequests.push.apply(this.tvRequests, x); + this.currentlyLoaded = this.currentlyLoaded + this.amountToLoad; + }); + } + + search(text: any) { + this.searchChanged.next(text.target.value); + } + + removeRequest(request: ITvRequestModel) { + this.requestService.removeTvRequest(request); + this.removeRequestFromUi(request); + } + + changeAvailability(request: ITvRequestModel, available: boolean) { + request.available = available; + + this.updateRequest(request); + } + + approve(request: ITvRequestModel) { + request.approved = true; + request.denied = false; + this.updateRequest(request); + } + + deny(request: ITvRequestModel) { + request.approved = false; + request.denied = true; + this.updateRequest(request); + } + + private updateRequest(request: ITvRequestModel) { + this.requestService.updateTvRequest(request).subscribe(x => request = x); + } + + private loadInit() { + this.requestService.getTvRequests(this.amountToLoad, 0).subscribe(x => this.tvRequests = x); + this.isAdmin = this.identityService.hasRole("Admin"); + } + + private resetSearch() { + this.currentlyLoaded = 5; + this.loadInit(); + } + + private removeRequestFromUi(key: ITvRequestModel) { + var index = this.tvRequests.indexOf(key, 0); + if (index > -1) { + this.tvRequests.splice(index, 1); + } + } +} \ No newline at end of file diff --git a/Ombi/Ombi/wwwroot/app/services/request.service.ts b/Ombi/Ombi/wwwroot/app/services/request.service.ts index a932b6f22..6e124e1b7 100644 --- a/Ombi/Ombi/wwwroot/app/services/request.service.ts +++ b/Ombi/Ombi/wwwroot/app/services/request.service.ts @@ -6,7 +6,7 @@ import { ServiceAuthHelpers } from './service.helpers'; import { IRequestEngineResult } from '../interfaces/IRequestEngineResult'; import { ISearchMovieResult } from '../interfaces/ISearchMovieResult'; import { ISearchTvResult } from '../interfaces/ISearchTvResult'; -import { IMovieRequestModel } from '../interfaces/IRequestModel'; +import { IMovieRequestModel, ITvRequestModel } from '../interfaces/IRequestModel'; @Injectable() export class RequestService extends ServiceAuthHelpers { @@ -22,11 +22,11 @@ export class RequestService extends ServiceAuthHelpers { return this.http.post(`${this.url}/TV/`, JSON.stringify(tv), { headers: this.headers }).map(this.extractData); } - getRequests(count: number, position: number): Observable { + getMovieRequests(count: number, position: number): Observable { return this.http.get(`${this.url}/movie/${count}/${position}`).map(this.extractData); } - searchRequests(search: string): Observable { + searchMovieRequests(search: string): Observable { return this.http.get(`${this.url}/movie/search/${search}`).map(this.extractData); } @@ -34,7 +34,23 @@ export class RequestService extends ServiceAuthHelpers { this.http.delete(`${this.url}/movie/${request.id}`).map(this.extractData).subscribe(); } - updateRequest(request: IMovieRequestModel): Observable { + updateMovieRequest(request: IMovieRequestModel): Observable { return this.http.post(`${this.url}/movie/`, JSON.stringify(request), { headers: this.headers }).map(this.extractData); } + + getTvRequests(count: number, position: number): Observable { + return this.http.get(`${this.url}/tv/${count}/${position}`).map(this.extractData); + } + + searchTvRequests(search: string): Observable { + return this.http.get(`${this.url}/tv/search/${search}`).map(this.extractData); + } + + removeTvRequest(request: ITvRequestModel) { + this.http.delete(`${this.url}/tv/${request.id}`).map(this.extractData).subscribe(); + } + + updateTvRequest(request: ITvRequestModel): Observable { + return this.http.post(`${this.url}/tv/`, JSON.stringify(request), { headers: this.headers }).map(this.extractData); + } } \ No newline at end of file