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";
|
||||
.small-middle-container {
|
||||
margin: auto;
|
||||
width: 95%;
|
||||
margin-top: 10px;
|
||||
margin: auto;
|
||||
width: 95%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.col-md-10 {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.col-md-2 {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.control-label {
|
||||
font-weight: 400;
|
||||
}
|
||||
.emby-server-card {
|
||||
margin: 0em 1em 1em 0;
|
||||
width: 13em;
|
||||
min-height: 8em;
|
||||
|
||||
.row {
|
||||
display: block;
|
||||
}
|
||||
button {
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
white-space: normal;
|
||||
overflow-wrap: anywhere;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-danger-outline {
|
||||
background-color: #E84C3D;
|
||||
}
|
||||
i {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.btn-success-outline {
|
||||
background-color: #1b9d1b;
|
||||
h3 {
|
||||
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