feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)

pull/4804/head
Jamie 2 years ago committed by GitHub
parent 3e23d36ac3
commit ecfbb8eda9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -3,6 +3,7 @@
public class TesterResultModel public class TesterResultModel
{ {
public bool IsValid { get; set; } public bool IsValid { get; set; }
public string Version { get; set; }
public string ExpectedSubDir { get; set; } public string ExpectedSubDir { get; set; }
} }
} }

@ -312,7 +312,7 @@ namespace Ombi.Schedule.Jobs.Plex
{ {
break; break;
} }
if (quality.Equals(existing.Quality)) if (quality == null || quality.Equals(existing.Quality))
{ {
// We got it // We got it
continue; continue;

@ -12,3 +12,8 @@ export interface ILanguageProfiles {
name: string; name: string;
id: number; id: number;
} }
export interface ITag {
label: string;
id: number;
}

@ -1,4 +1,5 @@
export interface ITesterResult { export interface ITesterResult {
isValid: boolean; isValid: boolean;
version?: string;
expectedSubDir?: string; expectedSubDir?: string;
} }

@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs"; import { Observable } from "rxjs";
import { ISonarrSettings } from "../../interfaces"; import { ISonarrSettings, ITag } from "../../interfaces";
import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder } from "../../interfaces";
import { ServiceHelpers } from "../service.helpers"; import { ServiceHelpers } from "../service.helpers";
@ -36,6 +36,10 @@ export class SonarrService extends ServiceHelpers {
return this.http.get<ILanguageProfiles[]>(`${this.url}/v3/languageprofiles/`, {headers: this.headers}); return this.http.get<ILanguageProfiles[]>(`${this.url}/v3/languageprofiles/`, {headers: this.headers});
} }
public getTags(settings: ISonarrSettings): Observable<ITag[]> {
return this.http.post<ITag[]>(`${this.url}/tags/`, JSON.stringify(settings), {headers: this.headers});
}
public isEnabled(): Promise<boolean> { public isEnabled(): Promise<boolean> {
return this.http.get<boolean>(`${this.url}/enabled/`, { headers: this.headers }).toPromise(); return this.http.get<boolean>(`${this.url}/enabled/`, { headers: this.headers }).toPromise();
} }

