From c5f123b90380becee1e98a070deac0f6d33e3ac1 Mon Sep 17 00:00:00 2001 From: tidusjar Date: Tue, 23 Mar 2021 22:08:20 +0000 Subject: [PATCH] Got a bit of the work done, needs some polish then tackle tv --- src/Ombi.Core/Engine/MovieRequestEngine.cs | 30 +-- .../Models/Requests/BaseRequestOptions.cs | 39 ++++ .../Models/Requests/MovieRequestViewModel.cs | 3 +- .../Models/Requests/TvRequestViewModel.cs | 4 +- .../Rules/Search/AvailabilityRuleHelper.cs | 4 +- src/Ombi.Helpers/CacheKeys.cs | 1 + .../card/discover-card-details.component.html | 171 ------------------ .../card/discover-card-details.component.scss | 45 ----- .../card/discover-card-details.component.ts | 86 --------- .../card/discover-card.component.ts | 13 +- .../carousel-list.component.html | 2 +- .../carousel-list/carousel-list.component.ts | 1 + .../discover-collections.component.ts | 2 +- .../discover/discover.component.html | 6 +- .../components/discover/discover.component.ts | 12 +- .../grid/discover-grid.component.html | 168 ----------------- .../grid/discover-grid.component.scss | 137 -------------- .../grid/discover-grid.component.ts | 154 ---------------- .../src/app/discover/components/index.ts | 8 +- .../search-results.component.html | 2 +- .../search-results.component.ts | 6 +- .../src/app/interfaces/IRequestModel.ts | 9 +- .../src/app/interfaces/ISearchTvResult.ts | 5 +- .../movie/movie-details.component.ts | 7 +- .../movie-information-panel.component.ts | 7 +- .../tv-advanced-options.component.html | 4 +- .../tv-information-panel.component.ts | 9 +- .../components/tv/tv-details.component.html | 3 +- .../admin-request-dialog.component.html | 82 +++++++++ .../admin-request-dialog.component.scss | 8 + .../admin-request-dialog.component.ts | 124 +++++++++++++ .../ClientApp/src/app/shared/shared.module.ts | 6 +- .../user-preference.component.html | 2 +- src/Ombi/ClientApp/src/styles/Styles.scss | 4 +- src/Ombi/Controllers/V1/IdentityController.cs | 10 +- 35 files changed, 355 insertions(+), 819 deletions(-) create mode 100644 src/Ombi.Core/Models/Requests/BaseRequestOptions.cs delete mode 100644 src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.html delete mode 100644 src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.scss delete mode 100644 src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts delete mode 100644 src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.html delete mode 100644 src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.scss delete mode 100644 src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts create mode 100644 src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.html create mode 100644 src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.scss create mode 100644 src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts diff --git a/src/Ombi.Core/Engine/MovieRequestEngine.cs b/src/Ombi.Core/Engine/MovieRequestEngine.cs index c9dbde067..997702a5c 100644 --- a/src/Ombi.Core/Engine/MovieRequestEngine.cs +++ b/src/Ombi.Core/Engine/MovieRequestEngine.cs @@ -69,19 +69,25 @@ namespace Ombi.Core.Engine var userDetails = await GetUser(); var canRequestOnBehalf = false; - if (model.RequestOnBehalf.HasValue()) + var isAdmin = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin); + if (model.RequestOnBehalf.HasValue() && !isAdmin) { - canRequestOnBehalf = await UserManager.IsInRoleAsync(userDetails, OmbiRoles.PowerUser) || await UserManager.IsInRoleAsync(userDetails, OmbiRoles.Admin); + return new RequestEngineResult + { + Result = false, + Message = "You do not have the correct permissions to request on behalf of users!", + ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" + }; + } - if (!canRequestOnBehalf) + if ((model.RootFolderOverride.HasValue || model.QualityPathOverride.HasValue) && !isAdmin) + { + return new RequestEngineResult { - return new RequestEngineResult - { - Result = false, - Message = "You do not have the correct permissions to request on behalf of users!", - ErrorMessage = $"You do not have the correct permissions to request on behalf of users!" - }; - } + Result = false, + Message = "You do not have the correct permissions!", + ErrorMessage = $"You do not have the correct permissions!" + }; } var requestModel = new MovieRequests @@ -101,7 +107,9 @@ namespace Ombi.Core.Engine RequestedUserId = canRequestOnBehalf ? model.RequestOnBehalf : userDetails.Id, Background = movieInfo.BackdropPath, LangCode = model.LanguageCode, - RequestedByAlias = model.RequestedByAlias + RequestedByAlias = model.RequestedByAlias, + RootPathOverride = model.RootFolderOverride.GetValueOrDefault(), + QualityOverride = model.QualityPathOverride.GetValueOrDefault() }; var usDates = movieInfo.ReleaseDates?.Results?.FirstOrDefault(x => x.IsoCode == "US"); diff --git a/src/Ombi.Core/Models/Requests/BaseRequestOptions.cs b/src/Ombi.Core/Models/Requests/BaseRequestOptions.cs new file mode 100644 index 000000000..55868df44 --- /dev/null +++ b/src/Ombi.Core/Models/Requests/BaseRequestOptions.cs @@ -0,0 +1,39 @@ +#region Copyright +// /************************************************************************ +// Copyright (c) 2018 Jamie Rees +// File: MovieRequestViewModel.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 Newtonsoft.Json; + +namespace Ombi.Core.Models.Requests +{ + + public class BaseRequestOptions + { + public string RequestOnBehalf { get; set; } + public int? RootFolderOverride { get; set; } + public int? QualityPathOverride { get; set; } + } +} \ No newline at end of file diff --git a/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs b/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs index 22d1cc449..05eec3e3f 100644 --- a/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs +++ b/src/Ombi.Core/Models/Requests/MovieRequestViewModel.cs @@ -29,11 +29,10 @@ using Newtonsoft.Json; namespace Ombi.Core.Models.Requests { - public class MovieRequestViewModel + public class MovieRequestViewModel : BaseRequestOptions { public int TheMovieDbId { get; set; } public string LanguageCode { get; set; } = "en"; - public string RequestOnBehalf { get; set; } /// /// This is only set from a HTTP Header diff --git a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs index 95eb77735..960b30ab9 100644 --- a/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs +++ b/src/Ombi.Core/Models/Requests/TvRequestViewModel.cs @@ -20,7 +20,7 @@ namespace Ombi.Core.Models.Requests } - public class TvRequestViewModelBase + public class TvRequestViewModelBase : BaseRequestOptions { public bool RequestAll { get; set; } public bool LatestSeason { get; set; } @@ -28,7 +28,5 @@ namespace Ombi.Core.Models.Requests public List Seasons { get; set; } = new List(); [JsonIgnore] public string RequestedByAlias { get; set; } - - public string RequestOnBehalf { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs index ee1d94509..f1262623f 100644 --- a/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs +++ b/src/Ombi.Core/Rule/Rules/Search/AvailabilityRuleHelper.cs @@ -17,7 +17,7 @@ namespace Ombi.Core.Rule.Rules.Search { // If we have all the episodes for this season, then this season is available if (season.Episodes.All(x => x.Available)) - {yarn + { season.SeasonAvailable = true; } } @@ -25,7 +25,7 @@ namespace Ombi.Core.Rule.Rules.Search { search.FullyAvailable = true; } - else if (search.SeasonRequests.Any(x => x.Episodes.Any(e => e.Available))) + else if (search.SeasonRequests.Any(x => x.Episodes.Any(e => e.Available))) { search.PartlyAvailable = true; } diff --git a/src/Ombi.Helpers/CacheKeys.cs b/src/Ombi.Helpers/CacheKeys.cs index f7a40d321..89faead8a 100644 --- a/src/Ombi.Helpers/CacheKeys.cs +++ b/src/Ombi.Helpers/CacheKeys.cs @@ -21,5 +21,6 @@ namespace Ombi.Helpers public const string LidarrRootFolders = nameof(LidarrRootFolders); public const string LidarrQualityProfiles = nameof(LidarrQualityProfiles); public const string FanartTv = nameof(FanartTv); + public const string UsersDropdown = nameof(UsersDropdown); } } diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.html b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.html deleted file mode 100644 index 3ac12d570..000000000 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.html +++ /dev/null @@ -1,171 +0,0 @@ -
- -
-
- -
-
- - {{data.title}} - -
-
-
- - - -
-
-
-

{{data.title}}

-
- -
-
-
- {{'Discovery.CardDetails.Availability' | translate}}: - - - -
-
- {{'Discovery.CardDetails.Studio' | translate}}: - {{movie.productionCompanies[0].name}} - {{'Discovery.CardDetails.Network' | translate}}: - {{tv.network.name}} - {{'Discovery.CardDetails.UnknownNetwork' | translate}} -
-
- {{'Discovery.CardDetails.RequestStatus' | translate}}: - - - - - -
-
- {{'Discovery.CardDetails.Director' | translate}}: - {{movie.credits.crew[0].name}} - Director: - {{tvCreator}} -
-
- {{'Discovery.CardDetails.InCinemas' | translate}}: - {{movie.releaseDate | amLocal | amDateFormat: 'LL'}} - {{'Discovery.CardDetails.FirstAired' | translate}}: - {{tv.firstAired | amLocal | amDateFormat: 'LL'}} -
-
- {{'Discovery.CardDetails.Writer' | translate}}: - {{movie.credits.crew[1].name}} - {{'Discovery.CardDetails.ExecProducer' | translate}}: - {{tvProducer}} -
-
- -
-
- {{data.overview}} -
-
- - -
- -
-
-
-
-
- - - - - - - - - - -
- -
- - - - - - - - {{'Search.ViewOnPlex' | - translate}} - {{'Search.ViewOnEmby' | - translate}} - {{'Search.ViewOnJellyfin' | - translate}} - - -
-
- -
diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.scss b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.scss deleted file mode 100644 index e7fe00c60..000000000 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.scss +++ /dev/null @@ -1,45 +0,0 @@ -@import "~styles/variables.scss"; -.poster { - max-width: 100%; - border-radius: 2%; -} - -.details { - padding: 2%; - border-radius: 10px; - background: $backgroundTint; - div.dark & { - background: $backgroundTint-dark; - } -} - -.details strong { - font-weight: bold; -} - - -h3 strong { - font-weight: bold; -} - -.action-buttons-right { - width: 100%; - text-align: right; -} - -.btn-spacing { - margin-right: 1%; -} - -.media-icons { - color: $primary; - padding: 2%; - div.dark & { - color: $warn-dark; - } -} - -.overview { - height:300px; - overflow-y: auto; -} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts deleted file mode 100644 index 804e0e9ec..000000000 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card-details.component.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { Component, Inject, OnInit, ViewEncapsulation } from "@angular/core"; -import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from "@angular/material/dialog"; -import { IDiscoverCardResult } from "../../interfaces"; -import { SearchV2Service, RequestService, MessageService } from "../../../services"; -import { RequestType } from "../../../interfaces"; -import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; -import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; -import { Router } from "@angular/router"; -import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component"; - -@Component({ - selector: "discover-card-details", - templateUrl: "./discover-card-details.component.html", - styleUrls: ["./discover-card-details.component.scss"], - encapsulation: ViewEncapsulation.None, -}) -export class DiscoverCardDetailsComponent implements OnInit { - - public movie: ISearchMovieResultV2; - public tv: ISearchTvResultV2; - public tvCreator: string; - public tvProducer: string; - public loading: boolean; - public RequestType = RequestType; - - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: IDiscoverCardResult, private searchService: SearchV2Service, private dialog: MatDialog, - private requestService: RequestService, public messageService: MessageService, private router: Router) { } - - public async ngOnInit() { - this.loading = true; - if (this.data.type === RequestType.movie) { - this.movie = await this.searchService.getFullMovieDetailsPromise(+this.data.id); - } else if (this.data.type === RequestType.tvShow) { - this.tv = await this.searchService.getTvInfo(+this.data.id); - const creator = this.tv.crew.filter(tv => { - return tv.type === "Creator"; - })[0]; - if (creator && creator.person) { - this.tvCreator = creator.person.name; - } - const crewResult = this.tv.crew.filter(tv => { - return tv.type === "Executive Producer"; - })[0] - if (crewResult && crewResult.person) { - this.tvProducer = crewResult.person.name; - } - } - this.loading = false; - } - - public close(): void { - this.dialogRef.close(); - } - - public openDetails() { - if (this.data.type === RequestType.movie) { - this.router.navigate(['/details/movie/', this.data.id]); - } else if (this.data.type === RequestType.tvShow) { - this.router.navigate(['/details/tv/', this.data.id]); - } - - this.dialogRef.close(); - } - - public async request() { - this.loading = true; - if (this.data.type === RequestType.movie) { - const result = await this.requestService.requestMovie({ theMovieDbId: +this.data.id, languageCode: "", requestOnBehalf: null }).toPromise(); - this.loading = false; - - if (result.result) { - this.movie.requested = true; - this.messageService.send(result.message, "Ok"); - } else { - this.messageService.send(result.errorMessage, "Ok"); - } - } else if (this.data.type === RequestType.tvShow) { - this.dialog.open(EpisodeRequestComponent, { width: "700px", data: {series: this.tv }, panelClass: 'modal-panel' }) - } - this.loading = false; - - this.dialogRef.close(); - } -} diff --git a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts index 9ca9cfae6..3ea3f75bf 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/card/discover-card.component.ts @@ -3,10 +3,10 @@ import { IDiscoverCardResult } from "../../interfaces"; import { RequestType } from "../../../interfaces"; import { MessageService, RequestService, SearchV2Service } from "../../../services"; import { MatDialog } from "@angular/material/dialog"; -import { DiscoverCardDetailsComponent } from "./discover-card-details.component"; import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; import { EpisodeRequestComponent } from "../../../shared/episode-request/episode-request.component"; +import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component"; @Component({ selector: "discover-card", @@ -16,6 +16,7 @@ import { EpisodeRequestComponent } from "../../../shared/episode-request/episode export class DiscoverCardComponent implements OnInit { @Input() public result: IDiscoverCardResult; + @Input() public isAdmin: boolean; public RequestType = RequestType; public hide: boolean; public fullyLoaded = false; @@ -40,10 +41,6 @@ export class DiscoverCardComponent implements OnInit { } } - public openDetails(details: IDiscoverCardResult) { - this.dialog.open(DiscoverCardDetailsComponent, { width: "700px", data: details, panelClass: 'modal-panel' }) - } - public async getExtraTvInfo() { // if (this.result.tvMovieDb) { this.tvSearchResult = await this.searchService.getTvInfoWithMovieDbId(+this.result.id); @@ -125,7 +122,10 @@ export class DiscoverCardComponent implements OnInit { dia.afterClosed().subscribe(x => this.loading = false); return; case RequestType.movie: - this.requestService.requestMovie({ theMovieDbId: +this.result.id, languageCode: null, requestOnBehalf: null }).subscribe(x => { + if (this.isAdmin) { + this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.result.id }, panelClass: 'modal-panel' }); + } else { + this.requestService.requestMovie({ theMovieDbId: +this.result.id, languageCode: null, requestOnBehalf: null, qualityPathOverride: null, rootFolderOverride: null }).subscribe(x => { if (x.result) { this.result.requested = true; this.messageService.send(x.message, "Ok"); @@ -135,6 +135,7 @@ export class DiscoverCardComponent implements OnInit { this.loading = false; }); return; + } } } diff --git a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html index 71fa3ea63..adff68d23 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.html @@ -8,6 +8,6 @@ - + \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts index ca8dfd44f..cfe3ef6dd 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/carousel-list/carousel-list.component.ts @@ -22,6 +22,7 @@ export class CarouselListComponent implements OnInit { @Input() public discoverType: DiscoverType; @Input() public id: string; + @Input() public isAdmin: boolean; @ViewChild('carousel', {static: false}) carousel: Carousel; public DiscoverOption = DiscoverOption; diff --git a/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts b/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts index dd634ef92..9d1193c58 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/collections/discover-collections.component.ts @@ -34,7 +34,7 @@ export class DiscoverCollectionsComponent implements OnInit { public async requestCollection() { await this.collection.collection.forEach(async (movie) => { - await this.requestService.requestMovie({theMovieDbId: movie.id, languageCode: null, requestOnBehalf: null}).toPromise(); + await this.requestService.requestMovie({theMovieDbId: movie.id, languageCode: null, requestOnBehalf: null, qualityPathOverride: null, rootFolderOverride: null}).toPromise(); }); this.messageService.send("Requested Collection"); } diff --git a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html index fd114128e..701a6b204 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/discover/discover.component.html @@ -2,7 +2,7 @@

{{'Discovery.PopularTab' | translate}}

- +
@@ -10,7 +10,7 @@

{{'Discovery.TrendingTab' | translate}}

- +
@@ -18,7 +18,7 @@

{{'Discovery.UpcomingTab' | translate}}

- +
- - -
- -
-
- {{result.title}} -
-
-
-

{{result.title}}

-
-
- - - {{'Common.Available' | translate}} - - - - {{'Common.ProcessingRequest' | translate}} - - - - {{'Common.RequestDenied' | translate}} - - - - {{'Common.PendingApproval' | translate}} - - - - play_circle_outline - - - - play_circle_outline - - - - play_circle_outline - - - - - play_circle_outline - - - - play_circle_outline - - - - play_circle_outline - - -
-
- - - - {{'Discovery.CardDetails.Studio' | translate}}: {{movie.productionCompanies[0].name}} - - - {{'Discovery.CardDetails.Network' | translate}}: - {{tv.network.name}} - - - {{'Discovery.CardDetails.Director' | translate}}: {{movie.credits.crew[0].name}} - - Director: {{tvCreator}} - - {{'Discovery.CardDetails.InCinemas' | translate}}: - {{movie.releaseDate | amLocal | amDateFormat: 'LL'}} - - {{'Discovery.CardDetails.FirstAired' | translate}}: - {{tv.firstAired | amLocal | amDateFormat: 'LL'}} - - - {{'Discovery.CardDetails.Writer' | translate}}: {{movie.credits.crew[1].name}} - - {{'Discovery.CardDetails.ExecProducer' | translate}}: {{tvProducer}} - - - - - - -
-
-

{{result.overview}}

-
-
- -
-
- -
- - - - - - - - - - -
- -
- - - - - - - - {{'Search.ViewOnPlex' | - translate}} - {{'Search.ViewOnEmby' | - translate}} - {{'Search.ViewOnJellyfin' | - translate}} - -
-
-
-
-
-
diff --git a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.scss b/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.scss deleted file mode 100644 index caecaddb0..000000000 --- a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.scss +++ /dev/null @@ -1,137 +0,0 @@ -$ombi-primary:#3f3f3f; -$card-background: #2b2b2b; - -$blue: #1976D2; -$pink: #C2185B; -$green:#1DE9B6; -$orange:#F57C00; - -.btn-blue { - background-color: $blue; -} - -.btn-pink { - background-color: $pink; -} - -.btn-green { - background-color: $green; -} - -.btn-orange { - background-color: $orange; -} - -.btn-spacing { - margin-top:10%; -} - -#cardImage { - border-radius: 5px 5px 0px 0px; - height: 75%; -} - -.dark-card { - border-radius: 8px; -} - -// Changed height to 100% to make all cards the same height -.top-spacing { - margin-top: 1%; -} - -.card-poster { - width: 100%; - border-radius: 8px 0px 0px 8px; - margin-top: -6.5%; - margin-bottom: -6.6%; -} - -.main-container { - margin-left: -2%; -} - - -.rating { - position: absolute; - font-weight: bold; -} - -$border-width: 3px; - -.available { - background-color: #1DE9B6 !important; - color: black !important; -} - -.approved { - background-color: #ff5722 !important; -} - -.requested { - background-color: #ffd740 !important; - color: black !important; -} - -.denied { - background-color: #C2185B !important; -} - -.notrequested { - background-color: #303030 !important; -} - -.expand { - text-align: center; -} - -@media (min-width: 1025px) { - - // Changed height to 100% to make all cards the same height - .grow { - transition: all .2s ease-in-out; - height: 100%; - } - - .grow:hover { - transform: scale(1.1); - } -} - -::ng-deep mat-dialog-container.mat-dialog-container { - // background-color: $ombi-primary; - // color: white; - border-radius: 2% -} - - -/* Title adjust for the Discover page */ -.mat-card-content h6 { - overflow: hidden; - white-space: nowrap; - font-weight: 400; - font-size: 1.1rem; -} - -/* Summary adjust for Discover page */ -.small, -small { - font-size: 0.8rem; -} - -@media (min-width: 2000px) { - #cardImage { - height: 80%; - object-fit: cover; - display: block; - } -} - -.overview { - font-size: 1.2em; -} - -.backdrop { - background-position: 50% 33%; - background-size: cover; -} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts b/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts deleted file mode 100644 index 51ea7a85f..000000000 --- a/src/Ombi/ClientApp/src/app/discover/components/grid/discover-grid.component.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { Component, OnInit, Input } from "@angular/core"; -import { IDiscoverCardResult } from "../../interfaces"; -import { RequestType, ISearchTvResult, ISearchMovieResult, ISearchMovieResultContainer } from "../../../interfaces"; -import { ImageService, RequestService, SearchV2Service } from "../../../services"; -import { MatDialog } from "@angular/material/dialog"; -import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; -import { ISearchMovieResultV2 } from "../../../interfaces/ISearchMovieResultV2"; -import { EpisodeRequestComponent, EpisodeRequestData } from "../../../shared/episode-request/episode-request.component"; -import { MatSnackBar } from "@angular/material/snack-bar"; -import { Router } from "@angular/router"; -import { DomSanitizer } from "@angular/platform-browser"; - -@Component({ - selector: "discover-grid", - templateUrl: "./discover-grid.component.html", - styleUrls: ["./discover-grid.component.scss"], -}) -export class DiscoverGridComponent implements OnInit { - - @Input() public result: IDiscoverCardResult; - public RequestType = RequestType; - public requesting: boolean; - - public tv: ISearchTvResultV2; - public tvCreator: string; - public tvProducer: string; - public movie: ISearchMovieResultV2; - - constructor(private searchService: SearchV2Service, private dialog: MatDialog, - private requestService: RequestService, private notification: MatSnackBar, - private router: Router, private sanitizer: DomSanitizer, private imageService: ImageService) { } - - public ngOnInit() { - if (this.result.type == RequestType.tvShow) { - this.getExtraTvInfo(); - } - if (this.result.type == RequestType.movie) { - this.getExtraMovieInfo(); - } - } - - public async getExtraTvInfo() { - this.tv = await this.searchService.getTvInfo(+this.result.id); - this.setTvDefaults(this.tv); - this.updateTvItem(this.tv); - const creator = this.tv.crew.filter(tv => { - return tv.type === "Creator"; - })[0]; - if (creator && creator.person) { - this.tvCreator = creator.person.name; - } - const crewResult = this.tv.crew.filter(tv => { - return tv.type === "Executive Producer"; - })[0] - if (crewResult && crewResult.person) { - this.tvProducer = crewResult.person.name; - } - this.setTvBackground(); - } - - public openDetails() { - if (this.result.type === RequestType.movie) { - this.router.navigate(['/details/movie/', this.result.id]); - } else if (this.result.type === RequestType.tvShow) { - this.router.navigate(['/details/tv/', this.result.id]); - } - } - - public getStatusClass(): string { - if (this.result.available) { - return "available"; - } - if (this.result.approved) { - return "approved"; - } - if (this.result.requested) { - return "requested"; - } - return "notrequested"; - } - - private getExtraMovieInfo() { - this.searchService.getFullMovieDetails(+this.result.id) - .subscribe(m => { - this.movie = m; - this.updateMovieItem(m); - }); - - this.setMovieBackground() - } - - private setMovieBackground(): void { - this.result.background = this.sanitizer.bypassSecurityTrustStyle - ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(" + "https://image.tmdb.org/t/p/original" + this.result.background + ")"); - } - - private setTvBackground(): void { - if (this.result.background != null) { - this.result.background = this.sanitizer.bypassSecurityTrustStyle - ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(https://image.tmdb.org/t/p/original" + this.result.background + ")"); - } else { - this.imageService.getTvBanner(+this.result.id).subscribe(x => { - if (x) { - this.result.background = this.sanitizer.bypassSecurityTrustStyle - ("linear-gradient( rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5) ), url(" + x + ")"); - } - }); - } - } - - private updateMovieItem(updated: ISearchMovieResultV2) { - this.result.url = "http://www.imdb.com/title/" + updated.imdbId + "/"; - this.result.available = updated.available; - this.result.requested = updated.requested; - this.result.requested = updated.requestProcessing; - this.result.rating = updated.voteAverage; - } - - - private setTvDefaults(x: ISearchTvResultV2) { - if (!x.imdbId) { - x.imdbId = "https://www.tvmaze.com/shows/" + x.seriesId; - } else { - x.imdbId = "http://www.imdb.com/title/" + x.imdbId + "/"; - } - } - - private updateTvItem(updated: ISearchTvResultV2) { - this.result.title = updated.title; - this.result.id = updated.id; - this.result.available = updated.fullyAvailable; - this.result.posterPath = updated.banner; - this.result.requested = updated.requested; - this.result.url = updated.imdbId; - } - - public async request() { - this.requesting = true; - if (this.result.type === RequestType.movie) { - const result = await this.requestService.requestMovie({ theMovieDbId: +this.result.id, languageCode: "", requestOnBehalf: null }).toPromise(); - - if (result.result) { - this.result.requested = true; - this.notification.open(result.message, "Ok"); - } else { - this.notification.open(result.errorMessage, "Ok"); - } - } else if (this.result.type === RequestType.tvShow) { - this.dialog.open(EpisodeRequestComponent, { width: "700px", data: { series: this.tv, requestOnBehalf: null }, panelClass: 'modal-panel' }) - } - this.requesting = false; - } - -} diff --git a/src/Ombi/ClientApp/src/app/discover/components/index.ts b/src/Ombi/ClientApp/src/app/discover/components/index.ts index e3d59fe7b..2d399cd76 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/index.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/index.ts @@ -1,13 +1,11 @@ import { DiscoverComponent } from "./discover/discover.component"; -import { DiscoverCardDetailsComponent } from "./card/discover-card-details.component"; import { DiscoverCollectionsComponent } from "./collections/discover-collections.component"; import { DiscoverActorComponent } from "./actor/discover-actor.component"; import { DiscoverCardComponent } from "./card/discover-card.component"; import { Routes } from "@angular/router"; import { AuthGuard } from "../../auth/auth.guard"; -import { SearchService, RequestService } from "../../services"; +import { SearchService, RequestService, SonarrService, RadarrService } from "../../services"; import { MatDialog } from "@angular/material/dialog"; -import { DiscoverGridComponent } from "./grid/discover-grid.component"; import { DiscoverSearchResultsComponent } from "./search-results/search-results.component"; import { CarouselListComponent } from "./carousel-list/carousel-list.component"; import { RequestServiceV2 } from "../../services/requestV2.service"; @@ -16,10 +14,8 @@ import { RequestServiceV2 } from "../../services/requestV2.service"; export const components: any[] = [ DiscoverComponent, DiscoverCardComponent, - DiscoverCardDetailsComponent, DiscoverCollectionsComponent, DiscoverActorComponent, - DiscoverGridComponent, DiscoverSearchResultsComponent, CarouselListComponent, ]; @@ -29,6 +25,8 @@ export const providers: any[] = [ MatDialog, RequestService, RequestServiceV2, + SonarrService, + RadarrService, ]; export const routes: Routes = [ diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html index fb836a105..29a583890 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.html @@ -4,7 +4,7 @@
- +
diff --git a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts index 6637b9e07..d9e046da9 100644 --- a/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts +++ b/src/Ombi/ClientApp/src/app/discover/components/search-results/search-results.component.ts @@ -8,6 +8,7 @@ import { SearchFilter } from "../../../my-nav/SearchFilter"; import { StorageService } from "../../../shared/storage/storage-service"; import { isEqual } from "lodash"; +import { AuthService } from "../../../auth/auth.service"; @Component({ templateUrl: "./search-results.component.html", @@ -18,6 +19,7 @@ export class DiscoverSearchResultsComponent implements OnInit { public loadingFlag: boolean; public searchTerm: string; public results: IMultiSearchResult[]; + public isAdmin: boolean; public discoverResults: IDiscoverCardResult[] = []; @@ -26,7 +28,8 @@ export class DiscoverSearchResultsComponent implements OnInit { constructor(private searchService: SearchV2Service, private route: ActivatedRoute, private filterService: FilterService, - private store: StorageService) { + private store: StorageService, + private authService: AuthService) { this.route.params.subscribe((params: any) => { this.searchTerm = params.searchTerm; this.clear(); @@ -36,6 +39,7 @@ export class DiscoverSearchResultsComponent implements OnInit { public async ngOnInit() { this.loadingFlag = true; + this.isAdmin = this.authService.isAdmin(); this.filterService.onFilterChange.subscribe(async x => { if (!isEqual(this.filter, x)) { diff --git a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts index 495563c36..4f72b0631 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/IRequestModel.ts @@ -168,10 +168,9 @@ export interface IEpisodesRequests { selected: boolean; // This is for the UI only } -export interface IMovieRequestModel { +export interface IMovieRequestModel extends BaseRequestOptions { theMovieDbId: number; languageCode: string | undefined; - requestOnBehalf: string | undefined; } export interface IFilter { @@ -187,3 +186,9 @@ export enum FilterType { Processing = 4, PendingApproval = 5, } + +export class BaseRequestOptions { + requestOnBehalf: string | undefined; + rootFolderOverride: number | undefined; + qualityPathOverride: number | undefined; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts index 2a5b3742e..b59ad3780 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISearchTvResult.ts @@ -1,4 +1,4 @@ -import { INewSeasonRequests } from "./IRequestModel"; +import { BaseRequestOptions, INewSeasonRequests } from "./IRequestModel"; export interface ISearchTvResult { id: number; @@ -47,12 +47,11 @@ export interface ITvRequestViewModelV2 extends ITvRequestViewModelBase { } -export interface ITvRequestViewModelBase { +export interface ITvRequestViewModelBase extends BaseRequestOptions { requestAll: boolean; firstSeason: boolean; latestSeason: boolean; seasons: ISeasonsViewModel[]; - requestOnBehalf: string | undefined; } export interface ISeasonsViewModel { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts index b493edae1..143c85085 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/movie-details.component.ts @@ -13,6 +13,7 @@ import { MovieAdvancedOptionsComponent } from "./panels/movie-advanced-options/m import { RequestServiceV2 } from "../../../services/requestV2.service"; import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; import { forkJoin } from "rxjs"; +import { AdminRequestDialogComponent } from "../../../shared/admin-request-dialog/admin-request-dialog.component"; @Component({ templateUrl: "./movie-details.component.html", @@ -84,7 +85,10 @@ export class MovieDetailsComponent { } public async request(userId?: string) { - const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null, requestOnBehalf: userId }).toPromise(); + if (this.isAdmin) { + this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id }, panelClass: 'modal-panel' }); + } else { + const result = await this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, languageCode: null, requestOnBehalf: userId, qualityPathOverride: 0, rootFolderOverride: 0 }).toPromise(); if (result.result) { this.movie.requested = true; this.messageService.send(result.message, "Ok"); @@ -92,6 +96,7 @@ export class MovieDetailsComponent { this.messageService.send(result.errorMessage, "Ok"); } } + } public openDialog() { this.dialog.open(YoutubeTrailerComponent, { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts index 89d9c076c..334dee65e 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/movie/panels/movie-information-panel.component.ts @@ -13,7 +13,7 @@ import { IStreamingData } from "../../../../interfaces/IStreams"; }) export class MovieInformationPanelComponent implements OnInit { - constructor(private searchService: SearchV2Service, @Inject(APP_BASE_HREF) public baseUrl: string) { } + constructor(private searchService: SearchV2Service, @Inject(APP_BASE_HREF) public internalBaseUrl: string) { } @Input() public movie: ISearchMovieResultV2; @Input() public request: IMovieRequests; @@ -22,7 +22,12 @@ export class MovieInformationPanelComponent implements OnInit { public ratings: IMovieRatings; public streams: IStreamingData[]; + public baseUrl: string; + public ngOnInit() { + if (this.internalBaseUrl.length > 1) { + this.baseUrl = this.internalBaseUrl; + } this.searchService.getRottenMovieRatings(this.movie.title, +this.movie.releaseDate.toString().substring(0,4)) .subscribe(x => this.ratings = x); diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html index 5724a35bf..14e6629f1 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-advanced-options/tv-advanced-options.component.html @@ -18,6 +18,6 @@
- - + +
\ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts index d6096d6cd..701aa26e9 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-information-panel/tv-information-panel.component.ts @@ -1,4 +1,5 @@ -import { Component, ViewEncapsulation, Input, OnInit } from "@angular/core"; +import { APP_BASE_HREF } from "@angular/common"; +import { Component, ViewEncapsulation, Input, OnInit, Inject } from "@angular/core"; import { ITvRequests } from "../../../../../interfaces"; import { ITvRatings } from "../../../../../interfaces/IRatings"; import { ISearchTvResultV2 } from "../../../../../interfaces/ISearchTvResultV2"; @@ -13,7 +14,7 @@ import { SearchV2Service } from "../../../../../services"; }) export class TvInformationPanelComponent implements OnInit { - constructor(private searchService: SearchV2Service) { } + constructor(private searchService: SearchV2Service, @Inject(APP_BASE_HREF) public internalBaseUrl: string) { } @Input() public tv: ISearchTvResultV2; @Input() public request: ITvRequests; @@ -24,8 +25,12 @@ export class TvInformationPanelComponent implements OnInit { public seasonCount: number; public totalEpisodes: number = 0; public nextEpisode: any; + public baseUrl: string; public ngOnInit(): void { + if (this.internalBaseUrl.length > 1) { + this.baseUrl = this.internalBaseUrl; + } this.searchService.getRottenTvRatings(this.tv.title, +this.tv.firstAired.toString().substring(0,4)) .subscribe(x => this.ratings = x); diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html index e186340be..65dcece80 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.html @@ -53,9 +53,10 @@ (click)="request()"> {{ 'Common.Request' | translate }} - + diff --git a/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.html b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.html new file mode 100644 index 000000000..8eb9d1ec9 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.html @@ -0,0 +1,82 @@ + + +
+

Advanced Options

+ + +
+ +
+ + +

{{'MediaDetails.RequestOnBehalf' | translate }}

+ + {{ 'MediaDetails.PleaseSelectUser' | translate}} + + + + {{displayFn(option)}} + + + + + + +
+ + +
+
+

Sonarr Overrides

+ + {{'MediaDetails.QualityProfilesSelect' | translate }} + + {{profile.name}} + + +
+
+ + {{'MediaDetails.RootFolderSelect' | translate }} + + {{profile.path}} + + +
+
+ + + +
+
+

Radarr Overrides

+ + {{'MediaDetails.QualityProfilesSelect' | translate }} + + {{profile.name}} + + +
+
+ + {{'MediaDetails.RootFolderSelect' | translate }} + + {{profile.path}} + + +
+
+ + + +
+ + +
+
+ diff --git a/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.scss b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.scss new file mode 100644 index 000000000..8d701c21f --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.scss @@ -0,0 +1,8 @@ + +@import "~styles/variables.scss"; + +.alert-info { + background: $accent; + border-color: $ombi-background-primary; + color:white; +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts new file mode 100644 index 000000000..217aad80b --- /dev/null +++ b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts @@ -0,0 +1,124 @@ +import { Component, Inject, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup } from "@angular/forms"; +import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; +import { Observable } from "rxjs"; +import { startWith, map } from "rxjs/operators"; +import { IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUserDropdown, RequestType } from "../../interfaces"; +import { IdentityService, MessageService, RadarrService, RequestService, SonarrService } from "../../services"; +import { RequestServiceV2 } from "../../services/requestV2.service"; + +export interface IAdminRequestDialogData { + type: RequestType, + id: number +} + +@Component({ + selector: "admin-request-dialog", + templateUrl: "admin-request-dialog.component.html", + styleUrls: [ "admin-request-dialog.component.scss" ] +}) +export class AdminRequestDialogComponent implements OnInit { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: IAdminRequestDialogData, + private requestServiceV2: RequestServiceV2, + private notificationService: MessageService, + private identityService: IdentityService, + private sonarrService: SonarrService, + private radarrService: RadarrService, + private requestService: RequestService, + private fb: FormBuilder + ) {} + + public form: FormGroup; + public RequestType = RequestType; + + public options: IUserDropdown[]; + public filteredOptions: Observable; + public userId: string; + + public radarrEnabled: boolean; + + + public sonarrProfiles: ISonarrProfile[]; + public sonarrRootFolders: ISonarrRootFolder[]; + public radarrProfiles: IRadarrProfile[]; + public radarrRootFolders: IRadarrRootFolder[]; + + public async ngOnInit() { + + this.form = this.fb.group({ + username: [null], + sonarrPathId: [null], + sonarrFolderId: [null], + radarrPathId: [null], + radarrFolderId: [null] + }) + + this.options = await this.identityService.getUsersDropdown().toPromise(); + + this.filteredOptions = this.form.controls['username'].valueChanges.pipe( + startWith(""), + map((value) => this._filter(value)) + ); + + if (this.data.type === RequestType.tvShow) { + this.sonarrService.getQualityProfilesWithoutSettings().subscribe(c => { + this.sonarrProfiles = c; + }); + this.sonarrService.getRootFoldersWithoutSettings().subscribe(c => { + this.sonarrRootFolders = c; + }); + } + if (this.data.type === RequestType.movie) { + this.radarrEnabled = await this.radarrService.isRadarrEnabled(); + if (this.radarrEnabled) { + this.radarrService.getQualityProfilesFromSettings().subscribe(c => { + this.radarrProfiles = c; + }); + this.radarrService.getRootFoldersFromSettings().subscribe(c => { + this.radarrRootFolders = c; + }); + } + } + } + + public displayFn(user: IUserDropdown): string { + const username = user?.username ? user.username : ""; + const email = user?.email ? `(${user.email})` : ""; + return `${username} ${email}`; + } + + private _filter(value: string | IUserDropdown): IUserDropdown[] { + const filterValue = + typeof value === "string" + ? value.toLowerCase() + : value.username.toLowerCase(); + + return this.options.filter((option) => + option.username.toLowerCase().includes(filterValue) + ); + } + + public async submitRequest() { + const model = this.form.value; + if (this.data.type === RequestType.movie) { + this.requestService.requestMovie({ + qualityPathOverride: model.radarrPathId, + requestOnBehalf: model.username?.id, + rootFolderOverride: model.radarrFolderId, + theMovieDbId: this.data.id, + languageCode: null + }).subscribe((x) => { + if (x.result) { + this.notificationService.send(x.message, "Ok"); + } else { + this.notificationService.send(x.errorMessage, "Ok"); + } + + this.dialogRef.close(); + }) + } + + } +} diff --git a/src/Ombi/ClientApp/src/app/shared/shared.module.ts b/src/Ombi/ClientApp/src/app/shared/shared.module.ts index 634561b7f..1189320dd 100644 --- a/src/Ombi/ClientApp/src/app/shared/shared.module.ts +++ b/src/Ombi/ClientApp/src/app/shared/shared.module.ts @@ -1,6 +1,6 @@ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { TranslateModule } from "@ngx-translate/core"; import { TruncateModule } from "@yellowspot/ng-truncate"; import { MomentModule } from "ngx-moment"; @@ -37,15 +37,18 @@ import { MatSlideToggleModule } from "@angular/material/slide-toggle"; import { MatTabsModule } from "@angular/material/tabs"; import { EpisodeRequestComponent } from "./episode-request/episode-request.component"; import { DetailsGroupComponent } from "../issues/components/details-group/details-group.component"; +import { AdminRequestDialogComponent } from "./admin-request-dialog/admin-request-dialog.component"; @NgModule({ declarations: [ IssuesReportComponent, EpisodeRequestComponent, DetailsGroupComponent, + AdminRequestDialogComponent, ], imports: [ SidebarModule, + ReactiveFormsModule, FormsModule, CommonModule, InputSwitchModule, @@ -85,6 +88,7 @@ import { DetailsGroupComponent } from "../issues/components/details-group/detail MatProgressSpinnerModule, IssuesReportComponent, EpisodeRequestComponent, + AdminRequestDialogComponent, DetailsGroupComponent, TruncateModule, InputSwitchModule, diff --git a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html index 2a3203f68..e4c7758f6 100644 --- a/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html +++ b/src/Ombi/ClientApp/src/app/user-preferences/components/user-preference/user-preference.component.html @@ -114,7 +114,7 @@ - Coming Soon... + diff --git a/src/Ombi/ClientApp/src/styles/Styles.scss b/src/Ombi/ClientApp/src/styles/Styles.scss index 55bc8657c..a02ddf5ca 100644 --- a/src/Ombi/ClientApp/src/styles/Styles.scss +++ b/src/Ombi/ClientApp/src/styles/Styles.scss @@ -142,8 +142,8 @@ background-color: $ombi-active; } - hr{ - border-top: 1px solid $ombi-background-primary; + hr { + border-top: 1px solid $accent-dark; } .form-control{ diff --git a/src/Ombi/Controllers/V1/IdentityController.cs b/src/Ombi/Controllers/V1/IdentityController.cs index b57a6e890..bf1173d70 100644 --- a/src/Ombi/Controllers/V1/IdentityController.cs +++ b/src/Ombi/Controllers/V1/IdentityController.cs @@ -67,7 +67,8 @@ namespace Ombi.Controllers.V1 IMovieRequestEngine movieRequestEngine, ITvRequestEngine tvRequestEngine, IMusicRequestEngine musicEngine, - IUserDeletionEngine deletionEngine) + IUserDeletionEngine deletionEngine, + ICacheService cacheService) { UserManager = user; Mapper = mapper; @@ -95,10 +96,13 @@ namespace Ombi.Controllers.V1 _userQualityProfiles = userProfiles; MusicRequestEngine = musicEngine; _deletionEngine = deletionEngine; + _cacheService = cacheService; } private OmbiUserManager UserManager { get; } private readonly IUserDeletionEngine _deletionEngine; + private readonly ICacheService _cacheService; + private RoleManager RoleManager { get; } private IMapper Mapper { get; } private IEmailProvider EmailProvider { get; } @@ -289,8 +293,8 @@ namespace Ombi.Controllers.V1 [PowerUser] public async Task> GetAllUsersDropdown() { - var users = await UserManager.Users.Where(x => x.UserType != UserType.SystemUser) - .ToListAsync(); + var users = await _cacheService.GetOrAdd(CacheKeys.UsersDropdown, + async () => await UserManager.Users.Where(x => x.UserType != UserType.SystemUser).ToListAsync()); var model = new List();