From 3879fc04de24ecb3aa4ff51df2a93d2c50b13d66 Mon Sep 17 00:00:00 2001 From: "Jamie.Rees" Date: Mon, 15 May 2017 15:30:12 +0100 Subject: [PATCH] #865 Added support for multiple plex servers --- Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs | 143 +++++++------ .../Settings/Models/External/PlexSettings.cs | 13 +- .../Controllers/External/PlexController.cs | 39 ++-- Ombi/Ombi/gulpfile.js | 1 + Ombi/Ombi/package.json | 3 +- Ombi/Ombi/wwwroot/app/app.module.ts | 6 +- Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts | 14 +- .../wwwroot/app/login/login.component.html | 2 +- .../app/settings/plex/plex.component.html | 192 ++++++++++-------- .../app/settings/plex/plex.component.ts | 30 ++- .../wwwroot/app/settings/settings.module.ts | 4 +- 11 files changed, 270 insertions(+), 177 deletions(-) diff --git a/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs b/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs index 3289434fc..4b8f6a7fe 100644 --- a/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs +++ b/Ombi/Ombi.Schedule/Jobs/PlexContentCacher.cs @@ -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); - - // Let's now process this. - - var contentToAdd = new List(); - foreach (var content in allContent) + foreach (var servers in plexSettings.Servers ?? new List()) { - if (content.viewGroup.Equals(PlexMediaType.Show.ToString(), StringComparison.CurrentCultureIgnoreCase)) + + var allContent = GetAllContent(servers); + + // Let's now process this. + + var contentToAdd = new List(); + 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(); - 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(); + foreach (var season in seasonList.MediaContainer.Metadata) { - 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()) - { - // We have new seasons on Plex, let's add them back into the entity - existingContent.Seasons.AddRange(seasonDifference); - await Repo.Update(existingContent); - continue; + seasonsContent.Add(new SeasonsContent + { + ParentKey = int.Parse(season.parentRatingKey), + SeasonKey = int.Parse(season.ratingKey), + SeasonNumber = season.index + }); } - else + + // Do we already have this item? + var existingContent = await Repo.GetByKey(metadata.key); + if (existingContent != null) { - // No changes, no need to do anything - 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; + } } + + // 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 + { + 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() + }; + + + 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() - }; - - - item.Seasons.AddRange(seasonsContent); - - contentToAdd.Add(item); } } - } - if (contentToAdd.Any()) - { - await Repo.AddRange(contentToAdd); + if (contentToAdd.Any()) + { + await Repo.AddRange(contentToAdd); + } + } } - private List GetAllContent(PlexSettings plexSettings) + private List GetAllContent(PlexServers plexSettings) { var sections = PlexApi.GetLibrarySections(plexSettings.PlexAuthToken, plexSettings.FullUri).Result; - + var libs = new List(); 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()) { - return false; + if (string.IsNullOrEmpty(server?.Ip) || string.IsNullOrEmpty(server?.PlexAuthToken)) + { + return false; + } } } return plex.Enable; diff --git a/Ombi/Ombi.Settings/Settings/Models/External/PlexSettings.cs b/Ombi/Ombi.Settings/Settings/Models/External/PlexSettings.cs index d8691e1cf..7363e8fb1 100644 --- a/Ombi/Ombi.Settings/Settings/Models/External/PlexSettings.cs +++ b/Ombi/Ombi.Settings/Settings/Models/External/PlexSettings.cs @@ -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 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 { get; set; } + public List PlexSelectedLibraries { get; set; } } - public class PlexSelectedLibraries { public int Key { get; set; } diff --git a/Ombi/Ombi/Controllers/External/PlexController.cs b/Ombi/Ombi/Controllers/External/PlexController.cs index 42d125199..9ffccb7ad 100644 --- a/Ombi/Ombi/Controllers/External/PlexController.cs +++ b/Ombi/Ombi/Controllers/External/PlexController.cs @@ -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(); + 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 GetPlexLibraries([FromBody] PlexSettings settings) + public async Task GetPlexLibraries([FromBody] PlexServers settings) { var libs = await PlexApi.GetLibrarySections(settings.PlexAuthToken, settings.FullUri); diff --git a/Ombi/Ombi/gulpfile.js b/Ombi/Ombi/gulpfile.js index f23ca39dc..fb2e8d15c 100644 --- a/Ombi/Ombi/gulpfile.js +++ b/Ombi/Ombi/gulpfile.js @@ -35,6 +35,7 @@ var paths = { '@angular/forms', '@angular/platform-browser/animations', '@angular/material', + '@ng-bootstrap/ng-bootstrap', 'ngx-infinite-scroll' ], dest: './lib' diff --git a/Ombi/Ombi/package.json b/Ombi/Ombi/package.json index 193498c2d..92bd942fe 100644 --- a/Ombi/Ombi/package.json +++ b/Ombi/Ombi/package.json @@ -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", diff --git a/Ombi/Ombi/wwwroot/app/app.module.ts b/Ombi/Ombi/wwwroot/app/app.module.ts index 9af92c40b..c3c05128b 100644 --- a/Ombi/Ombi/wwwroot/app/app.module.ts +++ b/Ombi/Ombi/wwwroot/app/app.module.ts @@ -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, diff --git a/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts b/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts index 3a901ad2d..395e1c3da 100644 --- a/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts +++ b/Ombi/Ombi/wwwroot/app/interfaces/ISettings.ts @@ -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, diff --git a/Ombi/Ombi/wwwroot/app/login/login.component.html b/Ombi/Ombi/wwwroot/app/login/login.component.html index 7a01eb2a0..475e7b9fa 100644 --- a/Ombi/Ombi/wwwroot/app/login/login.component.html +++ b/Ombi/Ombi/wwwroot/app/login/login.component.html @@ -2,7 +2,7 @@

Login

- @UI.UserLogin_Paragraph + Hey! Welcome, login with your credentails below!

diff --git a/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.html b/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.html index 71d9aca68..132f41f98 100644 --- a/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.html +++ b/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.html @@ -10,100 +10,130 @@ - - -
- -
- -
+
+
-
- -
- -
-
+ + +
+ + +
+
+
+ +
+
+
-
-
- - -
-
+
+ +
+ +
+
-
-
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ + +
+
+ +
+
- - + + -
- - 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). - -
+
+ + 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). + +
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
+
+ +
+ +
+
-
- -
- -
-
-
- -
-
-
-
- -
-
- - -
-
- -
-
-
-
-
-
- - -
+
+ +
+ +
+
+
+ +
+
+
+
+ +
+
+ + +
+
+ +
+
+
+
+
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + -
-
- -
-
- -
-
- - - +
diff --git a/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.ts b/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.ts index 4a9a8dd9e..5bdba3af1 100644 --- a/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.ts +++ b/Ombi/Ombi/wwwroot/app/settings/plex/plex.component.ts @@ -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({ 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"); diff --git a/Ombi/Ombi/wwwroot/app/settings/settings.module.ts b/Ombi/Ombi/wwwroot/app/settings/settings.module.ts index 08d42f23b..e5b9e1d4b 100644 --- a/Ombi/Ombi/wwwroot/app/settings/settings.module.ts +++ b/Ombi/Ombi/wwwroot/app/settings/settings.module.ts @@ -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,