@ -57,14 +57,17 @@
</div> </div>
</div> </div>
<div class="col-md-5 col-4 col-sm-12"> <div class="col-md-5 col-4 col-sm-12">
<label for="username" class="control-label"><h3>Sonarr Interface</h3></label>
<div class="form-group col-md-12"> <div class="form-group col-md-12">
<div id="profiles"> <div class="row">
<div class="md-form-field" style="display:inline;"> <div class="col-md-12">
<button mat-raised-button id="profiles" (click)="getProfiles(form)" class="mat-stroked-button"> <button mat-raised-button id="profiles" type="button" (click)="getProfiles(form)" class="mat-stroked-button">
Load Qualities <span *ngIf="profilesRunning" class="fas fa-spinner fa-spin"></span></button> Load Qualities <span *ngIf="profilesRunning" class="fas fa-spinner fa-spin"></span></button>
<div class="md-form-field" style="margin-top:1em;"></div> <div class="md-form-field" style="margin-top:1em;"></div>
</div> </div>
</div>
<div class="row">
<div id="profiles" class="col-md-6">
<div class="md-form-field" style="display:contents;"> <div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Quality Profiles</mat-label> <mat-label>Quality Profiles</mat-label>
@ -76,7 +79,7 @@
</div> </div>
</div> </div>
<div id="qualityProfileAnime"> <div id="qualityProfileAnime" class="col-md-6">
<div class="md-form-field" style="display:contents;"> <div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Quality Profiles (Anime)</mat-label> <mat-label>Quality Profiles (Anime)</mat-label>
@ -88,14 +91,18 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="form-group col-md-12"> <div class="form-group col-md-12">
<div id="rootFolders"> <div class="row">
<div class="md-form-field" style="display:inline"> <div class="col-md-12">
<button mat-raised-button id="rootFolder" (click)="getRootFolders(form)" class="mat-stroked-button"> <button mat-raised-button id="rootFolder" type="button" (click)="getRootFolders(form)" class="mat-stroked-button">
Load Folders <span *ngIf="rootFoldersRunning" class="fas fa-spinner fa-spin"></span></button><div class="md-form-field" style="margin-top:1em;"></div> Load Folders <span *ngIf="rootFoldersRunning" class="fas fa-spinner fa-spin"></span></button><div class="md-form-field" style="margin-top:1em;"></div>
</div> </div>
</div>
<div class="row">
<div id="rootFolders" class="col-md-6">
<div class="md-form-field" style="display:contents;"> <div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Default Root Folders</mat-label> <mat-label>Default Root Folders</mat-label>
@ -107,7 +114,7 @@
</div> </div>
</div> </div>
<div id="rootFoldersAnime"> <div id="rootFoldersAnime" class="col-md-6">
<div class="md-form-field" style="display:contents;"> <div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Default Root Folders (Anime)</mat-label> <mat-label>Default Root Folders (Anime)</mat-label>
@ -118,21 +125,58 @@
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</div></div>
<div class="form-group col-md-12">
<div class="row">
<div class="col-md-12">
<button mat-raised-button id="rootFolder" type="button" (click)="getTags(form)" class="mat-stroked-button">
Load Tags <span *ngIf="tagsRunning" class="fas fa-spinner fa-spin"></span></button><div class="md-form-field" style="margin-top:1em;"></div>
</div>
</div>
<div class="row">
<div id="tag" class="col-md-6">
<div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline">
<mat-label>Default Tag</mat-label>
<mat-select formControlName="tag">
<mat-option *ngFor="let tag of tags" [value]="tag.id">{{tag.label}} </mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div id="animeTag" class="col-md-6">
<div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline">
<mat-label>Anime Tags</mat-label>
<mat-select formControlName="animeTag">
<mat-option *ngFor="let tag of animeTags" [value]="tag.id">{{tag.label}} </mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
</div></div>
<div class="form-group col-md-12" *ngIf="sonarrVersion === '3'">
<label for="select" class="control-label">Language Profiles
<i *ngIf="form.get('languageProfile').hasError('required')" class="fas fa-exclamation-circle error-text" pTooltip="A Language Profile is required"></i>
</label>
<div class="md-form-field" style="display:inline">
<div class="row">
<div class="col-md-12">
<button type="button" mat-raised-button (click)="getLanguageProfiles(form)" class="mat-stroked-button">Load
Languages <span *ngIf="langRunning" class="fas fa-spinner fa-spin"> </span></button><div class="md-form-field" style="margin-top:1em;"></div>
</div>
</div> </div>
</div>
<div class="row">
<div id="langaugeProfile" class="col-md-6">
<div class="form-group col-md-12">
<label for="select" class="control-label">Language Profiles
<i *ngIf="form.get('languageProfile').hasError('required')" class="fas fa-exclamation-circle error-text" pTooltip="A Language Profile is required"></i>
</label>
<div id="langaugeProfile">
<div class="md-form-field" style="display:inline">
<button type="button" mat-raised-button (click)="getLanguageProfiles(form)" class="mat-stroked-button">Load
Languages <span *ngIf="langRunning" class="fas fa-spinner fa-spin"> </span></button><div class="md-form-field" style="margin-top:1em;"></div>
</div>
<div class="md-form-field" style="display:contents;"> <div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Language Profiles </mat-label> <mat-label>Language Profiles</mat-label>
<mat-select formControlName="languageProfile"> <mat-select formControlName="languageProfile">
<mat-option *ngFor="let lang of languageProfiles" [value]="lang.id">{{lang.name}}</mat-option> <mat-option *ngFor="let lang of languageProfiles" [value]="lang.id">{{lang.name}}</mat-option>
</mat-select> </mat-select>
@ -141,19 +185,20 @@
</div> </div>
</div> </div>
<div id="langaugeProfileAnime"> <div id="langaugeProfileAnime" class="col-md-6">
<div class="md-form-field" style="display:contents;"> <div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline"> <mat-form-field appearance="outline">
<mat-label>Language Profiles Anime</mat-label> <mat-label>Anime</mat-label>
<mat-select formControlName="languageProfileAnime"> <mat-select formControlName="languageProfileAnime">
<mat-option *ngFor="let lang of languageProfiles" [value]="lang.id">{{lang.name}}</mat-option> <mat-option *ngFor="let lang of languageProfiles" [value]="lang.id">{{lang.name}}</mat-option>
</mat-select> </mat-select>
<mat-error>A Language Profile Anime is required</mat-error> <mat-error>A Language Profile Anime is required</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="form-group col-md-12"> <div class="form-group col-md-12">
@ -170,18 +215,19 @@
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="col-md-4">
<div class="form-group col-md-7"> <div class="form-group col-md-7">
<div> <div>
<button mat-raised-button type="submit" class="mat-stroked-button accent mat-accent">Submit</button> <button mat-raised-button type="button" (click)="test(form)" class="mat-stroked-button">Test Connectivity
<span id="spinner"> </span></button>
</div> </div>
</div> </div>
<div class="form-group col-md-7"> <div class="form-group col-md-7">
<div> <div>
<button mat-raised-button type="button" (click)="test(form)" class="mat-stroked-button">Test Connectivity <button mat-raised-button type="submit" class="mat-stroked-button accent mat-accent">Submit</button>
<span id="spinner"> </span></button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</form> </form>

