diff --git a/src/Ombi.Api.Radarr/IRadarrV3Api.cs b/src/Ombi.Api.Radarr/IRadarrV3Api.cs index 072e8ef51..0b91b4925 100644 --- a/src/Ombi.Api.Radarr/IRadarrV3Api.cs +++ b/src/Ombi.Api.Radarr/IRadarrV3Api.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading.Tasks; using Ombi.Api.Radarr.Models; using Ombi.Api.Radarr.Models.V3; @@ -14,7 +15,8 @@ namespace Ombi.Api.Radarr Task GetMovie(int id, string apiKey, string baseUrl); Task UpdateMovie(MovieResponse movie, string apiKey, string baseUrl); Task MovieSearch(int[] movieIds, string apiKey, string baseUrl); - Task AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability); + Task AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath,string apiKey, string baseUrl, bool searchNow, string minimumAvailability, List tags); Task> GetTags(string apiKey, string baseUrl); + Task CreateTag(string apiKey, string baseUrl, string tagName); } } \ No newline at end of file diff --git a/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs b/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs index 09e985f43..9efea8ee1 100644 --- a/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs +++ b/src/Ombi.Api.Radarr/Models/V2/RadarrAddMovie.cs @@ -29,5 +29,6 @@ namespace Ombi.Api.Radarr.Models public int year { get; set; } public string minimumAvailability { get; set; } public long sizeOnDisk { get; set; } + public int[] tags { get; set; } } } \ No newline at end of file diff --git a/src/Ombi.Api.Radarr/RadarrV3Api.cs b/src/Ombi.Api.Radarr/RadarrV3Api.cs index 9e7e0f4c2..76656be65 100644 --- a/src/Ombi.Api.Radarr/RadarrV3Api.cs +++ b/src/Ombi.Api.Radarr/RadarrV3Api.cs @@ -72,7 +72,7 @@ namespace Ombi.Api.Radarr return await Api.Request(request); } - public async Task AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability) + public async Task AddMovie(int tmdbId, string title, int year, int qualityId, string rootPath, string apiKey, string baseUrl, bool searchNow, string minimumAvailability, List tags) { var request = new Request("/api/v3/movie", baseUrl, HttpMethod.Post); @@ -86,7 +86,8 @@ namespace Ombi.Api.Radarr monitored = true, year = year, minimumAvailability = minimumAvailability, - sizeOnDisk = 0 + sizeOnDisk = 0, + tags = tags.Any() ? tags.ToArray() : Enumerable.Empty().ToArray() }; if (searchNow) @@ -156,5 +157,14 @@ namespace Ombi.Api.Radarr { request.AddHeader("X-Api-Key", key); } + + public Task CreateTag(string apiKey, string baseUrl, string tagName) + { + var request = new Request($"/api/v3/tag", baseUrl, HttpMethod.Post); + request.AddHeader("X-Api-Key", apiKey); + request.AddJsonBody(new { Label = tagName }); + + return Api.Request(request); + } } } diff --git a/src/Ombi.Core/Senders/MovieSender.cs b/src/Ombi.Core/Senders/MovieSender.cs index 36d40bdad..f6907e2f5 100644 --- a/src/Ombi.Core/Senders/MovieSender.cs +++ b/src/Ombi.Core/Senders/MovieSender.cs @@ -15,6 +15,8 @@ using Ombi.Store.Entities; using Ombi.Store.Repository; using System.Collections.Generic; using Ombi.Api.Radarr.Models; +using Microsoft.Extensions.Options; +using Ombi.Api.Sonarr; namespace Ombi.Core.Senders { @@ -67,7 +69,7 @@ namespace Ombi.Core.Senders } if (radarrSettings.Enabled) { - return await SendToRadarr(model, is4K, radarrSettings); + return await SendToRadarr(model, radarrSettings); } var dogSettings = await _dogNzbSettings.GetSettingsAsync(); @@ -131,7 +133,7 @@ namespace Ombi.Core.Senders return await _dogNzbApi.AddMovie(settings.ApiKey, id); } - private async Task SendToRadarr(MovieRequests model, bool is4K, RadarrSettings settings) + private async Task SendToRadarr(MovieRequests model, RadarrSettings settings) { var qualityToUse = int.Parse(settings.DefaultQualityProfile); @@ -154,6 +156,17 @@ namespace Ombi.Core.Senders } } + var tags = new List(); + if (settings.Tag.HasValue) + { + tags.Add(settings.Tag.Value); + } + if (settings.SendUserTags) + { + var userTag = await GetOrCreateTag(model, settings); + tags.Add(userTag.id); + } + // Overrides on the request take priority if (model.QualityOverride > 0) { @@ -174,7 +187,7 @@ namespace Ombi.Core.Senders { var result = await _radarrV3Api.AddMovie(model.TheMovieDbId, model.Title, model.ReleaseDate.Year, qualityToUse, rootFolderPath, settings.ApiKey, settings.FullUri, !settings.AddOnly, - settings.MinimumAvailability); + settings.MinimumAvailability, tags); if (!string.IsNullOrEmpty(result.Error?.message)) { @@ -212,5 +225,17 @@ namespace Ombi.Core.Senders var selectedPath = paths.FirstOrDefault(x => x.id == overrideId); return selectedPath?.path ?? string.Empty; } + + private async Task GetOrCreateTag(MovieRequests model, RadarrSettings s) + { + var tagName = model.RequestedUser.UserName; + // Does tag exist? + + var allTags = await _radarrV3Api.GetTags(s.ApiKey, s.FullUri); + var existingTag = allTags.FirstOrDefault(x => x.label.Equals(tagName, StringComparison.InvariantCultureIgnoreCase)); + existingTag ??= await _radarrV3Api.CreateTag(s.ApiKey, s.FullUri, tagName); + + return existingTag; + } } } \ No newline at end of file diff --git a/src/Ombi.Settings/Settings/Models/External/RadarrSettings.cs b/src/Ombi.Settings/Settings/Models/External/RadarrSettings.cs index 1b3e0982f..1791761c5 100644 --- a/src/Ombi.Settings/Settings/Models/External/RadarrSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/RadarrSettings.cs @@ -9,6 +9,8 @@ public bool AddOnly { get; set; } public string MinimumAvailability { get; set; } public bool ScanForAvailability { get; set; } + public int? Tag { get; set; } + public bool SendUserTags { get; set; } } public class Radarr4KSettings : RadarrSettings diff --git a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs index 8e0b37524..332d97357 100644 --- a/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs +++ b/src/Ombi.Settings/Settings/Models/External/SonarrSettings.cs @@ -19,11 +19,11 @@ public string RootPathAnime { get; set; } public int? AnimeTag { get; set; } public int? Tag { get; set; } + public bool SendUserTags { get; set; } public bool AddOnly { get; set; } public int LanguageProfile { get; set; } public int LanguageProfileAnime { get; set; } public bool ScanForAvailability { get; set; } - public bool SendUserTags { get; set; } } } \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index 8eebbb6f9..7e12d7b9b 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -14,6 +14,7 @@ import { BrowserModule } from "@angular/platform-browser"; import { ButtonModule } from "primeng/button"; import { CUSTOMIZATION_INITIALIZER } from "./state/customization/customization-initializer"; import { SONARR_INITIALIZER } from "./state/sonarr/sonarr-initializer"; +import { RADARR_INITIALIZER } from "./state/radarr/radarr-initializer"; import { ConfirmDialogModule } from "primeng/confirmdialog"; import { CookieComponent } from "./auth/cookie.component"; import { CookieService } from "ng2-cookies"; @@ -24,6 +25,7 @@ import { DialogModule } from "primeng/dialog"; import { FEATURES_INITIALIZER } from "./state/features/features-initializer"; import { FeatureState } from "./state/features"; import { SonarrSettingsState } from "./state/sonarr"; +import { RadarrSettingsState } from "./state/radarr"; import { JwtModule } from "@auth0/angular-jwt"; import { LandingPageComponent } from "./landingpage/landingpage.component"; import { LandingPageService } from "./services"; @@ -162,7 +164,7 @@ export function JwtTokenGetter() { }), SidebarModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, LayoutModule, MatSlideToggleModule, - NgxsModule.forRoot([CustomizationState, FeatureState, SonarrSettingsState], { + NgxsModule.forRoot([CustomizationState, FeatureState, SonarrSettingsState, RadarrSettingsState], { developmentMode: !environment.production, }), ...environment.production ? [] : @@ -211,6 +213,7 @@ export function JwtTokenGetter() { FEATURES_INITIALIZER, SONARR_INITIALIZER, CUSTOMIZATION_INITIALIZER, + RADARR_INITIALIZER, { provide: APP_BASE_HREF, useValue: window["baseHref"] 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 c7348e91c..f524c168a 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 @@ -157,7 +157,7 @@ export class DiscoverCardComponent implements OnInit { AdminRequestDialogComponent, { width: "700px", - data: { type: RequestType.movie, id: this.result.id }, + data: { type: RequestType.movie, id: this.result.id, is4k: is4k }, panelClass: "modal-panel", } ); diff --git a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts index cd7956fdd..759cdeb73 100644 --- a/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts +++ b/src/Ombi/ClientApp/src/app/interfaces/ISettings.ts @@ -160,6 +160,8 @@ export interface IRadarrSettings extends IExternalSettings { addOnly: boolean; minimumAvailability: string; scanForAvailability: boolean; + tag: number | null; + sendUserTags: boolean; } export interface IRadarrCombined { diff --git a/src/Ombi/ClientApp/src/app/login/login.component.ts b/src/Ombi/ClientApp/src/app/login/login.component.ts index f2aacdae4..0e0c2a836 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.ts +++ b/src/Ombi/ClientApp/src/app/login/login.component.ts @@ -6,14 +6,13 @@ import { TranslateService } from "@ngx-translate/core"; import { APP_BASE_HREF } from "@angular/common"; import { AuthService } from "../auth/auth.service"; import { IAuthenticationSettings, ICustomizationSettings } from "../interfaces"; -import { PlexTvService } from "../services"; -import { SettingsService } from "../services"; -import { StatusService } from "../services"; +import { PlexTvService, StatusService, SettingsService } from "../services"; import { StorageService } from "../shared/storage/storage-service"; import { MatSnackBar } from "@angular/material/snack-bar"; import { CustomizationFacade } from "../state/customization"; import { SonarrFacade } from "app/state/sonarr"; +import { RadarrFacade } from "app/state/radarr"; @Component({ templateUrl: "./login.component.html", @@ -62,6 +61,7 @@ export class LoginComponent implements OnDestroy, OnInit { private plexTv: PlexTvService, private store: StorageService, private sonarrFacade: SonarrFacade, + private radarrFacade: RadarrFacade, private readonly notify: MatSnackBar ) { this.href = href; @@ -89,7 +89,7 @@ export class LoginComponent implements OnDestroy, OnInit { }); if (authService.loggedIn()) { - this.router.navigate(["/"]); + this.loadStores(); } } @@ -144,7 +144,7 @@ export class LoginComponent implements OnDestroy, OnInit { if (this.authService.loggedIn()) { this.ngOnDestroy(); - this.sonarrFacade.load().subscribe(); + this.loadStores(); this.router.navigate(["/"]); } else { this.notify.open(this.errorBody, "OK", { @@ -221,7 +221,7 @@ export class LoginComponent implements OnDestroy, OnInit { this.oAuthWindow.close(); } this.oauthLoading = false; - this.sonarrFacade.load().subscribe(); + this.loadStores(); this.router.navigate(["search"]); return; } @@ -252,7 +252,7 @@ export class LoginComponent implements OnDestroy, OnInit { if (this.authService.loggedIn()) { this.ngOnDestroy(); - this.sonarrFacade.load().subscribe(); + this.loadStores(); this.router.navigate(["/"]); } else { this.notify.open(this.errorBody, "OK", { @@ -274,4 +274,9 @@ export class LoginComponent implements OnDestroy, OnInit { public ngOnDestroy() { clearInterval(this.pinTimer); } + + private loadStores() { + this.sonarrFacade.load().subscribe(); + this.radarrFacade.load().subscribe(); + } } 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 e40945ebb..5d5de3ee4 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 @@ -75,7 +75,7 @@ export class MovieDetailsComponent implements OnInit{ this.isAdmin = this.auth.hasRole("admin") || this.auth.hasRole("poweruser"); if (this.isAdmin) { - this.showAdvanced = await this.radarrService.isRadarrEnabled(); + this.showAdvanced = await firstValueFrom(this.radarrService.isRadarrEnabled()); } if (this.imdbId) { @@ -111,7 +111,7 @@ export class MovieDetailsComponent implements OnInit{ is4K = false; } if (this.isAdmin) { - const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id }, panelClass: 'modal-panel' }); + const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.movie, id: this.movie.id, is4K: is4K }, panelClass: 'modal-panel' }); dialog.afterClosed().subscribe(async (result) => { if (result) { const requestResult = await firstValueFrom(this.requestService.requestMovie({ theMovieDbId: this.theMovidDbId, diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts index bac88756e..24bbbe77b 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/panels/tv-request-grid/tv-request-grid.component.ts @@ -62,7 +62,7 @@ export class TvRequestGridComponent { }); if (this.isAdmin) { - const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.tvShow, id: this.tv.id }, panelClass: 'modal-panel' }); + const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.tvShow, id: this.tv.id, is4k: null }, panelClass: 'modal-panel' }); dialog.afterClosed().subscribe(async (result) => { if (result) { viewModel.requestOnBehalf = result.username?.id; diff --git a/src/Ombi/ClientApp/src/app/services/applications/radarr.service.ts b/src/Ombi/ClientApp/src/app/services/applications/radarr.service.ts index eaa785233..711607f8e 100644 --- a/src/Ombi/ClientApp/src/app/services/applications/radarr.service.ts +++ b/src/Ombi/ClientApp/src/app/services/applications/radarr.service.ts @@ -3,7 +3,7 @@ import { HttpClient } from "@angular/common/http"; import { Injectable, Inject } from "@angular/core"; import { Observable } from "rxjs"; -import { IRadarrProfile, IRadarrRootFolder } from "../../interfaces"; +import { IRadarrProfile, IRadarrRootFolder, ITag } from "../../interfaces"; import { IRadarrSettings } from "../../interfaces"; import { ServiceHelpers } from "../service.helpers"; @@ -23,10 +23,28 @@ export class RadarrService extends ServiceHelpers { public getRootFoldersFromSettings(): Observable { return this.http.get(`${this.url}/RootFolders/`, { headers: this.headers }); } + public getQualityProfilesFromSettings(): Observable { return this.http.get(`${this.url}/Profiles/`, { headers: this.headers }); } - public isRadarrEnabled(): Promise { - return this.http.get(`${this.url}/enabled/`, { headers: this.headers }).toPromise(); + + public getRootFolders4kFromSettings(): Observable { + return this.http.get(`${this.url}/RootFolders/4k`, { headers: this.headers }); + } + + public getQualityProfiles4kFromSettings(): Observable { + return this.http.get(`${this.url}/Profiles/4k`, { headers: this.headers }); + } + + public isRadarrEnabled(): Observable { + return this.http.get(`${this.url}/enabled/`, { headers: this.headers }); + } + + public getTagsWithSettings(settings: IRadarrSettings): Observable { + return this.http.post(`${this.url}/tags/`, JSON.stringify(settings), { headers: this.headers }); + } + + public getTags(): Observable { + return this.http.get(`${this.url}/tags/`, { headers: this.headers }) } } diff --git a/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.html b/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.html index 2dbf6869d..b3264b109 100644 --- a/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.html +++ b/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.html @@ -8,6 +8,10 @@
Scan for Availability
+
+ Add the user as a tag +
This will add the username of the requesting user as a tag in Sonarr. If the tag doesn't exist, Ombi will create it.
+
Do not search for Movies @@ -79,6 +83,22 @@
+
+
+ +
+
+ + Tag + + + {{tag.label}} + + + + +
+
Default Minimum Availability diff --git a/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.ts b/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.ts index 1dbceead8..a4f12760f 100644 --- a/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/radarr/components/radarr-form.component.ts @@ -1,7 +1,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core"; import { ControlContainer, UntypedFormGroup, Validators } from "@angular/forms"; +import { finalize, map } from "rxjs"; -import { IMinimumAvailability, IRadarrProfile, IRadarrRootFolder, IRadarrSettings } from "../../../interfaces"; +import { IMinimumAvailability, IRadarrProfile, IRadarrRootFolder, IRadarrSettings, ITag } from "../../../interfaces"; import { TesterService, NotificationService, RadarrService } from "../../../services"; @@ -16,8 +17,10 @@ export class RadarrFormComponent implements OnInit { public qualities: IRadarrProfile[]; public rootFolders: IRadarrRootFolder[]; public minimumAvailabilityOptions: IMinimumAvailability[]; + public tags: ITag[]; public profilesRunning: boolean; public rootFoldersRunning: boolean; + public tagsRunning: boolean; public form: UntypedFormGroup; constructor(private radarrService: RadarrService, @@ -34,6 +37,10 @@ export class RadarrFormComponent implements OnInit { this.rootFolders = []; this.rootFolders.push({ path: "Please Select", id: -1 }); + + this.tags = []; + this.tags.push({ label: "None", id: -1 }); + this.minimumAvailabilityOptions = [ { name: "Announced", value: "Announced" }, { name: "In Cinemas", value: "InCinemas" }, @@ -47,9 +54,16 @@ export class RadarrFormComponent implements OnInit { if (this.form.controls.defaultRootPath.value) { this.getRootFolders(this.form); } + + if (this.form.controls.tag.value) { + this.getTags(this.form); + } + + this.toggleValidators(); } public toggleValidators() { + debugger; const enabled = this.form.controls.enabled.value as boolean; this.form.controls.apiKey.setValidators(enabled ? [Validators.required] : null); this.form.controls.defaultQualityProfile.setValidators(enabled ? [Validators.required] : null); @@ -81,6 +95,20 @@ export class RadarrFormComponent implements OnInit { }); } + public getTags(form: UntypedFormGroup) { + this.tagsRunning = true; + this.radarrService.getTagsWithSettings(form.value).pipe( + finalize(() => { + this.tagsRunning = false; + this.tags.unshift({ label: "None", id: -1 }); + this.notificationService.success("Successfully retrieved the Tags"); + }), + map(result => { + this.tags = result; + }) + ).subscribe() + } + public test(form: UntypedFormGroup) { if (form.invalid) { this.notificationService.error("Please check your entered values"); diff --git a/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts b/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts index 2a6443074..869de4856 100644 --- a/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/radarr/radarr.component.ts @@ -1,8 +1,9 @@ -import { Component, OnInit, QueryList, ViewChild, ViewChildren } from "@angular/core"; +import { Component, OnInit, QueryList, ViewChildren } from "@angular/core"; import { UntypedFormBuilder, UntypedFormGroup } from "@angular/forms"; +import { RadarrFacade } from "app/state/radarr"; import { IMinimumAvailability, IRadarrCombined, IRadarrProfile, IRadarrRootFolder } from "../../interfaces"; -import { NotificationService, SettingsService } from "../../services"; +import { NotificationService } from "../../services"; import { FeaturesFacade } from "../../state/features/features.facade"; import { RadarrFormComponent } from "./components/radarr-form.component"; @@ -23,7 +24,7 @@ export class RadarrComponent implements OnInit { @ViewChildren('4kForm') public form4k: QueryList; @ViewChildren('normalForm') public normalForm: QueryList; - constructor(private settingsService: SettingsService, + constructor(private radarrFacade: RadarrFacade, private notificationService: NotificationService, private featureFacade: FeaturesFacade, private fb: UntypedFormBuilder) { } @@ -31,34 +32,38 @@ export class RadarrComponent implements OnInit { public ngOnInit() { this.is4kEnabled = this.featureFacade.is4kEnabled(); - this.settingsService.getRadarr() + this.radarrFacade.state$() .subscribe(x => { this.form = this.fb.group({ radarr: this.fb.group({ - enabled: [x.radarr.enabled], - apiKey: [x.radarr.apiKey], - defaultQualityProfile: [+x.radarr.defaultQualityProfile], - defaultRootPath: [x.radarr.defaultRootPath], - ssl: [x.radarr.ssl], - subDir: [x.radarr.subDir], - ip: [x.radarr.ip], - port: [x.radarr.port], - addOnly: [x.radarr.addOnly], - minimumAvailability: [x.radarr.minimumAvailability], - scanForAvailability: [x.radarr.scanForAvailability] + enabled: [x.settings.radarr.enabled], + apiKey: [x.settings.radarr.apiKey], + defaultQualityProfile: [+x.settings.radarr.defaultQualityProfile], + defaultRootPath: [x.settings.radarr.defaultRootPath], + tag: [x.settings.radarr.tag], + sendUserTags: [x.settings.radarr.sendUserTags], + ssl: [x.settings.radarr.ssl], + subDir: [x.settings.radarr.subDir], + ip: [x.settings.radarr.ip], + port: [x.settings.radarr.port], + addOnly: [x.settings.radarr.addOnly], + minimumAvailability: [x.settings.radarr.minimumAvailability], + scanForAvailability: [x.settings.radarr.scanForAvailability] }), radarr4K: this.fb.group({ - enabled: [x.radarr4K.enabled], - apiKey: [x.radarr4K.apiKey], - defaultQualityProfile: [+x.radarr4K.defaultQualityProfile], - defaultRootPath: [x.radarr4K.defaultRootPath], - ssl: [x.radarr4K.ssl], - subDir: [x.radarr4K.subDir], - ip: [x.radarr4K.ip], - port: [x.radarr4K.port], - addOnly: [x.radarr4K.addOnly], - minimumAvailability: [x.radarr4K.minimumAvailability], - scanForAvailability: [x.radarr4K.scanForAvailability] + enabled: [x.settings.radarr4K.enabled], + apiKey: [x.settings.radarr4K.apiKey], + defaultQualityProfile: [+x.settings.radarr4K.defaultQualityProfile], + defaultRootPath: [x.settings.radarr4K.defaultRootPath], + tag: [x.settings.radarr4K.tag], + sendUserTags: [x.settings.radarr4K.sendUserTags], + ssl: [x.settings.radarr4K.ssl], + subDir: [x.settings.radarr4K.subDir], + ip: [x.settings.radarr4K.ip], + port: [x.settings.radarr4K.port], + addOnly: [x.settings.radarr4K.addOnly], + minimumAvailability: [x.settings.radarr4K.minimumAvailability], + scanForAvailability: [x.settings.radarr4K.scanForAvailability] }), }); this.normalForm.changes.forEach((comp => { @@ -70,7 +75,6 @@ export class RadarrComponent implements OnInit { })) } }); - } @@ -82,17 +86,26 @@ export class RadarrComponent implements OnInit { const radarrForm = form.controls.radarr as UntypedFormGroup; const radarr4KForm = form.controls.radarr4K as UntypedFormGroup; - if (radarrForm.controls.enabled.value && (radarrForm.controls.defaultQualityProfile.value === -1 || radarrForm.controls.defaultRootPath.value === "Please Select")) { + if (radarrForm.controls.enabled.value && (radarrForm.controls.defaultQualityProfile.value === -1 + || radarrForm.controls.defaultRootPath.value === "Please Select")) { this.notificationService.error("Please check your entered values for Radarr"); return; } - if (radarr4KForm.controls.enabled.value && (radarr4KForm.controls.defaultQualityProfile.value === -1 || radarr4KForm.controls.defaultRootPath.value === "Please Select")) { + if (radarr4KForm.controls.enabled.value && (radarr4KForm.controls.defaultQualityProfile.value === -1 + || radarr4KForm.controls.defaultRootPath.value === "Please Select")) { this.notificationService.error("Please check your entered values for Radarr 4K"); return; } + if (radarr4KForm.controls.tag.value === -1) { + radarr4KForm.controls.tag.setValue(null); + } + if (radarrForm.controls.tag.value === -1) { + radarr4KForm.controls.tag.setValue(null); + } + const settings = form.value; - this.settingsService.saveRadarr(settings).subscribe(x => { + this.radarrFacade.updateSettings(settings).subscribe(x => { if (x) { this.notificationService.success("Successfully saved Radarr settings"); } else { diff --git a/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html b/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html index fc85de648..27d1b6053 100644 --- a/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html +++ b/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html @@ -138,7 +138,7 @@
- Default Tag + Tag {{tag.label}} 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 index 9cc059361..93c7fc5a7 100644 --- 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 @@ -65,25 +65,25 @@

