#865 Added support for multiple plex servers

pull/1425/head
Jamie.Rees 7 years ago
parent a278917dcd
commit 3879fc04de

@ -73,7 +73,8 @@ namespace Ombi.Schedule.Jobs
StartTheCache(plexSettings).Wait();
}
catch (Exception e) {
catch (Exception e)
{
Logger.LogWarning(LoggingEvents.CacherException, e, "Exception thrown when attempting to cache the Plex Content");
}
}
@ -119,87 +120,92 @@ namespace Ombi.Schedule.Jobs
private async Task StartTheCache(PlexSettings plexSettings)
{
var allContent = GetAllContent(plexSettings);
foreach (var servers in plexSettings.Servers ?? new List<PlexServers>())
{
// Let's now process this.
var allContent = GetAllContent(servers);
var contentToAdd = new List<PlexContent>();
foreach (var content in allContent)
{
if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
// Let's now process this.
var contentToAdd = new List<PlexContent>();
foreach (var content in allContent)
{
// Process Shows
foreach (var metadata in content.Metadata)
if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase))
{
var seasonList = await PlexApi.GetSeasons(plexSettings.PlexAuthToken, plexSettings.FullUri,
metadata.ratingKey);
var seasonsContent = new List<SeasonsContent>();
foreach (var season in seasonList.MediaContainer.Metadata)
// Process Shows
foreach (var metadata in content.Metadata)
{
seasonsContent.Add(new SeasonsContent
var seasonList = await PlexApi.GetSeasons(servers.PlexAuthToken, servers.FullUri,
metadata.ratingKey);
var seasonsContent = new List<SeasonsContent>();
foreach (var season in seasonList.MediaContainer.Metadata)
{
ParentKey = int.Parse(season.parentRatingKey),
SeasonKey = int.Parse(season.ratingKey),
SeasonNumber = season.index
});
}
seasonsContent.Add(new SeasonsContent
{
ParentKey = int.Parse(season.parentRatingKey),
SeasonKey = int.Parse(season.ratingKey),
SeasonNumber = season.index
});
}
// Do we already have this item?
var existingContent = await Repo.GetByKey(metadata.key);
if (existingContent != null)
{
// Ok so we have it, let's check if there are any new seasons
var seasonDifference = seasonsContent.Except(existingContent.Seasons).ToList();
if (seasonDifference.Any())
// Do we already have this item?
var existingContent = await Repo.GetByKey(metadata.key);
if (existingContent != null)
{
// We have new seasons on Plex, let's add them back into the entity
existingContent.Seasons.AddRange(seasonDifference);
await Repo.Update(existingContent);
continue;
// Ok so we have it, let's check if there are any new seasons
var seasonDifference = seasonsContent.Except(existingContent.Seasons).ToList();
if (seasonDifference.Any())
{
// We have new seasons on Plex, let's add them back into the entity
existingContent.Seasons.AddRange(seasonDifference);
await Repo.Update(existingContent);
continue;
}
else
{
// No changes, no need to do anything
continue;
}
}
else
// 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...
var showMetadata = await PlexApi.GetMetadata(servers.PlexAuthToken, servers.FullUri,
metadata.ratingKey);
var item = new PlexContent
{
// No changes, no need to do anything
continue;
}
AddedAt = DateTime.Now,
Key = metadata.ratingKey,
ProviderId = PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata
.FirstOrDefault()
.guid),
ReleaseYear = metadata.year.ToString(),
Type = PlexMediaTypeEntity.Show,
Title = metadata.title,
Url = PlexHelper.GetPlexMediaUrl(servers.MachineIdentifier, metadata.ratingKey),
Seasons = new List<SeasonsContent>()
};
item.Seasons.AddRange(seasonsContent);
contentToAdd.Add(item);
}
// 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...
var showMetadata = await PlexApi.GetMetadata(plexSettings.PlexAuthToken, plexSettings.FullUri,
metadata.ratingKey);
var item = new PlexContent
{
AddedAt = DateTime.Now,
Key = metadata.ratingKey,
ProviderId = PlexHelper.GetProviderIdFromPlexGuid(showMetadata.MediaContainer.Metadata
.FirstOrDefault()
.guid),
ReleaseYear = metadata.year.ToString(),
Type = PlexMediaTypeEntity.Show,
Title = metadata.title,
Url = PlexHelper.GetPlexMediaUrl(plexSettings.MachineIdentifier, metadata.ratingKey),
Seasons = new List<SeasonsContent>()
};
item.Seasons.AddRange(seasonsContent);
contentToAdd.Add(item);
}
}
}
if (contentToAdd.Any())
{
await Repo.AddRange(contentToAdd);
if (contentToAdd.Any())
{
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 libs = new List<Mediacontainer>();
if (sections != null)
{
@ -232,9 +238,12 @@ namespace Ombi.Schedule.Jobs
{
if (plex.Enable)
{
if (string.IsNullOrEmpty(plex?.Ip) || string.IsNullOrEmpty(plex?.PlexAuthToken))
foreach (var server in plex.Servers ?? new List<PlexServers>())
{
return false;
if (string.IsNullOrEmpty(server?.Ip) || string.IsNullOrEmpty(server?.PlexAuthToken))
{
return false;
}
}
}
return plex.Enable;

@ -2,18 +2,23 @@
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 List<PlexServers> Servers { get; set; }
}
public class PlexServers : ExternalSettings
{
public string Name { get; set; }
public bool EnableEpisodeSearching { get; set; }
public string PlexAuthToken { get; set; }
public string MachineIdentifier { get; set; }
public List<PlexSelectedLibraries> PlexSelectedLibraries { get; set; }
public List<PlexSelectedLibraries> PlexSelectedLibraries { get; set; }
}
public class PlexSelectedLibraries
{
public int Key { get; set; }

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

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

@ -15,6 +15,7 @@
"@angular/platform-browser-dynamic": "^4.1.0",
"@angular/platform-server": "^4.1.0",
"@angular/router": "^4.1.0",
"@ng-bootstrap/ng-bootstrap": "^1.0.0-alpha.25",
"@types/jquery": "^2.0.33",
"@types/systemjs": "^0.20.2",
"angular2-jwt": "^0.2.0",
@ -27,7 +28,7 @@
"gulp-clean-css": "^3.0.4",
"gulp-filter": "^5.0.0",
"gulp-if": "^2.0.2",
"gulp-rename": "^1.2.2",
"gulp-rename": "^1.2.2",
"gulp-run": "^1.7.1",
"gulp-sass": "^2.3.2",
"gulp-sourcemaps": "^1.9.0",

@ -2,7 +2,8 @@ import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
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';
@ -75,7 +76,8 @@ const routes: Routes = [
AuthModule,
WizardModule,
DialogModule,
MdButtonModule
MdButtonModule,
NgbModule.forRoot(),
],
declarations: [
AppComponent,

@ -4,7 +4,6 @@
export interface IExternalSettings extends ISettings {
ssl: boolean,
enable:boolean,
subDir: string,
ip: string,
port:number,
@ -20,15 +19,23 @@ export interface IOmbiSettings extends ISettings {
export interface IEmbySettings extends IExternalSettings {
apiKey: string,
enable: boolean,
administratorId: string,
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,
plexAuthToken: string,
machineIdentifier: string,
plexSelectedLibraries : IPlexLibraries[],
plexSelectedLibraries: IPlexLibraries[],
}
export interface IPlexLibraries {
@ -39,6 +46,7 @@ export interface IPlexLibraries {
export interface ISonarrSettings extends IExternalSettings {
apiKey: string,
enable: boolean,
qualityProfile: string,
seasonFolders: boolean,
rootPath: string,

@ -2,7 +2,7 @@
<h1>Login</h1>
<div>
<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>
</div>
<form method="POST" id="loginForm">

@ -10,100 +10,130 @@
<label for="enable">Enable</label>
</div>
</div>
<div class="form-group">
<label for="Ip" class="control-label">Hostname or IP</label>
<div>
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" [(ngModel)]="settings.ip" value="{{settings.ip}}">
</div>
</div>
<div class="form-group">
<label for="portNumber" class="control-label">Port</label>
<div>
<input type="text" [(ngModel)]="settings.port" class="form-control form-control-custom " id="portNumber" name="Port" placeholder="Port Number" value="{{settings.port}}">
</div>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="ssl" [(ngModel)]="settings.ssl" ng-checked="settings.ssl">
<label for="ssl">SSL</label>
</div>
<div style="float: right;">
<button type="submit" (click)="addTab()" class="btn btn-success-outline">Add Server</button>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="EnableTvEpisodeSearching" [(ngModel)]="settings.enableEpisodeSearching" ng-checked="settings.enableEpisodeSearching">
<label for="EnableTvEpisodeSearching">Enable Episode Searching</label>
</div>
<small>
If enabled then we will lookup all episodes on your Plex server and store them in the local database. This will stop episode requests that already exist on Plex (that might not be in Sonarr).
Please be aware that this is a very resource intensive process and while the Plex Episode Cacher job is running the application may appear slow (Depending on the size of your Plex library).
</small>
</div>
<div class="form-group">
<label for="authToken" class="control-label">Plex Authorization Token</label>
<div class="">
<input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="settings.plexAuthToken" placeholder="Plex Auth Token" value="{{settings.plexAuthToken}}">
</div>
</div>
<div class="form-group">
<label for="MachineIdentifier" class="control-label">Machine Identifier</label>
<div class="">
<input type="text" class="form-control-custom form-control" id="MachineIdentifier" name="MachineIdentifier" [(ngModel)]="settings.machineIdentifier" value="{{settings.machineIdentifier}}">
</div>
</div>
<div class="form-group">
<label for="username" class="control-label">Username and Password</label>
<div>
<input type="text" class="form-control form-control-custom" id="username" [(ngModel)]="username" placeholder="username">
</div>
<br />
<div>
<input type="password" class="form-control form-control-custom" id="password" [(ngModel)]="password" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="">
<button id="requestToken" (click)="requestToken()" class="btn btn-primary-outline">Request Token <i class="fa fa-key"></i></button>
</div>
</div>
<label>Please select the libraries you want Ombi to look in for content</label>
<div class="form-group">
<div>
<button (click)="loadLibraries()" class="btn btn-primary-outline">Load Libraries <i class="fa fa-film"></i></button>
</div>
</div>
<div *ngIf="settings.plexSelectedLibraries">
<div *ngFor="let lib of settings.plexSelectedLibraries">
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="{{lib.title}}" [(ngModel)]="lib.enabled" ng-checked="lib.enabled">
<label for="{{lib.title}}">{{lib.title}}</label>
</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">
<label for="Ip" class="control-label">Hostname or IP</label>
<div>
<input type="text" class="form-control form-control-custom " id="Ip" name="Ip" placeholder="localhost" [(ngModel)]="server.ip" value="{{server.ip}}">
</div>
</div>
<div class="form-group">
<label for="portNumber" class="control-label">Port</label>
<div>
<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 class="form-group">
<div class="checkbox">
<input type="checkbox" id="ssl" [(ngModel)]="server.ssl" ng-checked="server.ssl">
<label for="ssl">SSL</label>
</div>
</div>
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="EnableTvEpisodeSearching" [(ngModel)]="server.enableEpisodeSearching" ng-checked="server.enableEpisodeSearching">
<label for="EnableTvEpisodeSearching">Enable Episode Searching</label>
</div>
<small>
If enabled then we will lookup all episodes on your Plex server and store them in the local database. This will stop episode requests that already exist on Plex (that might not be in Sonarr).
Please be aware that this is a very resource intensive process and while the Plex Episode Cacher job is running the application may appear slow (Depending on the size of your Plex library).
</small>
</div>
<div class="form-group">
<label for="authToken" class="control-label">Plex Authorization Token</label>
<div class="">
<input type="text" class="form-control-custom form-control" id="authToken" [(ngModel)]="server.plexAuthToken" placeholder="Plex Auth Token" value="{{server.plexAuthToken}}">
</div>
</div>
<div class="form-group">
<label for="MachineIdentifier" class="control-label">Machine Identifier</label>
<div class="">
<input type="text" class="form-control-custom form-control" id="MachineIdentifier" name="MachineIdentifier" [(ngModel)]="server.machineIdentifier" value="{{server.machineIdentifier}}">
</div>
</div>
<div class="form-group">
<label for="username" class="control-label">Username and Password</label>
<div>
<input type="text" class="form-control form-control-custom" id="username" [(ngModel)]="username" placeholder="username">
</div>
<br/>
<div>
<input type="password" class="form-control form-control-custom" id="password" [(ngModel)]="password" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="">
<button id="requestToken" (click)="requestToken()" class="btn btn-primary-outline">Request Token <i class="fa fa-key"></i></button>
</div>
</div>
<label>Please select the libraries you want Ombi to look in for content</label>
<div class="form-group">
<div>
<button (click)="loadLibraries()" class="btn btn-primary-outline">Load Libraries <i class="fa fa-film"></i></button>
</div>
</div>
<div *ngIf="server.plexSelectedLibraries">
<div *ngFor="let lib of server.plexSelectedLibraries">
<div class="form-group">
<div class="checkbox">
<input type="checkbox" id="{{lib.title}}" [(ngModel)]="lib.enabled" ng-checked="lib.enabled">
<label for="{{lib.title}}">{{lib.title}}</label>
</div>
</div>
</div>
</div>
<div class="form-group">
<div>
<button id="testPlex" type="submit" (click)="testPlex()" class="btn btn-primary-outline">
Test Connectivity
<div id="spinner"></div>
</button>
</div>
</div>
</ng-template>
</ngb-tab>
</div>
</ngb-tabset>
</div>
</div>
<div class="form-group">
<div>
<button id="testPlex" type="submit" (click)="testPlex()" class="btn btn-primary-outline">Test Connectivity <div id="spinner"></div></button>
</div>
</div>
<div class="form-group">
<div>
<button (click)="save()" type="submit" id="save" class="btn btn-primary-outline">Submit</button>

@ -1,6 +1,6 @@
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';
@ -21,7 +21,10 @@ export class PlexComponent implements OnInit {
password: string;
ngOnInit(): void {
this.settingsService.getPlex().subscribe(x => this.settings = x);
this.settingsService.getPlex().subscribe(x => {
this.settings = x;
}
);
}
requestToken() {
@ -32,23 +35,40 @@ export class PlexComponent implements OnInit {
// 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.settings.plexSelectedLibraries = [];
server.plexSelectedLibraries = [];
x.mediaContainer.directory.forEach((item, index) => {
var lib: IPlexLibraries = {
key: item.key,
title: item.title,
enabled: false
};
this.settings.plexSelectedLibraries.push(lib);
server.plexSelectedLibraries.push(lib);
});
});
}
save() {
var filtered = this.settings.servers.filter(x => x.name !== "");
this.settings.servers = filtered;
this.settingsService.savePlex(this.settings).subscribe(x => {
if (x) {
this.notificationService.success("Settings Saved", "Successfully saved Plex settings");

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

Loading…
Cancel
Save