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 bool IsValid { get; set; }
public string Version { get; set; }
public string ExpectedSubDir { get; set; }
}
}

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

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

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

@ -4,7 +4,7 @@ import { Injectable, Inject } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { ISonarrSettings } from "../../interfaces";
import { ISonarrSettings, ITag } from "../../interfaces";
import { ILanguageProfiles, ISonarrProfile, ISonarrRootFolder } from "../../interfaces";
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});
}
public getTags(settings: ISonarrSettings): Observable<ITag[]> {
return this.http.post<ITag[]>(`${this.url}/tags/`, JSON.stringify(settings), {headers: this.headers});
}
public isEnabled(): Promise<boolean> {
return this.http.get<boolean>(`${this.url}/enabled/`, { headers: this.headers }).toPromise();
}

@ -57,14 +57,17 @@
</div>
</div>
<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 id="profiles">
<div class="md-form-field" style="display:inline;">
<button mat-raised-button id="profiles" (click)="getProfiles(form)" class="mat-stroked-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>
<div class="row">
<div class="col-md-12">
<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>
<div class="md-form-field" style="margin-top:1em;"></div>
</div>
</div>
<div class="row">
<div id="profiles" class="col-md-6">
<div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline">
<mat-label>Quality Profiles</mat-label>
@ -76,7 +79,7 @@
</div>
</div>
<div id="qualityProfileAnime">
<div id="qualityProfileAnime" class="col-md-6">
<div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline">
<mat-label>Quality Profiles (Anime)</mat-label>
@ -88,14 +91,18 @@
</div>
</div>
</div>
</div>
<div class="form-group col-md-12">
<div id="rootFolders">
<div class="md-form-field" style="display:inline">
<button mat-raised-button id="rootFolder" (click)="getRootFolders(form)" class="mat-stroked-button">
<div class="row">
<div class="col-md-12">
<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>
</div>
</div>
<div class="row">
<div id="rootFolders" class="col-md-6">
<div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline">
<mat-label>Default Root Folders</mat-label>
@ -107,7 +114,7 @@
</div>
</div>
<div id="rootFoldersAnime">
<div id="rootFoldersAnime" class="col-md-6">
<div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline">
<mat-label>Default Root Folders (Anime)</mat-label>
@ -118,21 +125,58 @@
</mat-form-field>
</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 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;">
<mat-form-field appearance="outline">
<mat-label>Language Profiles </mat-label>
<mat-label>Language Profiles</mat-label>
<mat-select formControlName="languageProfile">
<mat-option *ngFor="let lang of languageProfiles" [value]="lang.id">{{lang.name}}</mat-option>
</mat-select>
@ -141,19 +185,20 @@
</div>
</div>
<div id="langaugeProfileAnime">
<div id="langaugeProfileAnime" class="col-md-6">
<div class="md-form-field" style="display:contents;">
<mat-form-field appearance="outline">
<mat-label>Language Profiles Anime</mat-label>
<mat-label>Anime</mat-label>
<mat-select formControlName="languageProfileAnime">
<mat-option *ngFor="let lang of languageProfiles" [value]="lang.id">{{lang.name}}</mat-option>
</mat-select>
<mat-error>A Language Profile Anime is required</mat-error>
</mat-form-field>
</div>
</div>
</div>
</div>
<div class="form-group col-md-12">
@ -170,18 +215,19 @@
</div>
</div>
<div class="col-md-4">
<div class="form-group col-md-7">
<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 class="form-group col-md-7">
<div>
<button mat-raised-button type="button" (click)="test(form)" class="mat-stroked-button">Test Connectivity
<span id="spinner"> </span></button>
<button mat-raised-button type="submit" class="mat-stroked-button accent mat-accent">Submit</button>
</div>
</div>
</div>
</div>
</form>

@ -1,7 +1,8 @@
import { Component, OnInit } from "@angular/core";
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 { SonarrService } from "../../services";
@ -21,14 +22,20 @@ export class SonarrComponent implements OnInit {
public rootFoldersAnime: ISonarrRootFolder[];
public languageProfiles: ILanguageProfiles[];
public languageProfilesAnime: ILanguageProfiles[];
public tags: ITag[];
public animeTags: ITag[];
public selectedRootFolder: ISonarrRootFolder;
public selectedQuality: ISonarrProfile;
public selectedLanguageProfiles: ILanguageProfiles;
public profilesRunning: boolean;
public rootFoldersRunning: boolean;
public tagsRunning: boolean;
public langRunning: boolean;
public form: UntypedFormGroup;
public advanced = false;
public sonarrVersion: string;
formErrors: any;
constructor(private settingsService: SettingsService,
@ -72,12 +79,29 @@ export class SonarrComponent implements OnInit {
port: [x.port, [Validators.required]],
addOnly: [x.addOnly],
seasonFolders: [x.seasonFolders],
languageProfile: [x.languageProfile, [Validators.required, validateProfile]],
languageProfile: [x.languageProfile],
languageProfileAnime: [x.languageProfileAnime],
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) {
this.getProfiles(this.form);
}
@ -87,6 +111,9 @@ export class SonarrComponent implements OnInit {
if (x.languageProfile) {
this.getLanguageProfiles(this.form);
}
if (x.tag || x.animeTag) {
this.getTags(this.form);
}
this.formErrors ={
apiKey: {},
@ -97,12 +124,12 @@ export class SonarrComponent implements OnInit {
};
this.onFormValuesChanged();
});
this.rootFolders = [];
this.qualities = [];
this.languageProfiles = [];
this.rootFolders.push({ path: "Please Select", id: -1 });
this.qualities.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) {
@ -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) {
if (form.invalid) {
this.notificationService.error("Please check your entered values");
return;
}
const settings = <ISonarrSettings> form.value;
this.testerService.sonarrTest(settings).subscribe(result => {
if (result.isValid) {
this.sonarrVersion = result.version[0];
this.notificationService.success("Successfully connected to Sonarr!");
} else if (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");
}
}
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)
.subscribe(x => {
@ -190,6 +236,7 @@ export class SonarrComponent implements OnInit {
});
}
}
function validateProfile(qualityProfile): { [key: string]:boolean } | null {
if (qualityProfile.value !== undefined && (isNaN(qualityProfile.value) || qualityProfile.value == -1)) {

@ -46,7 +46,7 @@ namespace Ombi.Controllers.V1.External
/// </summary>
public TesterController(INotificationService service, IDiscordNotification notification, IEmailNotification emailN,
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,
ILidarrApi lidarrApi, IGotifyNotification gotifyNotification, IWhatsAppApi whatsAppApi, OmbiUserManager um, IWebhookNotification webhookNotification,
IJellyfinApi jellyfinApi, IPrincipal user)
@ -90,7 +90,7 @@ namespace Ombi.Controllers.V1.External
private IPlexApi PlexApi { get; }
private IRadarrV3Api RadarrApi { get; }
private IEmbyApiFactory EmbyApi { get; }
private ISonarrApi SonarrApi { get; }
private ISonarrV3Api SonarrApi { get; }
private ICouchPotatoApi CouchPotatoApi { get; }
private ILogger<TesterController> Log { get; }
private IEmailProvider EmailProvider { get; }
@ -415,6 +415,7 @@ namespace Ombi.Controllers.V1.External
return new TesterResultModel
{
IsValid = result.urlBase == settings.SubDir || string.IsNullOrEmpty(result.urlBase) && string.IsNullOrEmpty(settings.SubDir),
Version = result.version,
ExpectedSubDir = result.urlBase
};
}

Loading…
Cancel
Save