#865 Added support for multiple plex servers

This commit is contained in:
Jamie.Rees 2017-05-15 15:30:12 +01:00
commit 3879fc04de
11 changed files with 270 additions and 177 deletions

View file

@ -73,7 +73,8 @@ namespace Ombi.Schedule.Jobs
StartTheCache(plexSettings).Wait(); StartTheCache(plexSettings).Wait();
} }
catch (Exception e) { catch (Exception e)
{
Logger.LogWarning(LoggingEvents.CacherException, e, "Exception thrown when attempting to cache the Plex Content"); Logger.LogWarning(LoggingEvents.CacherException, e, "Exception thrown when attempting to cache the Plex Content");
} }
} }
@ -119,7 +120,10 @@ namespace Ombi.Schedule.Jobs
private async Task StartTheCache(PlexSettings plexSettings) private async Task StartTheCache(PlexSettings plexSettings)
{ {
var allContent = GetAllContent(plexSettings); foreach (var servers in plexSettings.Servers ?? new List<PlexServers>())
{
var allContent = GetAllContent(servers);
// Let's now process this. // Let's now process this.
@ -131,7 +135,7 @@ namespace Ombi.Schedule.Jobs
// Process Shows // Process Shows
foreach (var metadata in content.Metadata) foreach (var metadata in content.Metadata)
{ {
var seasonList = await PlexApi.GetSeasons(plexSettings.PlexAuthToken, plexSettings.FullUri, var seasonList = await PlexApi.GetSeasons(servers.PlexAuthToken, servers.FullUri,
metadata.ratingKey); metadata.ratingKey);
var seasonsContent = new List<SeasonsContent>(); var seasonsContent = new List<SeasonsContent>();
foreach (var season in seasonList.MediaContainer.Metadata) foreach (var season in seasonList.MediaContainer.Metadata)
@ -166,7 +170,7 @@ namespace Ombi.Schedule.Jobs
// Get the show metadata... This sucks since the `metadata` var contains all information about the show // Get the show metadata... This sucks since the `metadata` var contains all information about the show
// But it does not contain the `guid` property that we need to pull out thetvdb id... // But it does not contain the `guid` property that we need to pull out thetvdb id...
var showMetadata = await PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri, var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
metadata.ratingKey); metadata.ratingKey);
var item = new PlexContent var item = new PlexContent
{ {
@ -178,7 +182,7 @@ namespace Ombi.Schedule.Jobs
ReleaseYear = metadata.year.ToString(), ReleaseYear = metadata.year.ToString(),
Type = PlexMediaTypeEntity.Show, Type = PlexMediaTypeEntity.Show,
Title = metadata.title, Title = metadata.title,
Url = PlexHelper.GetPlexMediaUrl(plexSettings.MachineIdentifier, metadata.ratingKey), Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, metadata.ratingKey),
Seasons = new List<SeasonsContent>() Seasons = new List<SeasonsContent>()
}; };
@ -194,9 +198,11 @@ namespace Ombi.Schedule.Jobs
{ {
await Repo.AddRange(contentToAdd); await Repo.AddRange(contentToAdd);
} }
}
} }
private List<Mediacontainer> GetAllContent(PlexSettings plexSettings) private List<Mediacontainer> GetAllContent(PlexServers plexSettings)
{ {
var sections = PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri).Result; var sections = PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri).Result;
@ -232,11 +238,14 @@ namespace Ombi.Schedule.Jobs
{ {
if (plex.Enable) if (plex.Enable)
{ {
if (string.IsNullOrEmpty(plex?.Ip) || string.IsNullOrEmpty(plex?.PlexAuthToken)) foreach (var server in plex.Servers ?? new List<PlexServers>())
{
if (string.IsNullOrEmpty(server?.Ip) || string.IsNullOrEmpty(server?.PlexAuthToken))
{ {
return false; return false;
} }
} }
}
return plex.Enable; return plex.Enable;
} }
} }

View file