@ -1,7 +1,8 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { UntypedFormBuilder, FormControl, UntypedFormGroup, Validators } from "@angular/forms"; import { UntypedFormBuilder, FormControl, UntypedFormGroup, Validators } from "@angular/forms";
import { finalize, map } from "rxjs";
import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder } from "../../interfaces"; import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder, ITag } from "../../interfaces";
import { ISonarrSettings } from "../../interfaces"; import { ISonarrSettings } from "../../interfaces";
import { SonarrService } from "../../services"; import { SonarrService } from "../../services";
@ -21,14 +22,20 @@ export class SonarrComponent implements OnInit {
public rootFoldersAnime: ISonarrRootFolder[]; public rootFoldersAnime: ISonarrRootFolder[];
public languageProfiles: ILanguageProfiles[]; public languageProfiles: ILanguageProfiles[];
public languageProfilesAnime: ILanguageProfiles[]; public languageProfilesAnime: ILanguageProfiles[];
public tags: ITag[];
public animeTags: ITag[];
public selectedRootFolder: ISonarrRootFolder; public selectedRootFolder: ISonarrRootFolder;
public selectedQuality: ISonarrProfile; public selectedQuality: ISonarrProfile;
public selectedLanguageProfiles: ILanguageProfiles; public selectedLanguageProfiles: ILanguageProfiles;
public profilesRunning: boolean; public profilesRunning: boolean;
public rootFoldersRunning: boolean; public rootFoldersRunning: boolean;
public tagsRunning: boolean;
public langRunning: boolean; public langRunning: boolean;
public form: UntypedFormGroup; public form: UntypedFormGroup;
public advanced = false; public advanced = false;
public sonarrVersion: string;
formErrors: any; formErrors: any;
constructor(private settingsService: SettingsService, constructor(private settingsService: SettingsService,
@ -72,12 +79,29 @@ export class SonarrComponent implements OnInit {
port: [x.port, [Validators.required]], port: [x.port, [Validators.required]],
addOnly: [x.addOnly], addOnly: [x.addOnly],
seasonFolders: [x.seasonFolders], seasonFolders: [x.seasonFolders],
languageProfile: [x.languageProfile, [Validators.required, validateProfile]], languageProfile: [x.languageProfile],
languageProfileAnime: [x.languageProfileAnime], languageProfileAnime: [x.languageProfileAnime],
scanForAvailability: [x.scanForAvailability], scanForAvailability: [x.scanForAvailability],
sendUserTags: [x.sendUserTags] sendUserTags: [x.sendUserTags],
tag: [x.tag],
animeTag: [x.animeTag]
}); });
this.rootFolders = [];
this.qualities = [];
this.languageProfiles = [];
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 (x.qualityProfile) { if (x.qualityProfile) {
this.getProfiles(this.form); this.getProfiles(this.form);
} }
@ -87,6 +111,9 @@ export class SonarrComponent implements OnInit {
if (x.languageProfile) { if (x.languageProfile) {
this.getLanguageProfiles(this.form); this.getLanguageProfiles(this.form);
} }
if (x.tag || x.animeTag) {
this.getTags(this.form);
}
this.formErrors ={ this.formErrors ={
apiKey: {}, apiKey: {},
@ -97,12 +124,12 @@ export class SonarrComponent implements OnInit {
}; };
this.onFormValuesChanged(); this.onFormValuesChanged();
}); });
this.rootFolders = [];
this.qualities = [];
this.languageProfiles = [];
this.rootFolders.push({ path: "Please Select", id: -1 }); this.rootFolders.push({ path: "Please Select", id: -1 });
this.qualities.push({ name: "Please Select", id: -1 }); this.qualities.push({ name: "Please Select", id: -1 });
this.languageProfiles.push({ name: "Please Select", id: -1 }); this.languageProfiles.push({ name: "Please Select", id: -1 });
this.animeTags.push({label: "None", id: -1});
this.tags.push({label: "None", id: -1});
} }
public getProfiles(form: UntypedFormGroup) { public getProfiles(form: UntypedFormGroup) {
@ -142,14 +169,27 @@ export class SonarrComponent implements OnInit {
}); });
} }
public getTags(form: UntypedFormGroup) {
this.tagsRunning = true;
this.sonarrService.getTags(form.value).pipe(
finalize(() => {
this.tagsRunning = false;
this.animeTags.unshift({ label: "None", id: -1 });
this.tags.unshift({ label: "None", id: -1 });
this.notificationService.success("Successfully retrieved the Tags");
}),
map(result => {
this.tags = result;
this.tags.forEach(val => this.animeTags.push(Object.assign({}, val)));
})
).subscribe()
}
public test(form: UntypedFormGroup) { public test(form: UntypedFormGroup) {
if (form.invalid) {
this.notificationService.error("Please check your entered values");
return;
}
const settings = <ISonarrSettings> form.value; const settings = <ISonarrSettings> form.value;
this.testerService.sonarrTest(settings).subscribe(result => { this.testerService.sonarrTest(settings).subscribe(result => {
if (result.isValid) { if (result.isValid) {
this.sonarrVersion = result.version[0];
this.notificationService.success("Successfully connected to Sonarr!"); this.notificationService.success("Successfully connected to Sonarr!");
} else if (result.expectedSubDir) { } else if (result.expectedSubDir) {
this.notificationService.error("Your Sonarr Base URL must be set to " + result.expectedSubDir); this.notificationService.error("Your Sonarr Base URL must be set to " + result.expectedSubDir);
@ -179,6 +219,12 @@ export class SonarrComponent implements OnInit {
this.notificationService.error("Please check your entered values"); this.notificationService.error("Please check your entered values");
} }
} }
if (form.controls.animeTag.value == -1) {
form.controls.animeTag.setValue(null);
}
if (form.controls.tag.value == -1) {
form.controls.tag.setValue(null);
}
this.settingsService.saveSonarr(form.value) this.settingsService.saveSonarr(form.value)
.subscribe(x => { .subscribe(x => {
@ -190,6 +236,7 @@ export class SonarrComponent implements OnInit {
}); });
} }
} }
function validateProfile(qualityProfile): { [key: string]:boolean } | null { function validateProfile(qualityProfile): { [key: string]:boolean } | null {
if (qualityProfile.value !== undefined && (isNaN(qualityProfile.value) || qualityProfile.value == -1)) { if (qualityProfile.value !== undefined && (isNaN(qualityProfile.value) || qualityProfile.value == -1)) {

@ -46,7 +46,7 @@ namespace Ombi.Controllers.V1.External
/// </summary> /// </summary>
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN, public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN,
IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm, IPushbulletNotification pushbullet, ISlackNotification slack, IPushoverNotification po, IMattermostNotification mm,
IPlexApi plex, IEmbyApiFactory emby, IRadarrV3Api radarr, ISonarrApi sonarr, ILogger<TesterController> log, IEmailProvider provider, IPlexApi plex, IEmbyApiFactory emby, IRadarrV3Api radarr, ISonarrV3Api sonarr, ILogger<TesterController> log, IEmailProvider provider,
ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, ILegacyMobileNotification mobileNotification, ICouchPotatoApi cpApi, ITelegramNotification telegram, ISickRageApi srApi, INewsletterJob newsletter, ILegacyMobileNotification mobileNotification,
ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification, ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification,
IJellyfinApi jellyfinApi, IPrincipal user) IJellyfinApi jellyfinApi, IPrincipal user)
@ -90,7 +90,7 @@ namespace Ombi.Controllers.V1.External
private IPlexApi PlexApi { get; } private IPlexApi PlexApi { get; }
private IRadarrV3Api RadarrApi { get; } private IRadarrV3Api RadarrApi { get; }
private IEmbyApiFactory EmbyApi { get; } private IEmbyApiFactory EmbyApi { get; }
private ISonarrApi SonarrApi { get; } private ISonarrV3Api SonarrApi { get; }
private ICouchPotatoApi CouchPotatoApi { get; } private ICouchPotatoApi CouchPotatoApi { get; }
private ILogger<TesterController> Log { get; } private ILogger<TesterController> Log { get; }
private IEmailProvider EmailProvider { get; } private IEmailProvider EmailProvider { get; }
@ -415,6 +415,7 @@ namespace Ombi.Controllers.V1.External
return new TesterResultModel return new TesterResultModel
{ {
IsValid = result.urlBase == settings.SubDir || string.IsNullOrEmpty(result.urlBase) && string.IsNullOrEmpty(settings.SubDir), IsValid = result.urlBase == settings.SubDir || string.IsNullOrEmpty(result.urlBase) && string.IsNullOrEmpty(settings.SubDir),
Version = result.version,
ExpectedSubDir = result.urlBase ExpectedSubDir = result.urlBase
}; };
} }

Loading…
Cancel
Save