mirror of https://github.com/Ombi-app/Ombi
Merge 853ca6b6bd
into ee3bf10421
commit
1331c49934
@ -0,0 +1,113 @@
|
|||||||
|
<h1 mat-dialog-title>Server Configuration</h1>
|
||||||
|
<mat-dialog-content>
|
||||||
|
<form #embyServerForm="ngForm">
|
||||||
|
<h2>Connection</h2>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" floatLabel=auto>
|
||||||
|
<mat-label>Server Name</mat-label>
|
||||||
|
<input matInput placeholder="Server Name" name="name" [(ngModel)]="server.name" value="{{server.name}}"
|
||||||
|
(change)="processChangeEvent()">
|
||||||
|
<mat-hint>Auto populated during discovery of the server if left empty.</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<mat-form-field class="col-md-6 col-12" appearance="outline" floatLabel=auto>
|
||||||
|
<mat-label>Hostname / IP</mat-label>
|
||||||
|
<input matInput placeholder="Hostname or IP" name="ip" [(ngModel)]="server.ip" value="{{server.ip}}"
|
||||||
|
(change)="processChangeEvent()" #serverHostnameIpControl="ngModel" required>
|
||||||
|
<mat-error *ngIf="serverHostnameIpControl.hasError('required')">Must be specified.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field class="col-md-4 col-7" appearance="outline" floatLabel=auto>
|
||||||
|
<mat-label>Port</mat-label>
|
||||||
|
<input matInput placeholder="Port" name="port" [(ngModel)]="server.port" value="{{server.port}}"
|
||||||
|
(change)="processChangeEvent()" #serverPortControl="ngModel" required pattern="^[0-9]*$">
|
||||||
|
<mat-error *ngIf="serverPortControl.hasError('required')">Must be specified.</mat-error>
|
||||||
|
<mat-error *ngIf="serverPortControl.hasError('pattern')">Must be a number.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-slide-toggle class="col-md-2 col-5 mt-3" id="ssl" name="ssl" [(ngModel)]="server.ssl" [checked]="server.ssl"
|
||||||
|
(change)="processChangeEvent()">
|
||||||
|
SSL
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" floatLabel=auto>
|
||||||
|
<mat-label>Base URL</mat-label>
|
||||||
|
<input matInput placeholder="Base Url" name="subDir" [(ngModel)]="server.subDir" value="{{server.subDir}}"
|
||||||
|
(change)="processChangeEvent()">
|
||||||
|
<mat-hint>Optional url path to be used with reverse proxy. Example: 'emby' to get
|
||||||
|
'https://ip:port/emby'.</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" floatLabel=auto>
|
||||||
|
<mat-label>API Key</mat-label>
|
||||||
|
<input matInput placeholder="Api Key" name="apiKey" [(ngModel)]="server.apiKey" value="{{server.apiKey}}"
|
||||||
|
(change)="processChangeEvent()" #serverApiKeyControl="ngModel" required>
|
||||||
|
<mat-error *ngIf="serverApiKeyControl.hasError('required')">Must be specified.</mat-error>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<mat-form-field appearance="outline" floatLabel=auto>
|
||||||
|
<mat-label>Externally Facing Hostname</mat-label>
|
||||||
|
<input matInput placeholder="e.g. https://emby.server.com/" name="serverHostname"
|
||||||
|
[(ngModel)]="server.serverHostname" value="{{server.serverHostname}}" (change)="processChangeEvent()">
|
||||||
|
<mat-hint>
|
||||||
|
The external address that users will navigate to when they press the 'View On Emby'
|
||||||
|
button.
|
||||||
|
<br />
|
||||||
|
<span>Current URL: {{server.serverHostname ? server.serverHostname :
|
||||||
|
'https://app.emby.media'}}/#!/item/item.html?id=1</span>
|
||||||
|
</mat-hint>
|
||||||
|
</mat-form-field>
|
||||||
|
|
||||||
|
<h2>Libraries</h2>
|
||||||
|
<label *ngIf="server.embySelectedLibraries && server.embySelectedLibraries.length == 0">
|
||||||
|
Discover the server to load available libraries. If you still not seeing any libraries, make sure they are
|
||||||
|
configured in Emby.</label>
|
||||||
|
<div *ngIf="server.embySelectedLibraries && server.embySelectedLibraries.length > 0">
|
||||||
|
<label>Please select the libraries for Ombi to monitor. If nothing is selected, Ombi will monitor all
|
||||||
|
libraries.</label>
|
||||||
|
<div *ngFor="let lib of server.embySelectedLibraries">
|
||||||
|
<div class="md-form-field">
|
||||||
|
<div class="checkbox">
|
||||||
|
<mat-slide-toggle name="library-{{lib.key}}" [(ngModel)]="lib.enabled" [checked]="lib.enabled"
|
||||||
|
(change)="processChangeEvent()">
|
||||||
|
{{lib.title}}
|
||||||
|
</mat-slide-toggle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</mat-dialog-content>
|
||||||
|
|
||||||
|
<mat-dialog-actions align=end>
|
||||||
|
<button style="margin: .5em 0 0 .5em;" align-middle mat-stroked-button color="warn" *ngIf="!data.isNewServer"
|
||||||
|
(click)="delete()">
|
||||||
|
<span style="display: flex; align-items: baseline; white-space: pre-wrap;">
|
||||||
|
<i class="fas fa-trash"></i>
|
||||||
|
<span> Delete</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button style="margin: .5em 0 0 0.5em;" mat-stroked-button color="basic" (click)="cancel()">
|
||||||
|
<span style="display: flex; align-items: baseline; white-space: pre-wrap;">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
<span> Cancel</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button style="margin: .5em 0 0 .5em;" mat-stroked-button color="{{serverDiscoveryRequired ? 'accent' : 'basic'}}"
|
||||||
|
(click)="discoverServer()" id="discover">
|
||||||
|
<span>Discover Server</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button style="margin: .5em 0 0 .5em;" mat-stroked-button color="accent"
|
||||||
|
[disabled]="!isChangeDetected || serverDiscoveryRequired || isServerNameMissing" (click)="save()">
|
||||||
|
<span style="display: flex; align-items: baseline; white-space: pre-wrap;">
|
||||||
|
<i style="vertical-align: text-top;" class="fas fa-plus"></i>
|
||||||
|
<span> Save</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</mat-dialog-actions>
|
@ -0,0 +1,28 @@
|
|||||||
|
@media (max-width: 978px) {
|
||||||
|
::ng-deep .mat-dialog-container {
|
||||||
|
overflow: unset;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.mat-dialog-content{
|
||||||
|
max-height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mat-dialog-actions{
|
||||||
|
min-height: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
emby-server-dialog-component {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-height: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::ng-deep mat-form-field .mat-form-field {
|
||||||
|
&-subscript-wrapper {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,217 @@
|
|||||||
|
import { Component, Inject, ViewChild } from "@angular/core";
|
||||||
|
import { NgForm } from "@angular/forms";
|
||||||
|
import { MatDialogRef, MAT_DIALOG_DATA } from "@angular/material/dialog";
|
||||||
|
import {
|
||||||
|
IEmbyLibrariesSettings,
|
||||||
|
IEmbyServer,
|
||||||
|
IEmbySettings,
|
||||||
|
} from "app/interfaces";
|
||||||
|
import {
|
||||||
|
EmbyService,
|
||||||
|
NotificationService,
|
||||||
|
SettingsService,
|
||||||
|
TesterService,
|
||||||
|
} from "app/services";
|
||||||
|
import { isEqual } from "lodash";
|
||||||
|
|
||||||
|
export interface EmbyServerDialogData {
|
||||||
|
server: IEmbyServer;
|
||||||
|
isNewServer: boolean;
|
||||||
|
savedSettings: IEmbySettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "emby-server-dialog-component",
|
||||||
|
templateUrl: "emby-server-dialog.component.html",
|
||||||
|
styleUrls: ["emby-server-dialog.component.scss"],
|
||||||
|
})
|
||||||
|
export class EmbyServerDialog {
|
||||||
|
@ViewChild("embyServerForm") embyServerForm: NgForm;
|
||||||
|
public server: IEmbyServer;
|
||||||
|
public isServerNameMissing: boolean;
|
||||||
|
public isChangeDetected: boolean;
|
||||||
|
public serverDiscoveryRequired: boolean;
|
||||||
|
private validatedFields: {
|
||||||
|
ip: string;
|
||||||
|
port: number;
|
||||||
|
ssl: boolean;
|
||||||
|
apiKey: string;
|
||||||
|
subDir: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private dialogRef: MatDialogRef<EmbyServerDialog>,
|
||||||
|
@Inject(MAT_DIALOG_DATA) public data: EmbyServerDialogData,
|
||||||
|
private notificationService: NotificationService,
|
||||||
|
private settingsService: SettingsService,
|
||||||
|
private testerService: TesterService,
|
||||||
|
private embyService: EmbyService
|
||||||
|
) {
|
||||||
|
this.server = structuredClone(data.server);
|
||||||
|
this.isChangeDetected = false;
|
||||||
|
this.serverDiscoveryRequired = data.isNewServer;
|
||||||
|
this.isServerNameMissing = !this.server.name;
|
||||||
|
this.validatedFields = {
|
||||||
|
ip: this.server.ip,
|
||||||
|
port: this.server.port,
|
||||||
|
ssl: this.server.ssl,
|
||||||
|
apiKey: this.server.apiKey,
|
||||||
|
subDir: this.server.subDir,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public processChangeEvent() {
|
||||||
|
if (
|
||||||
|
this.validatedFields.ip !== this.server.ip ||
|
||||||
|
this.validatedFields.port?.toString() !== this.server.port?.toString() ||
|
||||||
|
this.validatedFields.ssl !== this.server.ssl ||
|
||||||
|
this.validatedFields.apiKey !== this.server.apiKey ||
|
||||||
|
this.validatedFields.subDir !== this.server.subDir ||
|
||||||
|
!this.embyServerForm.valid
|
||||||
|
) {
|
||||||
|
this.serverDiscoveryRequired = true;
|
||||||
|
} else {
|
||||||
|
this.serverDiscoveryRequired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isServerNameMissing = !this.server.name;
|
||||||
|
this.isChangeDetected = !isEqual(this.data.server, this.server);
|
||||||
|
}
|
||||||
|
|
||||||
|
public cancel() {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public delete() {
|
||||||
|
const settings: IEmbySettings = structuredClone(this.data.savedSettings);
|
||||||
|
const index = settings.servers.findIndex((i) => i.id === this.server.id);
|
||||||
|
if (index == -1) return;
|
||||||
|
|
||||||
|
settings.servers.splice(index, 1);
|
||||||
|
const errorMessage = "There was an error removing the server.";
|
||||||
|
this.settingsService.saveEmby(settings).subscribe({
|
||||||
|
next: (result) => {
|
||||||
|
if (result) {
|
||||||
|
this.data.savedSettings.servers.splice(index, 1);
|
||||||
|
this.dialogRef.close();
|
||||||
|
} else {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public save() {
|
||||||
|
const settings: IEmbySettings = structuredClone(this.data.savedSettings);
|
||||||
|
if (this.data.isNewServer) {
|
||||||
|
settings.servers.push(this.server);
|
||||||
|
} else {
|
||||||
|
const index = settings.servers.findIndex((i) => i.id === this.server.id);
|
||||||
|
if (index !== -1) settings.servers[index] = this.server;
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorMessage = "There was an error saving the server.";
|
||||||
|
this.settingsService.saveEmby(settings).subscribe({
|
||||||
|
next: (result) => {
|
||||||
|
if (result) {
|
||||||
|
if (this.data.isNewServer) {
|
||||||
|
this.data.savedSettings.servers.push(this.server);
|
||||||
|
} else {
|
||||||
|
const index = this.data.savedSettings.servers.findIndex(
|
||||||
|
(i) => i.id === this.server.id
|
||||||
|
);
|
||||||
|
if (index !== -1)
|
||||||
|
this.data.savedSettings.servers[index] = this.server;
|
||||||
|
}
|
||||||
|
this.dialogRef.close();
|
||||||
|
} else {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public discoverServer() {
|
||||||
|
this.embyServerForm.form.markAllAsTouched();
|
||||||
|
if (!this.embyServerForm.valid) return;
|
||||||
|
|
||||||
|
const errorMessage = `Failed to connect to the server. Make sure configuration is correct.`;
|
||||||
|
this.testerService.embyTest(this.server).subscribe({
|
||||||
|
next: (result) => {
|
||||||
|
if (!result) {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.retrieveServerNameAndId(this.server);
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private retrieveServerNameAndId(server: IEmbyServer) {
|
||||||
|
const errorMessage =
|
||||||
|
"Failed to discover server. Make sure configuration is correct.";
|
||||||
|
this.embyService.getPublicInfo(server).subscribe({
|
||||||
|
next: (result) => {
|
||||||
|
if (!result) {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server.name) {
|
||||||
|
server.name = result.serverName;
|
||||||
|
this.isServerNameMissing = false;
|
||||||
|
}
|
||||||
|
server.serverId = result.id;
|
||||||
|
this.loadLibraries(server);
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadLibraries(server: IEmbyServer) {
|
||||||
|
this.embyService.getLibraries(server).subscribe({
|
||||||
|
next: (result) => {
|
||||||
|
server.embySelectedLibraries = result.items.map((item) => {
|
||||||
|
const index = server.embySelectedLibraries.findIndex(
|
||||||
|
(x) => x.key == item.id
|
||||||
|
);
|
||||||
|
const enabled =
|
||||||
|
index === -1 ? false : server.embySelectedLibraries[index].enabled;
|
||||||
|
const lib: IEmbyLibrariesSettings = {
|
||||||
|
key: item.id,
|
||||||
|
title: item.name,
|
||||||
|
enabled: enabled,
|
||||||
|
collectionType: item.collectionType,
|
||||||
|
};
|
||||||
|
return lib;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.serverDiscoveryRequired = false;
|
||||||
|
this.validatedFields = {
|
||||||
|
ip: this.server.ip,
|
||||||
|
port: this.server.port,
|
||||||
|
ssl: this.server.ssl,
|
||||||
|
apiKey: this.server.apiKey,
|
||||||
|
subDir: this.server.subDir,
|
||||||
|
};
|
||||||
|
this.notificationService.success("Successfully discovered the server.");
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
const errorMessage = "There was an error retrieving libraries.";
|
||||||
|
this.notificationService.error(errorMessage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,41 +1,29 @@
|
|||||||
@import "./styles/shared.scss";
|
@import "./styles/shared.scss";
|
||||||
.small-middle-container {
|
.small-middle-container {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
width: 95%;
|
width: 95%;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.col-md-10 {
|
.emby-server-card {
|
||||||
display: grid;
|
margin: 0em 1em 1em 0;
|
||||||
}
|
width: 13em;
|
||||||
|
min-height: 8em;
|
||||||
.col-md-2 {
|
|
||||||
display: contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
.control-label {
|
|
||||||
font-weight: 400;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row {
|
button {
|
||||||
display: block;
|
text-align: center;
|
||||||
}
|
align-content: center;
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-danger-outline {
|
i {
|
||||||
background-color: #E84C3D;
|
margin-top: 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success-outline {
|
h3 {
|
||||||
background-color: #1b9d1b;
|
margin: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::ng-deep .dark .btn:hover {
|
|
||||||
box-shadow: 0 5px 11px 0 rgba(255, 255, 255, 0.18), 0 4px 15px 0 rgba(255, 255, 255, 0.15);
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width:1440px) {
|
|
||||||
.col-md-2 {
|
|
||||||
display: inline-table;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in new issue