diff --git a/src/Ombi/ClientApp/src/app/app.module.ts b/src/Ombi/ClientApp/src/app/app.module.ts index 24644d1df..0400d3351 100644 --- a/src/Ombi/ClientApp/src/app/app.module.ts +++ b/src/Ombi/ClientApp/src/app/app.module.ts @@ -1,5 +1,5 @@ import { APP_BASE_HREF, CommonModule, PlatformLocation } from "@angular/common"; -import { CustomPageService, ImageService, RequestService, SettingsService } from "./services"; +import { CustomPageService, ImageService, RequestService, SettingsService, SonarrService } from "./services"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; import { HTTP_INTERCEPTORS, HttpClient, HttpClientModule } from "@angular/common/http"; import { IdentityService, IssuesService, JobService, MessageService, PlexTvService, SearchService, StatusService } from "./services"; @@ -13,6 +13,7 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; 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 { ConfirmDialogModule } from "primeng/confirmdialog"; import { CookieComponent } from "./auth/cookie.component"; import { CookieService } from "ng2-cookies"; @@ -22,6 +23,7 @@ import { DataViewModule } from "primeng/dataview"; import { DialogModule } from "primeng/dialog"; import { FEATURES_INITIALIZER } from "./state/features/features-initializer"; import { FeatureState } from "./state/features"; +import { SonarrSettingsState } from "./state/sonarr"; import { JwtModule } from "@auth0/angular-jwt"; import { LandingPageComponent } from "./landingpage/landingpage.component"; import { LandingPageService } from "./services"; @@ -161,7 +163,7 @@ export function JwtTokenGetter() { }), SidebarModule, MatNativeDateModule, MatIconModule, MatSidenavModule, MatListModule, MatToolbarModule, LayoutModule, MatSlideToggleModule, - NgxsModule.forRoot([CustomizationState, FeatureState], { + NgxsModule.forRoot([CustomizationState, FeatureState, SonarrSettingsState], { developmentMode: !environment.production, }), ...environment.production ? [] : @@ -205,8 +207,10 @@ export function JwtTokenGetter() { MessageService, StorageService, RequestService, + SonarrService, SignalRNotificationService, FEATURES_INITIALIZER, + SONARR_INITIALIZER, CUSTOMIZATION_INITIALIZER, { provide: APP_BASE_HREF, diff --git a/src/Ombi/ClientApp/src/app/login/login.component.ts b/src/Ombi/ClientApp/src/app/login/login.component.ts index 6e8efc00b..f2aacdae4 100644 --- a/src/Ombi/ClientApp/src/app/login/login.component.ts +++ b/src/Ombi/ClientApp/src/app/login/login.component.ts @@ -13,6 +13,7 @@ import { StatusService } 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"; @Component({ templateUrl: "./login.component.html", @@ -60,6 +61,7 @@ export class LoginComponent implements OnDestroy, OnInit { private translate: TranslateService, private plexTv: PlexTvService, private store: StorageService, + private sonarrFacade: SonarrFacade, private readonly notify: MatSnackBar ) { this.href = href; @@ -142,6 +144,7 @@ export class LoginComponent implements OnDestroy, OnInit { if (this.authService.loggedIn()) { this.ngOnDestroy(); + this.sonarrFacade.load().subscribe(); this.router.navigate(["/"]); } else { this.notify.open(this.errorBody, "OK", { @@ -218,6 +221,7 @@ export class LoginComponent implements OnDestroy, OnInit { this.oAuthWindow.close(); } this.oauthLoading = false; + this.sonarrFacade.load().subscribe(); this.router.navigate(["search"]); return; } @@ -248,6 +252,7 @@ export class LoginComponent implements OnDestroy, OnInit { if (this.authService.loggedIn()) { this.ngOnDestroy(); + this.sonarrFacade.load().subscribe(); this.router.navigate(["/"]); } else { this.notify.open(this.errorBody, "OK", { diff --git a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts index edd531a0b..05bd99b82 100644 --- a/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts +++ b/src/Ombi/ClientApp/src/app/media-details/components/tv/tv-details.component.ts @@ -1,5 +1,5 @@ import { Component, ViewEncapsulation, OnInit } from "@angular/core"; -import { ImageService, SearchV2Service, MessageService, RequestService, SonarrService, SettingsStateService } from "../../../services"; +import { SearchV2Service, MessageService, RequestService, SonarrService, SettingsStateService } from "../../../services"; import { ActivatedRoute } from "@angular/router"; import { DomSanitizer } from "@angular/platform-browser"; import { ISearchTvResultV2 } from "../../../interfaces/ISearchTvResultV2"; @@ -11,9 +11,8 @@ import { AuthService } from "../../../auth/auth.service"; import { NewIssueComponent } from "../shared/new-issue/new-issue.component"; import { TvAdvancedOptionsComponent } from "./panels/tv-advanced-options/tv-advanced-options.component"; import { RequestServiceV2 } from "../../../services/requestV2.service"; -import { RequestBehalfComponent } from "../shared/request-behalf/request-behalf.component"; import { forkJoin } from "rxjs"; -import { TopBannerComponent } from "../shared/top-banner/top-banner.component"; +import { SonarrFacade } from "app/state/sonarr"; @Component({ templateUrl: "./tv-details.component.html", @@ -36,10 +35,15 @@ export class TvDetailsComponent implements OnInit { private tvdbId: number; constructor(private searchService: SearchV2Service, private route: ActivatedRoute, - private sanitizer: DomSanitizer, private imageService: ImageService, - public dialog: MatDialog, public messageService: MessageService, private requestService: RequestService, + private sanitizer: DomSanitizer, + public dialog: MatDialog, + public messageService: MessageService, + private requestService: RequestService, private requestService2: RequestServiceV2, - private auth: AuthService, private sonarrService: SonarrService, private settingsState: SettingsStateService) { + private auth: AuthService, + private sonarrService: SonarrService, + private sonarrFacade: SonarrFacade, + private settingsState: SettingsStateService) { this.route.params.subscribe((params: any) => { this.tvdbId = params.tvdbId; this.fromSearch = params.search; @@ -58,7 +62,7 @@ export class TvDetailsComponent implements OnInit { this.manageOwnRequests = this.auth.hasRole('ManageOwnRequests'); if (this.isAdmin) { - this.showAdvanced = await this.sonarrService.isEnabled(); + this.showAdvanced = this.sonarrFacade.isEnabled(); } // if (this.fromSearch) { @@ -138,6 +142,7 @@ export class TvDetailsComponent implements OnInit { this.tv.images.original = 'https://image.tmdb.org/t/p/w300/' + this.tv.images.original }; } + private loadAdvancedInfo() { const profile = this.sonarrService.getQualityProfilesWithoutSettings(); const folders = this.sonarrService.getRootFoldersWithoutSettings(); diff --git a/src/Ombi/ClientApp/src/app/services/applications/sonarr.service.ts b/src/Ombi/ClientApp/src/app/services/applications/sonarr.service.ts index ae41badf0..62f6208a6 100644 --- a/src/Ombi/ClientApp/src/app/services/applications/sonarr.service.ts +++ b/src/Ombi/ClientApp/src/app/services/applications/sonarr.service.ts @@ -40,7 +40,11 @@ export class SonarrService extends ServiceHelpers { return this.http.post(`${this.url}/tags/`, JSON.stringify(settings), {headers: this.headers}); } - public isEnabled(): Promise { - return this.http.get(`${this.url}/enabled/`, { headers: this.headers }).toPromise(); + public isEnabled(): Observable { + return this.http.get(`${this.url}/enabled/`, { headers: this.headers }); + } + + public getVersion(): Observable { + return this.http.get(`${this.url}/version/`, { headers: this.headers }); } } 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 cc4ce8d72..fc85de648 100644 --- a/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html +++ b/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.html @@ -157,7 +157,7 @@ -
+
diff --git a/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.ts b/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.ts index fcd5bc47e..0f299bc6f 100644 --- a/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.ts +++ b/src/Ombi/ClientApp/src/app/settings/sonarr/sonarr.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from "@angular/core"; import { UntypedFormBuilder, FormControl, UntypedFormGroup, Validators } from "@angular/forms"; +import { SonarrFacade } from "app/state/sonarr/sonarr.facade"; import { finalize, map } from "rxjs"; import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder, ITag } from "../../interfaces"; @@ -8,7 +9,6 @@ import { ISonarrSettings } from "../../interfaces"; import { SonarrService } from "../../services"; import { TesterService } from "../../services"; import { NotificationService } from "../../services"; -import { SettingsService } from "../../services"; @Component({ templateUrl: "./sonarr.component.html", @@ -22,7 +22,7 @@ export class SonarrComponent implements OnInit { public rootFoldersAnime: ISonarrRootFolder[]; public languageProfiles: ILanguageProfiles[]; public languageProfilesAnime: ILanguageProfiles[]; - + public tags: ITag[]; public animeTags: ITag[]; @@ -38,11 +38,13 @@ export class SonarrComponent implements OnInit { public sonarrVersion: string; formErrors: any; - constructor(private settingsService: SettingsService, - private sonarrService: SonarrService, + public sonarrState$ = this.sonarrFacade.sonarrState$(); + + constructor(private sonarrService: SonarrService, private notificationService: NotificationService, private testerService: TesterService, - private fb: UntypedFormBuilder){} + private fb: UntypedFormBuilder, + private sonarrFacade: SonarrFacade){} onFormValuesChanged() { @@ -64,27 +66,27 @@ export class SonarrComponent implements OnInit { } public ngOnInit() { - this.settingsService.getSonarr() - .subscribe(x => { + this.sonarrFacade.sonarrState$() + .subscribe(({settings, version}) => { this.form = this.fb.group({ - enabled: [x.enabled], - apiKey: [x.apiKey, [Validators.required]], - qualityProfile: [x.qualityProfile, [Validators.required, validateProfile]], - rootPath: [x.rootPath, [Validators.required, validateProfile]], - qualityProfileAnime: [x.qualityProfileAnime], - rootPathAnime: [x.rootPathAnime], - ssl: [x.ssl], - subDir: [x.subDir], - ip: [x.ip, [Validators.required]], - port: [x.port, [Validators.required]], - addOnly: [x.addOnly], - seasonFolders: [x.seasonFolders], - languageProfile: [x.languageProfile], - languageProfileAnime: [x.languageProfileAnime], - scanForAvailability: [x.scanForAvailability], - sendUserTags: [x.sendUserTags], - tag: [x.tag], - animeTag: [x.animeTag] + enabled: [settings.enabled], + apiKey: [settings.apiKey, [Validators.required]], + qualityProfile: [settings.qualityProfile, [Validators.required, validateProfile]], + rootPath: [settings.rootPath, [Validators.required, validateProfile]], + qualityProfileAnime: [settings.qualityProfileAnime], + rootPathAnime: [settings.rootPathAnime], + ssl: [settings.ssl], + subDir: [settings.subDir], + ip: [settings.ip, [Validators.required]], + port: [settings.port, [Validators.required]], + addOnly: [settings.addOnly], + seasonFolders: [settings.seasonFolders], + languageProfile: [settings.languageProfile], + languageProfileAnime: [settings.languageProfileAnime], + scanForAvailability: [settings.scanForAvailability], + sendUserTags: [settings.sendUserTags], + tag: [settings.tag], + animeTag: [settings.animeTag] }); this.rootFolders = []; @@ -93,25 +95,20 @@ export class SonarrComponent implements OnInit { this.tags = []; this.animeTags = []; - if (x.enabled && this.form.valid) { - this.testerService.sonarrTest(x).subscribe(result => { - this.sonarrVersion = result.version[0]; - if (this.sonarrVersion === '3') { - this.form.controls.languageProfile.addValidators([Validators.required, validateProfile]); - } - }); + if (version.length > 0) { + this.sonarrVersion = version[0]; } - if (x.qualityProfile) { + if (settings.qualityProfile) { this.getProfiles(this.form); } - if (x.rootPath) { + if (settings.rootPath) { this.getRootFolders(this.form); } - if (x.languageProfile) { + if (settings.languageProfile) { this.getLanguageProfiles(this.form); } - if (x.tag || x.animeTag) { + if (settings.tag || settings.animeTag) { this.getTags(this.form); } @@ -226,7 +223,7 @@ export class SonarrComponent implements OnInit { form.controls.tag.setValue(null); } - this.settingsService.saveSonarr(form.value) + this.sonarrFacade.updateSettings(form.value) .subscribe(x => { if (x) { this.notificationService.success("Successfully saved Sonarr settings"); 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 8d32ea8b9..9cc059361 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 @@ -53,7 +53,7 @@
- + {{'MediaDetails.LanguageProfileSelect' | translate }} {{profile.name}} 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 5ee3e586f..d0e99b19f 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,11 +1,11 @@ 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, ISonarrSettings, IUserDropdown, RequestType } from "../../interfaces"; -import { IdentityService, MessageService, RadarrService, RequestService, SettingsService, SonarrService } from "../../services"; -import { RequestServiceV2 } from "../../services/requestV2.service"; +import { ILanguageProfiles, IRadarrProfile, IRadarrRootFolder, ISonarrProfile, ISonarrRootFolder, IUserDropdown, RequestType } from "../../interfaces"; +import { IdentityService, RadarrService, SonarrService } from "../../services"; export interface IAdminRequestDialogData { type: RequestType, @@ -23,9 +23,9 @@ export class AdminRequestDialogComponent implements OnInit { @Inject(MAT_DIALOG_DATA) public data: IAdminRequestDialogData, private identityService: IdentityService, private sonarrService: SonarrService, - private settingsService: SettingsService, private radarrService: RadarrService, - private fb: UntypedFormBuilder + private fb: UntypedFormBuilder, + private sonarrFacade: SonarrFacade ) {} public form: UntypedFormGroup; @@ -63,11 +63,14 @@ export class AdminRequestDialogComponent implements OnInit { ); if (this.data.type === RequestType.tvShow) { - this.sonarrEnabled = await this.sonarrService.isEnabled(); + this.sonarrEnabled = this.sonarrFacade.isEnabled(); if (this.sonarrEnabled) { - this.sonarrService.getV3LanguageProfilesWithoutSettings().subscribe((profiles: ILanguageProfiles[]) => { - this.sonarrLanguageProfiles = profiles; - }) + 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; }); diff --git a/src/Ombi/ClientApp/src/app/state/sonarr/index.ts b/src/Ombi/ClientApp/src/app/state/sonarr/index.ts new file mode 100644 index 000000000..3b13dd683 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/sonarr/index.ts @@ -0,0 +1,4 @@ +export * from './sonarr.state'; +export * from './sonarr.actions'; +export * from './sonarr.facade'; +export * from './sonarr.selectors'; diff --git a/src/Ombi/ClientApp/src/app/state/sonarr/sonarr-initializer.ts b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr-initializer.ts new file mode 100644 index 000000000..1696348bc --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr-initializer.ts @@ -0,0 +1,12 @@ +import { APP_INITIALIZER } from "@angular/core"; +import { Observable } from "rxjs"; +import { SonarrFacade } from "./sonarr.facade"; + +export const SONARR_INITIALIZER = { + provide: APP_INITIALIZER, + useFactory: (sonarrFacade: SonarrFacade) => (): Observable => { + return sonarrFacade.load(); + }, + multi: true, + deps: [SonarrFacade], +}; \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.actions.ts b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.actions.ts new file mode 100644 index 000000000..015a4a5b0 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.actions.ts @@ -0,0 +1,10 @@ +import { ISonarrSettings } from "../../interfaces"; + +export class LoadSettings { + public static readonly type = '[Sonarr] LoadSettings'; +} + +export class UpdateSettings { + public static readonly type = '[Sonarr] UpdateSettings'; + constructor(public settings: ISonarrSettings) { } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.facade.ts b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.facade.ts new file mode 100644 index 000000000..0994e00ec --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.facade.ts @@ -0,0 +1,28 @@ +import { ISonarrSettings } from "../../interfaces"; +import { Injectable } from "@angular/core"; +import { Observable } from "rxjs"; +import { Store } from "@ngxs/store"; +import { SonarrState } from "./types"; +import { SonarrSelectors } from "./sonarr.selectors"; +import { LoadSettings, UpdateSettings } from "./sonarr.actions"; + +@Injectable({ + providedIn: 'root', +}) +export class SonarrFacade { + + public constructor(private store: Store) {} + + public sonarrState$ = (): Observable => this.store.select(SonarrSelectors.state); + + public updateSettings = (settings: ISonarrSettings): Observable => this.store.dispatch(new UpdateSettings(settings)); + + public load = (): Observable => this.store.dispatch(new LoadSettings()); + + public version = (): string => this.store.selectSnapshot(SonarrSelectors.version); + + public settings = (): ISonarrSettings => this.store.selectSnapshot(SonarrSelectors.settings); + + public isEnabled = (): boolean => this.store.selectSnapshot(SonarrSelectors.isEnabled); + +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.selectors.ts b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.selectors.ts new file mode 100644 index 000000000..746511874 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.selectors.ts @@ -0,0 +1,26 @@ +import { SonarrState, SONARR_STATE_TOKEN } from "./types"; +import { Selector } from "@ngxs/store"; +import { ISonarrSettings } from "../../interfaces"; + +export class SonarrSelectors { + + @Selector([SONARR_STATE_TOKEN]) + public static state(state: SonarrState): SonarrState { + return state; + } + + @Selector([SonarrSelectors.state]) + public static version(state: SonarrState): string { + return state.version; + } + + @Selector([SonarrSelectors.state]) + public static settings(state: SonarrState): ISonarrSettings { + return state.settings; + } + + @Selector([SonarrSelectors.state]) + public static isEnabled(state: SonarrState): boolean { + return state.settings?.enabled ?? false; + } +} \ No newline at end of file diff --git a/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.state.ts b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.state.ts new file mode 100644 index 000000000..4f08896f8 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/sonarr/sonarr.state.ts @@ -0,0 +1,41 @@ +import { Action, State, StateContext } from "@ngxs/store"; + +import { SonarrState, SONARR_STATE_TOKEN } from "./types"; +import { SettingsService, SonarrService } 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 { LoadSettings, UpdateSettings } from "./sonarr.actions"; +import { ISonarrSettings } from "../../interfaces"; + +@State({ + name: SONARR_STATE_TOKEN +}) +@Injectable() +export class SonarrSettingsState { + constructor(private sonarrService: SonarrService, private settingsService: SettingsService, private authService: AuthService) { } + + @Action(LoadSettings) + public load({ setState }: StateContext): Observable { + const isAdmin = this.authService.isAdmin(); + const calls = isAdmin ? [this.sonarrService.getVersion(), this.settingsService.getSonarr()] : [of(""), of({})]; + + return combineLatest(calls).pipe( + tap(([version, settings]) => + { + setState({settings: settings as ISonarrSettings, version: version as string}); + }), + map((result) => {settings: result[1], version: result[0]}) + ); + } + + @Action(UpdateSettings) + public enable(ctx: StateContext, { settings }: UpdateSettings): Observable { + const state = ctx.getState(); + return this.settingsService.saveSonarr(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/sonarr/types.ts b/src/Ombi/ClientApp/src/app/state/sonarr/types.ts new file mode 100644 index 000000000..3f3deea75 --- /dev/null +++ b/src/Ombi/ClientApp/src/app/state/sonarr/types.ts @@ -0,0 +1,9 @@ +import { ISonarrSettings } from "../../interfaces"; +import { StateToken } from "@ngxs/store"; + +export const SONARR_STATE_TOKEN = new StateToken('SonarrState'); + +export interface SonarrState { + settings: ISonarrSettings; + version: string; +} \ No newline at end of file diff --git a/src/Ombi/Controllers/V1/External/SonarrController.cs b/src/Ombi/Controllers/V1/External/SonarrController.cs index 80645c9fd..1d63c6013 100644 --- a/src/Ombi/Controllers/V1/External/SonarrController.cs +++ b/src/Ombi/Controllers/V1/External/SonarrController.cs @@ -151,5 +151,17 @@ namespace Ombi.Controllers.V1.External return settings.Enabled; } + [HttpGet("version")] + [PowerUser] + public async Task SonarrVersion() + { + var settings = await SonarrSettings.GetSettingsAsync(); + if (!settings.Enabled) + { + return string.Empty; + } + var status = await SonarrV3Api.SystemStatus(settings.ApiKey, settings.FullUri); + return status.version; + } } } \ No newline at end of file