mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-16 02:02:55 -07:00
feat(sonarr): Added the ability to add default tags when sending to Sonarr (#4803)
This commit is contained in:
parent
3e23d36ac3
commit
ecfbb8eda9
8 changed files with 151 additions and 46 deletions
|
@ -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>
|
||||
|
||||
<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 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>Language Profiles </mat-label>
|
||||
<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="md-form-field" style="display:contents;">
|
||||
<mat-form-field appearance="outline">
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group col-md-7">
|
||||
<div>
|
||||
<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…
Add table
Add a link
Reference in a new issue