-
-

Radarr 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.ts b/src/Ombi/ClientApp/src/app/shared/admin-request-dialog/admin-request-dialog.component.ts index d0e99b19f..b93fe66f7 100644 --- 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 @@ -1,124 +1,143 @@ -import { Component, Inject, OnInit } from "@angular/core"; -import { UntypedFormBuilder, UntypedFormGroup } from "@angular/forms"; -import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog"; -import { SonarrFacade } from "app/state/sonarr"; -import { firstValueFrom, Observable } from "rxjs"; -import { startWith, map } from "rxjs/operators"; -import { ILanguageProfiles, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUserDropdown, RequestType } from "../../interfaces"; -import { IdentityService, RadarrService, SonarrService } from "../../services"; +import { Component, Inject, OnInit } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { RadarrFacade } from 'app/state/radarr'; +import { SonarrFacade } from 'app/state/sonarr'; +import { firstValueFrom, Observable } from 'rxjs'; +import { startWith, map } from 'rxjs/operators'; +import { + ILanguageProfiles, + IRadarrProfile, + IRadarrRootFolder, + ISonarrProfile, + ISonarrRootFolder, + IUserDropdown, + RequestType, +} from '../../interfaces'; +import { IdentityService, RadarrService, SonarrService } from '../../services'; export interface IAdminRequestDialogData { - type: RequestType, - id: number + type: RequestType; + id: number; + is4k: boolean | null; } @Component({ - selector: "admin-request-dialog", - templateUrl: "admin-request-dialog.component.html", - styleUrls: [ "admin-request-dialog.component.scss" ] + 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 identityService: IdentityService, - private sonarrService: SonarrService, - private radarrService: RadarrService, - private fb: UntypedFormBuilder, - private sonarrFacade: SonarrFacade - ) {} + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: IAdminRequestDialogData, + private identityService: IdentityService, + private sonarrService: SonarrService, + private radarrService: RadarrService, + private fb: UntypedFormBuilder, + private sonarrFacade: SonarrFacade, + private radarrFacade: RadarrFacade, + ) {} - public form: UntypedFormGroup; - public RequestType = RequestType; + public form: UntypedFormGroup; + public RequestType = RequestType; - public options: IUserDropdown[]; - public filteredOptions: Observable; - public userId: string; + public options: IUserDropdown[]; + public filteredOptions: Observable; + public userId: string; - public radarrEnabled: boolean; - public sonarrEnabled: boolean; + public radarrEnabled: boolean; + public radarr4kEnabled: boolean; + public sonarrEnabled: boolean; - public sonarrProfiles: ISonarrProfile[]; - public sonarrRootFolders: ISonarrRootFolder[]; - public sonarrLanguageProfiles: ILanguageProfiles[]; - public radarrProfiles: IRadarrProfile[]; - public radarrRootFolders: IRadarrRootFolder[]; + public sonarrProfiles: ISonarrProfile[]; + public sonarrRootFolders: ISonarrRootFolder[]; + public sonarrLanguageProfiles: ILanguageProfiles[]; + public radarrProfiles: IRadarrProfile[]; + public radarrRootFolders: IRadarrRootFolder[]; - public async ngOnInit() { + public async ngOnInit() { + this.form = this.fb.group({ + username: [null], + sonarrPathId: [null], + sonarrFolderId: [null], + sonarrLanguageId: [null], + radarrPathId: [null], + radarrFolderId: [null], + }); - this.form = this.fb.group({ - username: [null], - sonarrPathId: [null], - sonarrFolderId: [null], - sonarrLanguageId: [null], - radarrPathId: [null], - radarrFolderId: [null] - }) + this.options = await firstValueFrom(this.identityService.getUsersDropdown()); - this.options = await firstValueFrom(this.identityService.getUsersDropdown()); + this.filteredOptions = this.form.controls['username'].valueChanges.pipe( + startWith(''), + map((value) => this._filter(value)), + ); - this.filteredOptions = this.form.controls['username'].valueChanges.pipe( - startWith(""), - map((value) => this._filter(value)) - ); + if (this.data.type === RequestType.tvShow) { + this.sonarrEnabled = this.sonarrFacade.isEnabled(); + if (this.sonarrEnabled) { + console.log(this.sonarrFacade.version()); + if (this.sonarrFacade.version()[0] === '3') { + this.sonarrService.getV3LanguageProfilesWithoutSettings().subscribe((profiles: ILanguageProfiles[]) => { + this.sonarrLanguageProfiles = profiles; + }); + } + this.sonarrService.getQualityProfilesWithoutSettings().subscribe((c) => { + this.sonarrProfiles = c; + }); + this.sonarrService.getRootFoldersWithoutSettings().subscribe((c) => { + this.sonarrRootFolders = c; + }); + } + } + if (this.data.type === RequestType.movie) { + this.radarrEnabled = this.radarrFacade.isEnabled(); + this.radarr4kEnabled = this.radarrFacade.is4KEnabled(); - if (this.data.type === RequestType.tvShow) { - this.sonarrEnabled = this.sonarrFacade.isEnabled(); - if (this.sonarrEnabled) { - console.log(this.sonarrFacade.version()); - if (this.sonarrFacade.version()[0] === "3") { - this.sonarrService.getV3LanguageProfilesWithoutSettings().subscribe((profiles: ILanguageProfiles[]) => { - this.sonarrLanguageProfiles = profiles; - }) - } - 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; - }); - } - } - } + if (this.data.is4k ?? false) { + if (this.radarr4kEnabled) { + this.radarrService.getQualityProfiles4kFromSettings().subscribe((c) => { + this.radarrProfiles = c; + }); + this.radarrService.getRootFolders4kFromSettings().subscribe((c) => { + this.radarrRootFolders = c; + }); + } + } else { + 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})` : ""; - if (username || email) { - return `${username} ${email}`; - } - return ''; - } + public displayFn(user: IUserDropdown): string { + const username = user?.username ? user.username : ''; + const email = user?.email ? `(${user.email})` : ''; + if (username || email) { + return `${username} ${email}`; + } + return ''; + } - private _filter(value: string | IUserDropdown): IUserDropdown[] { - const filterValue = - typeof value === "string" - ? value.toLowerCase() - : value.username.toLowerCase(); + 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) - ); - } + return this.options.filter((option) => option.username.toLowerCase().includes(filterValue)); + } - public async submitRequest() { - const model = this.form.value; - model.radarrQualityOverrideTitle = this.radarrProfiles?.filter(x => x.id == model.radarrPathId)[0]?.name; - model.radarrRootFolderTitle = this.radarrRootFolders?.filter(x => x.id == model.radarrFolderId)[0]?.path; - model.sonarrRootFolderTitle = this.sonarrRootFolders?.filter(x => x.id == model.sonarrFolderId)[0]?.path; - model.sonarrQualityOverrideTitle = this.sonarrProfiles?.filter(x => x.id == model.sonarrPathId)[0]?.name; - model.sonarrLanguageProfileTitle = this.sonarrLanguageProfiles?.filter(x => x.id == model.sonarrLanguageId)[0]?.name; - this.dialogRef.close(model); - } + public async submitRequest() { + const model = this.form.value; + model.radarrQualityOverrideTitle = this.radarrProfiles?.filter((x) => x.id == model.radarrPathId)[0]?.name; + model.radarrRootFolderTitle = this.radarrRootFolders?.filter((x) => x.id == model.radarrFolderId)[0]?.path; + model.sonarrRootFolderTitle = this.sonarrRootFolders?.filter((x) => x.id == model.sonarrFolderId)[0]?.path; + model.sonarrQualityOverrideTitle = this.sonarrProfiles?.filter((x) => x.id == model.sonarrPathId)[0]?.name; + model.sonarrLanguageProfileTitle = this.sonarrLanguageProfiles?.filter((x) => x.id == model.sonarrLanguageId)[0]?.name; + this.dialogRef.close(model); + } } diff --git a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts index 7da0fc238..3f4cb56fe 100644 --- a/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts +++ b/src/Ombi/ClientApp/src/app/shared/episode-request/episode-request.component.ts @@ -62,7 +62,7 @@ export class EpisodeRequestComponent { }); if (this.data.isAdmin) { - const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.tvShow, id: this.data.series.id }, panelClass: 'modal-panel' }); + const dialog = this.dialog.open(AdminRequestDialogComponent, { width: "700px", data: { type: RequestType.tvShow, id: this.data.series.id, is4k: null }, panelClass: 'modal-panel' }); dialog.afterClosed().subscribe(async (result) => { if (result) { viewModel.requestOnBehalf = result.username?.id; diff --git a/src/Ombi/ClientApp/src/app/state/radarr/index.ts b/src/Ombi/ClientApp/src/app/state/radarr/index.ts new file mode 100644 index 000000000..417d27996 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/radarr/index.ts @@ -0,0 +1,4 @@ +export * from './radarr.state'; +export * from './radarr.actions'; +export * from './radarr.facade'; +export * from './radarr.selectors'; diff --git a/src/Ombi/ClientApp/src/app/state/radarr/radarr-initializer.ts b/src/Ombi/ClientApp/src/app/state/radarr/radarr-initializer.ts new file mode 100644 index 000000000..fa8759016 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/radarr/radarr-initializer.ts @@ -0,0 +1,12 @@ +import { APP_INITIALIZER } from "@angular/core"; +import { Observable } from "rxjs"; +import { RadarrFacade } from "./radarr.facade"; + +export const RADARR_INITIALIZER = { + provide: APP_INITIALIZER, + useFactory: (radarrFacade: RadarrFacade) => (): Observable => { + return radarrFacade.load(); + }, + multi: true, + deps: [RadarrFacade], +}; \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/radarr/radarr.actions.ts b/src/Ombi/ClientApp/src/app/state/radarr/radarr.actions.ts new file mode 100644 index 000000000..f97ba7c1a --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/radarr/radarr.actions.ts @@ -0,0 +1,10 @@ +import { IRadarrCombined } from "../../interfaces"; + +export class LoadSettings { + public static readonly type = '[Radarr] LoadSettings'; +} + +export class UpdateSettings { + public static readonly type = '[Radarr] UpdateSettings'; + constructor(public settings: IRadarrCombined) { } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/radarr/radarr.facade.ts b/src/Ombi/ClientApp/src/app/state/radarr/radarr.facade.ts new file mode 100644 index 000000000..11afa1996 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/radarr/radarr.facade.ts @@ -0,0 +1,27 @@ +import { IRadarrCombined } from "../../interfaces"; +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; +import { Store } from "@ngxs/store"; +import { RadarrState } from "./types"; +import { RadarrSelectors } from "./radarr.selectors"; +import { LoadSettings, UpdateSettings } from "./radarr.actions"; + +@Injectable({ + providedIn: 'root', +}) +export class RadarrFacade { + + public constructor(private store: Store) {} + + public state$ = (): Observable => this.store.select(RadarrSelectors.state); + + public updateSettings = (settings: IRadarrCombined): Observable => this.store.dispatch(new UpdateSettings(settings)); + + public load = (): Observable => this.store.dispatch(new LoadSettings()); + + public settings = (): IRadarrCombined => this.store.selectSnapshot(RadarrSelectors.settings); + + public isEnabled = (): boolean => this.store.selectSnapshot(RadarrSelectors.isEnabled); + + public is4KEnabled = (): boolean => this.store.selectSnapshot(RadarrSelectors.is4KEnabled); +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/radarr/radarr.selectors.ts b/src/Ombi/ClientApp/src/app/state/radarr/radarr.selectors.ts new file mode 100644 index 000000000..381469199 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/radarr/radarr.selectors.ts @@ -0,0 +1,26 @@ +import { RadarrState, RADARR_STATE_TOKEN } from "./types"; +import { Selector } from "@ngxs/store"; +import { IRadarrCombined } from "../../interfaces"; + +export class RadarrSelectors { + + @Selector([RADARR_STATE_TOKEN]) + public static state(state: RadarrState): RadarrState { + return state; + } + + @Selector([RadarrSelectors.state]) + public static settings(state: RadarrState): IRadarrCombined { + return state.settings; + } + + @Selector([RadarrSelectors.settings]) + public static isEnabled(settings: IRadarrCombined): boolean { + return settings?.radarr?.enabled ?? false; + } + + @Selector([RadarrSelectors.settings]) + public static is4KEnabled(settings: IRadarrCombined): boolean { + return settings?.radarr4K?.enabled ?? false; + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/radarr/radarr.state.ts b/src/Ombi/ClientApp/src/app/state/radarr/radarr.state.ts new file mode 100644 index 000000000..b59faa624 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/radarr/radarr.state.ts @@ -0,0 +1,41 @@ +import { Action, State, StateContext } from "@ngxs/store"; + +import { RadarrState, RADARR_STATE_TOKEN } from "./types"; +import { SettingsService } from "../../services"; +import { AuthService } from "../../auth/auth.service"; +import { Injectable } from "@angular/core"; +import { combineLatest, Observable, of } from "rxjs"; +import { map, tap } from "rxjs/operators"; +import { IRadarrCombined } from "../../interfaces"; +import { LoadSettings, UpdateSettings } from "./radarr.actions"; + +@State({ + name: RADARR_STATE_TOKEN +}) +@Injectable() +export class RadarrSettingsState { + constructor(private settingsService: SettingsService, private authService: AuthService) { } + + @Action(LoadSettings) + public load({ setState }: StateContext): Observable { + const isAdmin = this.authService.isAdmin(); + const calls = isAdmin ? [this.settingsService.getRadarr()] : [of({})]; + + return combineLatest(calls).pipe( + tap(([settings]) => + { + setState({settings: settings as IRadarrCombined}); + }), + map((result) => {settings: result[0]}) + ); + } + + @Action(UpdateSettings) + public enable(ctx: StateContext, { settings }: UpdateSettings): Observable { + const state = ctx.getState(); + return this.settingsService.saveRadarr(settings).pipe( + tap((_) => ctx.setState({...state, settings})), + map(_ => {...state, settings}) + ); + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/radarr/types.ts b/src/Ombi/ClientApp/src/app/state/radarr/types.ts new file mode 100644 index 000000000..2a1facd8d --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/radarr/types.ts @@ -0,0 +1,8 @@ +import { IRadarrCombined } from "../../interfaces"; +import { StateToken } from "@ngxs/store"; + +export const RADARR_STATE_TOKEN = new StateToken('RadarrState'); + +export interface RadarrState { + settings: IRadarrCombined; +} \ No newline at end of file diff --git a/src/Ombi/Controllers/V1/External/RadarrController.cs b/src/Ombi/Controllers/V1/External/RadarrController.cs index 6c50b7df4..8c5979647 100644 --- a/src/Ombi/Controllers/V1/External/RadarrController.cs +++ b/src/Ombi/Controllers/V1/External/RadarrController.cs @@ -18,18 +18,18 @@ namespace Ombi.Controllers.V1.External public class RadarrController : ControllerBase { - public RadarrController(IRadarrApi radarr, ISettingsService settings, - ICacheService mem, IRadarrV3Api radarrV3Api) + public RadarrController( + ISettingsService settings, + ISettingsService radarr4kSettings, + IRadarrV3Api radarrV3Api) { - _radarrApi = radarr; _radarrSettings = settings; - _cache = mem; + _radarr4KSettings = radarr4kSettings; _radarrV3Api = radarrV3Api; } - private readonly IRadarrApi _radarrApi; private readonly ISettingsService _radarrSettings; - private readonly ICacheService _cache; + private readonly ISettingsService _radarr4KSettings; private readonly IRadarrV3Api _radarrV3Api; /// /// Gets the Radarr profiles. @@ -80,6 +80,23 @@ namespace Ombi.Controllers.V1.External return null; } + /// + /// Gets the Radarr 4K profiles using the saved settings + /// The data is cached for an hour + /// + /// + [HttpGet("Profiles/4k")] + [PowerUser] + public async Task GetProfiles4K() + { + var settings = await _radarr4KSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return Ok(await _radarrV3Api.GetProfiles(settings.ApiKey, settings.FullUri)); + } + return null; + } + /// /// Gets the Radarr root folders using the saved settings. /// The data is cached for an hour @@ -97,6 +114,23 @@ namespace Ombi.Controllers.V1.External return null; } + /// + /// Gets the Radarr 4K root folders using the saved settings. + /// The data is cached for an hour + /// + /// + [HttpGet("RootFolders/4k")] + [PowerUser] + public async Task> GetRootFolders4K() + { + var settings = await _radarr4KSettings.GetSettingsAsync(); + if (settings.Enabled) + { + return await _radarrV3Api.GetRootFolders(settings.ApiKey, settings.FullUri); + } + return null; + } + /// /// Gets the Radarr tags ///