@ -2,10 +2,16 @@
namespace Ombi.Core.Settings.Models.External namespace Ombi.Core.Settings.Models.External
{ {
public sealed class PlexSettings : ExternalSettings public sealed class PlexSettings : Ombi.Settings.Settings.Models.Settings
{ {
public bool Enable { get; set; } public bool Enable { get; set; }
public List<PlexServers> Servers { get; set; }
}
public class PlexServers : ExternalSettings
{
public string Name { get; set; }
public bool EnableEpisodeSearching { get; set; } public bool EnableEpisodeSearching { get; set; }
public string PlexAuthToken { get; set; } public string PlexAuthToken { get; set; }
@ -13,7 +19,6 @@ namespace Ombi.Core.Settings.Models.External
public List<PlexSelectedLibraries> PlexSelectedLibraries { get; set; } public List<PlexSelectedLibraries> PlexSelectedLibraries { get; set; }
} }
public class PlexSelectedLibraries public class PlexSelectedLibraries
{ {
public int Key { get; set; } public int Key { get; set; }

View file

@ -1,4 +1,6 @@
using System.Linq; using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -28,29 +30,42 @@ namespace Ombi.Controllers.External
{ {
// Do we already have settings? // Do we already have settings?
var settings = await PlexSettings.GetSettingsAsync(); var settings = await PlexSettings.GetSettingsAsync();
if (!string.IsNullOrEmpty(settings?.PlexAuthToken)) return null; if (!settings.Servers?.Any() ?? false) return null;
var result = await PlexApi.SignIn(request); var result = await PlexApi.SignIn(request);
if (!string.IsNullOrEmpty(result.user?.authentication_token)) if (!string.IsNullOrEmpty(result.user?.authentication_token))
{ {
var server = await PlexApi.GetServer(result.user.authentication_token); var server = await PlexApi.GetServer(result.user.authentication_token);
var firstServer = server.Server.FirstOrDefault(); var servers = server.Server;
await PlexSettings.SaveSettingsAsync(new PlexSettings
settings.Servers = new List<PlexServers>();
var serverNumber = 0;
foreach (var s in servers)
{
if (string.IsNullOrEmpty(s.LocalAddresses) || string.IsNullOrEmpty(s.Port))
{
continue;
}
settings.Servers.Add(new PlexServers
{ {
Enable = true,
PlexAuthToken = result.user.authentication_token, PlexAuthToken = result.user.authentication_token,
Ip = firstServer.LocalAddresses, Id = new Random().Next(),
MachineIdentifier = firstServer.MachineIdentifier, Ip = s.LocalAddresses.Split(new []{','}, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault(),
Port = int.Parse(firstServer.Port), MachineIdentifier = s.MachineIdentifier,
Ssl = firstServer.Scheme != "http", Port = int.Parse(s.Port),
Ssl = s.Scheme != "http",
Name = $"Server{serverNumber++}"
}); });
} }
await PlexSettings.SaveSettingsAsync(settings);
}
return result; return result;
} }
[HttpPost("Libraries")] [HttpPost("Libraries")]
public async Task<PlexLibraries> GetPlexLibraries([FromBody] PlexSettings settings) public async Task<PlexLibraries> GetPlexLibraries([FromBody] PlexServers settings)
{ {
var libs = await PlexApi.GetLibrarySections(settings.PlexAuthToken, settings.FullUri); var libs = await PlexApi.GetLibrarySections(settings.PlexAuthToken, settings.FullUri);

View file

@ -35,6 +35,7 @@ var paths = {
'@angular/forms', '@angular/forms',
'@angular/platform-browser/animations', '@angular/platform-browser/animations',
'@angular/material', '@angular/material',
'@ng-bootstrap/ng-bootstrap',
'ngx-infinite-scroll' 'ngx-infinite-scroll'
], ],
dest: './lib' dest: './lib'

View file

@ -15,6 +15,7 @@
"@angular/platform-browser-dynamic": "^4.1.0", "@angular/platform-browser-dynamic": "^4.1.0",
"@angular/platform-server": "^4.1.0", "@angular/platform-server": "^4.1.0",
"@angular/router": "^4.1.0", "@angular/router": "^4.1.0",
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.25",
"@types/jquery": "^2.0.33", "@types/jquery": "^2.0.33",
"@types/systemjs": "^0.20.2", "@types/systemjs": "^0.20.2",
"angular2-jwt": "^0.2.0", "angular2-jwt": "^0.2.0",

View file

@ -3,6 +3,7 @@ import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { MdButtonModule } from '@angular/material'; import { MdButtonModule } from '@angular/material';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
@ -75,7 +76,8 @@ const routes: Routes = [
AuthModule, AuthModule,
WizardModule, WizardModule,
DialogModule, DialogModule,
MdButtonModule MdButtonModule,
NgbModule.forRoot(),
], ],
declarations: [ declarations: [
AppComponent, AppComponent,

View file

@ -4,7 +4,6 @@
export interface IExternalSettings extends ISettings { export interface IExternalSettings extends ISettings {
ssl: boolean, ssl: boolean,
enable:boolean,
subDir: string, subDir: string,
ip: string, ip: string,
port:number, port:number,
@ -20,11 +19,19 @@ export interface IOmbiSettings extends ISettings {
export interface IEmbySettings extends IExternalSettings { export interface IEmbySettings extends IExternalSettings {
apiKey: string, apiKey: string,
enable: boolean,
administratorId: string, administratorId: string,
enableEpisodeSearching:boolean, enableEpisodeSearching:boolean,
} }
export interface IPlexSettings extends IExternalSettings { export interface IPlexSettings extends ISettings {
enable: boolean,
servers : IPlexServer[]
}
export interface IPlexServer extends IExternalSettings {
name:string,
enableEpisodeSearching: boolean, enableEpisodeSearching: boolean,
plexAuthToken: string, plexAuthToken: string,
machineIdentifier: string, machineIdentifier: string,
@ -39,6 +46,7 @@ export interface IPlexLibraries {
export interface ISonarrSettings extends IExternalSettings { export interface ISonarrSettings extends IExternalSettings {
apiKey: string, apiKey: string,
enable: boolean,
qualityProfile: string, qualityProfile: string,
seasonFolders: boolean, seasonFolders: boolean,
rootPath: string, rootPath: string,

View file

@ -2,7 +2,7 @@
<h1>Login</h1> <h1>Login</h1>
<div> <div>
<p> <p>
@UI.UserLogin_Paragraph <span title="@UI.UserLogin_Paragraph_SpanHover"><i class="fa fa-question-circle"></i></span> Hey! Welcome, login with your credentails below!
</p> </p>
</div> </div>
<form method="POST" id="loginForm"> <form method="POST" id="loginForm">

View file

@ -10,25 +10,47 @@
<label for="enable">Enable</label> <label for="enable">Enable</label>
</div> </div>
</div> </div>
<div style="float: right;">
<button type="submit" (click)="addTab()" class="btn btn-success-outline">Add Server</button>
</div>
<ngb-tabset>
<div *ngFor="let server of settings.servers">
<ngb-tab [id]="server.id" [title]="server.name">
<ng-template ngbTabContent>
<br/>
<br/>
<div style="float: right;">
<button type="submit" (click)="removeServer(server)" class="btn btn-danger-outline">Remove Server</button>
</div>
<br/>
<br/>
<div class="form-group">
<label for="name" class="control-label">Server name</label>
<div>
<input type="text" class="form-control form-control-custom " id="name" name="name" placeholder="Server" [(ngModel)]="server.name" value="{{server.name}}">
</div>
</div>
<div class="form-group"> <div class="form-group">
<label for="Ip" class="control-label">Hostname or IP</label> <label for="Ip" class="control-label">Hostname or IP</label>
<div> <div>
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" [(ngModel)]="settings.ip" value="{{settings.ip}}"> <input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" [(ngModel)]="server.ip" value="{{server.ip}}">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="portNumber" class="control-label">Port</label> <label for="portNumber" class="control-label">Port</label>
<div> <div>
<input type="text" [(ngModel)]="settings.port" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="{{settings.port}}"> <input type="text" [(ngModel)]="settings.port" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="{{server.port}}">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" id="ssl" [(ngModel)]="settings.ssl" ng-checked="settings.ssl"> <input type="checkbox" id="ssl" [(ngModel)]="server.ssl" ng-checked="server.ssl">
<label for="ssl">SSL</label> <label for="ssl">SSL</label>
</div> </div>
</div> </div>
@ -37,7 +59,7 @@
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" id="EnableTvEpisodeSearching" [(ngModel)]="settings.enableEpisodeSearching" ng-checked="settings.enableEpisodeSearching"> <input type="checkbox" id="EnableTvEpisodeSearching" [(ngModel)]="server.enableEpisodeSearching" ng-checked="server.enableEpisodeSearching">
<label for="EnableTvEpisodeSearching">Enable Episode Searching</label> <label for="EnableTvEpisodeSearching">Enable Episode Searching</label>
</div> </div>
@ -51,14 +73,14 @@
<div class="form-group"> <div class="form-group">
<label for="authToken" class="control-label">Plex Authorization Token</label> <label for="authToken" class="control-label">Plex Authorization Token</label>
<div class=""> <div class="">
<input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="settings.plexAuthToken" placeholder="Plex Auth Token" value="{{settings.plexAuthToken}}"> <input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="server.plexAuthToken" placeholder="Plex Auth Token" value="{{server.plexAuthToken}}">
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="MachineIdentifier" class="control-label">Machine Identifier</label> <label for="MachineIdentifier" class="control-label">Machine Identifier</label>
<div class=""> <div class="">
<input type="text" class="form-control-custom form-control" id="MachineIdentifier" name="MachineIdentifier" [(ngModel)]="settings.machineIdentifier" value="{{settings.machineIdentifier}}"> <input type="text" class="form-control-custom form-control" id="MachineIdentifier" name="MachineIdentifier" [(ngModel)]="server.machineIdentifier" value="{{server.machineIdentifier}}">
</div> </div>
</div> </div>
@ -84,8 +106,8 @@
<button (click)="loadLibraries()" class="btn btn-primary-outline">Load Libraries <i class="fa fa-film"></i></button> <button (click)="loadLibraries()" class="btn btn-primary-outline">Load Libraries <i class="fa fa-film"></i></button>
</div> </div>
</div> </div>
<div *ngIf="settings.plexSelectedLibraries"> <div *ngIf="server.plexSelectedLibraries">
<div *ngFor="let lib of settings.plexSelectedLibraries"> <div *ngFor="let lib of server.plexSelectedLibraries">
<div class="form-group"> <div class="form-group">
<div class="checkbox"> <div class="checkbox">
<input type="checkbox" id="{{lib.title}}" [(ngModel)]="lib.enabled" ng-checked="lib.enabled"> <input type="checkbox" id="{{lib.title}}" [(ngModel)]="lib.enabled" ng-checked="lib.enabled">
@ -98,9 +120,17 @@
<div class="form-group"> <div class="form-group">
<div> <div>
<button id="testPlex" type="submit" (click)="testPlex()" class="btn btn-primary-outline">Test Connectivity <div id="spinner"></div></button> <button id="testPlex" type="submit" (click)="testPlex()" class="btn btn-primary-outline">
Test Connectivity
<div id="spinner"></div>
</button>
</div> </div>
</div> </div>
</ng-template>
</ngb-tab>
</div>
</ngb-tabset>

View file

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { IPlexSettings, IPlexLibraries } from '../../interfaces/ISettings' import { IPlexSettings, IPlexLibraries, IPlexServer } from '../../interfaces/ISettings'
import { SettingsService } from '../../services/settings.service'; import { SettingsService } from '../../services/settings.service';
@ -21,7 +21,10 @@ export class PlexComponent implements OnInit {
password: string; password: string;
ngOnInit(): void { ngOnInit(): void {
this.settingsService.getPlex().subscribe(x => this.settings = x); this.settingsService.getPlex().subscribe(x => {
this.settings = x;
}
);
} }
requestToken() { requestToken() {
@ -32,23 +35,40 @@ export class PlexComponent implements OnInit {
// TODO Plex Service // TODO Plex Service
} }
loadLibraries() { addTab() {
//this.settings.servers.push(<IPlexServer>{ name: "New*", id: Math.floor(Math.random() * (99999 - 0 + 1) + 1) });
this.notificationService.warning("Disabled", "This feature is currently disabled");
}
removeServer(server: IPlexServer) {
this.notificationService.warning("Disabled", "This feature is currently disabled");
//var index = this.settings.servers.indexOf(server, 0);
//if (index > -1) {
// this.settings.servers.splice(index, 1);
//}
}
loadLibraries(server:IPlexServer) {
this.plexService.getLibraries(this.settings).subscribe(x => { this.plexService.getLibraries(this.settings).subscribe(x => {
this.settings.plexSelectedLibraries = []; server.plexSelectedLibraries = [];
x.mediaContainer.directory.forEach((item, index) => { x.mediaContainer.directory.forEach((item, index) => {
var lib: IPlexLibraries = { var lib: IPlexLibraries = {
key: item.key, key: item.key,
title: item.title, title: item.title,
enabled: false enabled: false
}; };
this.settings.plexSelectedLibraries.push(lib); server.plexSelectedLibraries.push(lib);
}); });
}); });
} }
save() { save() {
var filtered = this.settings.servers.filter(x => x.name !== "");
this.settings.servers = filtered;
this.settingsService.savePlex(this.settings).subscribe(x => { this.settingsService.savePlex(this.settings).subscribe(x => {
if (x) { if (x) {
this.notificationService.success("Settings Saved", "Successfully saved Plex settings"); this.notificationService.success("Settings Saved", "Successfully saved Plex settings");

View file

@ -2,6 +2,7 @@
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router'; import { RouterModule, Routes } from '@angular/router';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { AuthService } from '../auth/auth.service'; import { AuthService } from '../auth/auth.service';
import { AuthGuard } from '../auth/auth.guard'; import { AuthGuard } from '../auth/auth.guard';
@ -34,7 +35,8 @@ const routes: Routes = [
MenuModule, MenuModule,
InputSwitchModule, InputSwitchModule,
InputTextModule, InputTextModule,
AuthModule AuthModule,
NgbModule
], ],
declarations: [ declarations: [
SettingsMenuComponent, SettingsMenuComponent,