mirror of https://github.com/Radarr/Radarr
Merge pull request #563 from Sonarr/plex-partial-updates
Support for updating single series in Plex Library
This commit is contained in:
commit
49718fbfbe
|
@ -9,7 +9,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
|
|
||||||
public class PlexProviderTest : CoreTest
|
public class PlexClientServiceTest : CoreTest
|
||||||
{
|
{
|
||||||
private PlexClientSettings _clientSettings;
|
private PlexClientSettings _clientSettings;
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||||
.Returns("ok");
|
.Returns("ok");
|
||||||
|
|
||||||
|
|
||||||
Mocker.Resolve<PlexService>().Notify(_clientSettings, header, message);
|
Mocker.Resolve<PlexClientService>().Notify(_clientSettings, header, message);
|
||||||
|
|
||||||
|
|
||||||
fakeHttp.Verify(v => v.DownloadString(expectedUrl), Times.Once());
|
fakeHttp.Verify(v => v.DownloadString(expectedUrl), Times.Once());
|
||||||
|
@ -63,8 +63,8 @@ namespace NzbDrone.Core.Test.NotificationTests
|
||||||
fakeHttp.Setup(s => s.DownloadString(expectedUrl, "plex", "plex"))
|
fakeHttp.Setup(s => s.DownloadString(expectedUrl, "plex", "plex"))
|
||||||
.Returns("ok");
|
.Returns("ok");
|
||||||
|
|
||||||
|
|
||||||
Mocker.Resolve<PlexService>().Notify(_clientSettings, header, message);
|
Mocker.Resolve<PlexClientService>().Notify(_clientSettings, header, message);
|
||||||
|
|
||||||
|
|
||||||
fakeHttp.Verify(v => v.DownloadString(expectedUrl, "plex", "plex"), Times.Once());
|
fakeHttp.Verify(v => v.DownloadString(expectedUrl, "plex", "plex"), Times.Once());
|
|
@ -274,7 +274,7 @@
|
||||||
<Compile Include="Messaging\Events\EventAggregatorFixture.cs" />
|
<Compile Include="Messaging\Events\EventAggregatorFixture.cs" />
|
||||||
<Compile Include="Metadata\Consumers\Roksbox\FindMetadataFileFixture.cs" />
|
<Compile Include="Metadata\Consumers\Roksbox\FindMetadataFileFixture.cs" />
|
||||||
<Compile Include="Metadata\Consumers\Wdtv\FindMetadataFileFixture.cs" />
|
<Compile Include="Metadata\Consumers\Wdtv\FindMetadataFileFixture.cs" />
|
||||||
<Compile Include="NotificationTests\PlexProviderTest.cs" />
|
<Compile Include="NotificationTests\PlexClientServiceTest.cs" />
|
||||||
<Compile Include="NotificationTests\ProwlProviderTest.cs" />
|
<Compile Include="NotificationTests\ProwlProviderTest.cs" />
|
||||||
<Compile Include="NotificationTests\Xbmc\Http\ActivePlayersFixture.cs" />
|
<Compile Include="NotificationTests\Xbmc\Http\ActivePlayersFixture.cs" />
|
||||||
<Compile Include="NotificationTests\Xbmc\Http\CheckForErrorFixture.cs" />
|
<Compile Include="NotificationTests\Xbmc\Http\CheckForErrorFixture.cs" />
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace NzbDrone.Core.Notifications.Plex.Models
|
||||||
|
{
|
||||||
|
public class PlexIdentity
|
||||||
|
{
|
||||||
|
public string MachineIdentifier { get; set; }
|
||||||
|
public string Version { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Plex.Models
|
||||||
|
{
|
||||||
|
public class PlexPreferences
|
||||||
|
{
|
||||||
|
[JsonProperty("_children")]
|
||||||
|
public List<PlexPreference> Preferences { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlexPreference
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Plex.Models
|
||||||
|
{
|
||||||
|
public class PlexSectionDetails
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Path { get; set; }
|
||||||
|
public string Language { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlexSection
|
||||||
|
{
|
||||||
|
[JsonProperty("key")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Type { get; set; }
|
||||||
|
public string Language { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("_children")]
|
||||||
|
public List<PlexSectionDetails> Sections { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlexMediaContainer
|
||||||
|
{
|
||||||
|
[JsonProperty("_children")]
|
||||||
|
public List<PlexSection> Directories { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Plex.Models
|
||||||
|
{
|
||||||
|
public class PlexSectionItem
|
||||||
|
{
|
||||||
|
[JsonProperty("ratingKey")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Title { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlexSectionResponse
|
||||||
|
{
|
||||||
|
[JsonProperty("_children")]
|
||||||
|
public List<PlexSectionItem> Items { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,11 +7,11 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
{
|
{
|
||||||
public class PlexClient : NotificationBase<PlexClientSettings>
|
public class PlexClient : NotificationBase<PlexClientSettings>
|
||||||
{
|
{
|
||||||
private readonly IPlexService _plexService;
|
private readonly IPlexClientService _plexClientService;
|
||||||
|
|
||||||
public PlexClient(IPlexService plexService)
|
public PlexClient(IPlexClientService plexClientService)
|
||||||
{
|
{
|
||||||
_plexService = plexService;
|
_plexClientService = plexClientService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Link
|
public override string Link
|
||||||
|
@ -22,13 +22,13 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
public override void OnGrab(string message)
|
public override void OnGrab(string message)
|
||||||
{
|
{
|
||||||
const string header = "Sonarr [TV] - Grabbed";
|
const string header = "Sonarr [TV] - Grabbed";
|
||||||
_plexService.Notify(Settings, header, message);
|
_plexClientService.Notify(Settings, header, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnDownload(DownloadMessage message)
|
public override void OnDownload(DownloadMessage message)
|
||||||
{
|
{
|
||||||
const string header = "Sonarr [TV] - Downloaded";
|
const string header = "Sonarr [TV] - Downloaded";
|
||||||
_plexService.Notify(Settings, header, message.Message);
|
_plexClientService.Notify(Settings, header, message.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AfterRename(Series series)
|
public override void AfterRename(Series series)
|
||||||
|
@ -47,7 +47,7 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
{
|
{
|
||||||
var failures = new List<ValidationFailure>();
|
var failures = new List<ValidationFailure>();
|
||||||
|
|
||||||
failures.AddIfNotNull(_plexService.Test(Settings));
|
failures.AddIfNotNull(_plexClientService.Test(Settings));
|
||||||
|
|
||||||
return new ValidationResult(failures);
|
return new ValidationResult(failures);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,27 +5,24 @@ using FluentValidation.Results;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Http;
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Notifications.Plex.Models;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Plex
|
namespace NzbDrone.Core.Notifications.Plex
|
||||||
{
|
{
|
||||||
public interface IPlexService
|
public interface IPlexClientService
|
||||||
{
|
{
|
||||||
void Notify(PlexClientSettings settings, string header, string message);
|
void Notify(PlexClientSettings settings, string header, string message);
|
||||||
void UpdateLibrary(PlexServerSettings settings);
|
|
||||||
ValidationFailure Test(PlexClientSettings settings);
|
ValidationFailure Test(PlexClientSettings settings);
|
||||||
ValidationFailure Test(PlexServerSettings settings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlexService : IPlexService
|
public class PlexClientService : IPlexClientService
|
||||||
{
|
{
|
||||||
private readonly IHttpProvider _httpProvider;
|
private readonly IHttpProvider _httpProvider;
|
||||||
private readonly IPlexServerProxy _plexServerProxy;
|
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public PlexService(IHttpProvider httpProvider, IPlexServerProxy plexServerProxy, Logger logger)
|
public PlexClientService(IHttpProvider httpProvider, Logger logger)
|
||||||
{
|
{
|
||||||
_httpProvider = httpProvider;
|
_httpProvider = httpProvider;
|
||||||
_plexServerProxy = plexServerProxy;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,36 +39,6 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateLibrary(PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_logger.Debug("Sending Update Request to Plex Server");
|
|
||||||
var sections = GetSectionKeys(settings);
|
|
||||||
sections.ForEach(s => UpdateSection(s, settings));
|
|
||||||
}
|
|
||||||
|
|
||||||
catch(Exception ex)
|
|
||||||
{
|
|
||||||
_logger.WarnException("Failed to Update Plex host: " + settings.Host, ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<int> GetSectionKeys(PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
_logger.Debug("Getting sections from Plex host: {0}", settings.Host);
|
|
||||||
|
|
||||||
return _plexServerProxy.GetTvSections(settings).Select(s => s.Key).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSection(int key, PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
_logger.Debug("Updating Plex host: {0}, Section: {1}", settings.Host, key);
|
|
||||||
|
|
||||||
_plexServerProxy.Update(key, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string SendCommand(string host, int port, string command, string username, string password)
|
private string SendCommand(string host, int port, string command, string username, string password)
|
||||||
{
|
{
|
||||||
var url = String.Format("http://{0}:{1}/xbmcCmds/xbmcHttp?command={2}", host, port, command);
|
var url = String.Format("http://{0}:{1}/xbmcCmds/xbmcHttp?command={2}", host, port, command);
|
||||||
|
@ -106,31 +73,5 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValidationFailure Test(PlexServerSettings settings)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var sections = GetSectionKeys(new PlexServerSettings
|
|
||||||
{
|
|
||||||
Host = settings.Host,
|
|
||||||
Port = settings.Port,
|
|
||||||
Username = settings.Username,
|
|
||||||
Password = settings.Password
|
|
||||||
});
|
|
||||||
|
|
||||||
if (sections.Empty())
|
|
||||||
{
|
|
||||||
return new ValidationFailure("Host", "At least one TV library is required");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.ErrorException("Unable to connect to Plex Server: " + ex.Message, ex);
|
|
||||||
return new ValidationFailure("Host", "Unable to connect to Plex Server");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Plex
|
|
||||||
{
|
|
||||||
public class PlexSection
|
|
||||||
{
|
|
||||||
public Int32 Id { get; set; }
|
|
||||||
public String Path { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PlexDirectory
|
|
||||||
{
|
|
||||||
public String Type { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("_children")]
|
|
||||||
public List<PlexSection> Sections { get; set; }
|
|
||||||
|
|
||||||
public Int32 Key { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PlexMediaContainer
|
|
||||||
{
|
|
||||||
[JsonProperty("_children")]
|
|
||||||
public List<PlexDirectory> Directories { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,11 +7,11 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
{
|
{
|
||||||
public class PlexServer : NotificationBase<PlexServerSettings>
|
public class PlexServer : NotificationBase<PlexServerSettings>
|
||||||
{
|
{
|
||||||
private readonly IPlexService _plexService;
|
private readonly IPlexServerService _plexServerService;
|
||||||
|
|
||||||
public PlexServer(IPlexService plexService)
|
public PlexServer(IPlexServerService plexServerService)
|
||||||
{
|
{
|
||||||
_plexService = plexService;
|
_plexServerService = plexServerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Link
|
public override string Link
|
||||||
|
@ -25,19 +25,19 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
|
|
||||||
public override void OnDownload(DownloadMessage message)
|
public override void OnDownload(DownloadMessage message)
|
||||||
{
|
{
|
||||||
UpdateIfEnabled();
|
UpdateIfEnabled(message.Series);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AfterRename(Series series)
|
public override void AfterRename(Series series)
|
||||||
{
|
{
|
||||||
UpdateIfEnabled();
|
UpdateIfEnabled(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateIfEnabled()
|
private void UpdateIfEnabled(Series series)
|
||||||
{
|
{
|
||||||
if (Settings.UpdateLibrary)
|
if (Settings.UpdateLibrary)
|
||||||
{
|
{
|
||||||
_plexService.UpdateLibrary(Settings);
|
_plexServerService.UpdateLibrary(series, Settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
{
|
{
|
||||||
var failures = new List<ValidationFailure>();
|
var failures = new List<ValidationFailure>();
|
||||||
|
|
||||||
failures.AddIfNotNull(_plexService.Test(Settings));
|
failures.AddIfNotNull(_plexServerService.Test(Settings));
|
||||||
|
|
||||||
return new ValidationResult(failures);
|
return new ValidationResult(failures);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common.Cache;
|
using NzbDrone.Common.Cache;
|
||||||
using NzbDrone.Common.EnvironmentInfo;
|
using NzbDrone.Common.EnvironmentInfo;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
|
using NzbDrone.Core.Notifications.Plex.Models;
|
||||||
using NzbDrone.Core.Rest;
|
using NzbDrone.Core.Rest;
|
||||||
using RestSharp;
|
using RestSharp;
|
||||||
|
|
||||||
|
@ -14,8 +16,12 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
{
|
{
|
||||||
public interface IPlexServerProxy
|
public interface IPlexServerProxy
|
||||||
{
|
{
|
||||||
List<PlexDirectory> GetTvSections(PlexServerSettings settings);
|
List<PlexSection> GetTvSections(PlexServerSettings settings);
|
||||||
void Update(int sectionId, PlexServerSettings settings);
|
void Update(int sectionId, PlexServerSettings settings);
|
||||||
|
void UpdateSeries(int metadataId, PlexServerSettings settings);
|
||||||
|
string Version(PlexServerSettings settings);
|
||||||
|
List<PlexPreference> Preferences(PlexServerSettings settings);
|
||||||
|
int? GetMetadataId(int sectionId, int tvdbId, string language, PlexServerSettings settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PlexServerProxy : IPlexServerProxy
|
public class PlexServerProxy : IPlexServerProxy
|
||||||
|
@ -29,16 +35,14 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<PlexDirectory> GetTvSections(PlexServerSettings settings)
|
public List<PlexSection> GetTvSections(PlexServerSettings settings)
|
||||||
{
|
{
|
||||||
var request = GetPlexServerRequest("library/sections", Method.GET, settings);
|
var request = GetPlexServerRequest("library/sections", Method.GET, settings);
|
||||||
var client = GetPlexServerClient(settings);
|
var client = GetPlexServerClient(settings);
|
||||||
|
|
||||||
var response = client.Execute(request);
|
var response = client.Execute(request);
|
||||||
|
|
||||||
_logger.Trace("Sections response: {0}", response.Content);
|
_logger.Trace("Sections response: {0}", response.Content);
|
||||||
|
CheckForError(response);
|
||||||
CheckForError(response.Content);
|
|
||||||
|
|
||||||
return Json.Deserialize<PlexMediaContainer>(response.Content)
|
return Json.Deserialize<PlexMediaContainer>(response.Content)
|
||||||
.Directories
|
.Directories
|
||||||
|
@ -51,11 +55,68 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
var resource = String.Format("library/sections/{0}/refresh", sectionId);
|
var resource = String.Format("library/sections/{0}/refresh", sectionId);
|
||||||
var request = GetPlexServerRequest(resource, Method.GET, settings);
|
var request = GetPlexServerRequest(resource, Method.GET, settings);
|
||||||
var client = GetPlexServerClient(settings);
|
var client = GetPlexServerClient(settings);
|
||||||
|
|
||||||
var response = client.Execute(request);
|
var response = client.Execute(request);
|
||||||
|
|
||||||
CheckForError(response.Content);
|
_logger.Trace("Update response: {0}", response.Content);
|
||||||
_logger.Debug("Update response: {0}", response.Content);
|
CheckForError(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateSeries(int metadataId, PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
var resource = String.Format("library/metadata/{0}/refresh", metadataId);
|
||||||
|
var request = GetPlexServerRequest(resource, Method.PUT, settings);
|
||||||
|
var client = GetPlexServerClient(settings);
|
||||||
|
var response = client.Execute(request);
|
||||||
|
|
||||||
|
_logger.Trace("Update Series response: {0}", response.Content);
|
||||||
|
CheckForError(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Version(PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
var request = GetPlexServerRequest("identity", Method.GET, settings);
|
||||||
|
var client = GetPlexServerClient(settings);
|
||||||
|
var response = client.Execute(request);
|
||||||
|
|
||||||
|
_logger.Trace("Version response: {0}", response.Content);
|
||||||
|
CheckForError(response);
|
||||||
|
|
||||||
|
return Json.Deserialize<PlexIdentity>(response.Content).Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<PlexPreference> Preferences(PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
var request = GetPlexServerRequest(":/prefs", Method.GET, settings);
|
||||||
|
var client = GetPlexServerClient(settings);
|
||||||
|
var response = client.Execute(request);
|
||||||
|
|
||||||
|
_logger.Trace("Preferences response: {0}", response.Content);
|
||||||
|
CheckForError(response);
|
||||||
|
|
||||||
|
return Json.Deserialize<PlexPreferences>(response.Content).Preferences;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetMetadataId(int sectionId, int tvdbId, string language, PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
var guid = String.Format("com.plexapp.agents.thetvdb://{0}?lang={1}", tvdbId, language);
|
||||||
|
var resource = String.Format("library/sections/{0}/all?guid={1}", sectionId, System.Web.HttpUtility.UrlEncode(guid));
|
||||||
|
var request = GetPlexServerRequest(resource, Method.GET, settings);
|
||||||
|
var client = GetPlexServerClient(settings);
|
||||||
|
var response = client.Execute(request);
|
||||||
|
|
||||||
|
_logger.Trace("Sections response: {0}", response.Content);
|
||||||
|
CheckForError(response);
|
||||||
|
|
||||||
|
var item = Json.Deserialize<PlexSectionResponse>(response.Content)
|
||||||
|
.Items
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String Authenticate(string username, string password)
|
private String Authenticate(string username, string password)
|
||||||
|
@ -65,8 +126,8 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
|
|
||||||
var response = client.Execute(request);
|
var response = client.Execute(request);
|
||||||
|
|
||||||
CheckForError(response.Content);
|
|
||||||
_logger.Debug("Authentication Response: {0}", response.Content);
|
_logger.Debug("Authentication Response: {0}", response.Content);
|
||||||
|
CheckForError(response);
|
||||||
|
|
||||||
var user = Json.Deserialize<PlexUser>(JObject.Parse(response.Content).SelectToken("user").ToString());
|
var user = Json.Deserialize<PlexUser>(JObject.Parse(response.Content).SelectToken("user").ToString());
|
||||||
|
|
||||||
|
@ -109,7 +170,7 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
|
|
||||||
if (!settings.Username.IsNullOrWhiteSpace())
|
if (!settings.Username.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
request.AddParameter("X-Plex-Token", GetAuthenticationToken(settings.Username, settings.Password));
|
request.AddParameter("X-Plex-Token", GetAuthenticationToken(settings.Username, settings.Password), ParameterType.HttpHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
return request;
|
return request;
|
||||||
|
@ -120,14 +181,23 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
return _authCache.Get(username, () => Authenticate(username, password));
|
return _authCache.Get(username, () => Authenticate(username, password));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckForError(string response)
|
private void CheckForError(IRestResponse response)
|
||||||
{
|
{
|
||||||
var error = Json.Deserialize<PlexError>(response);
|
_logger.Trace("Checking for error");
|
||||||
|
|
||||||
|
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
throw new PlexException("Unauthorized");
|
||||||
|
}
|
||||||
|
|
||||||
|
var error = Json.Deserialize<PlexError>(response.Content);
|
||||||
|
|
||||||
if (error != null && !error.Error.IsNullOrWhiteSpace())
|
if (error != null && !error.Error.IsNullOrWhiteSpace())
|
||||||
{
|
{
|
||||||
throw new PlexException(error.Error);
|
throw new PlexException(error.Error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.Trace("No error detected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,169 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Notifications.Plex.Models;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Plex
|
||||||
|
{
|
||||||
|
public interface IPlexServerService
|
||||||
|
{
|
||||||
|
void UpdateLibrary(Series series, PlexServerSettings settings);
|
||||||
|
ValidationFailure Test(PlexServerSettings settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PlexServerService : IPlexServerService
|
||||||
|
{
|
||||||
|
private readonly ICached<bool> _partialUpdateCache;
|
||||||
|
private readonly IPlexServerProxy _plexServerProxy;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public PlexServerService(ICacheManager cacheManager, IPlexServerProxy plexServerProxy, Logger logger)
|
||||||
|
{
|
||||||
|
_partialUpdateCache = cacheManager.GetCache<bool>(GetType(), "partialUpdateCache");
|
||||||
|
_plexServerProxy = plexServerProxy;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void UpdateLibrary(Series series, PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Debug("Sending Update Request to Plex Server");
|
||||||
|
|
||||||
|
var sections = GetSections(settings);
|
||||||
|
|
||||||
|
//TODO: How long should we cache this for?
|
||||||
|
var partialUpdates = _partialUpdateCache.Get(settings.Host, () => PartialUpdatesAllowed(settings), TimeSpan.FromHours(2));
|
||||||
|
|
||||||
|
if (partialUpdates)
|
||||||
|
{
|
||||||
|
sections.ForEach(s => UpdateSeries(s.Id, series, s.Language, settings));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sections.ForEach(s => UpdateSection(s.Id, settings));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catch(Exception ex)
|
||||||
|
{
|
||||||
|
_logger.WarnException("Failed to Update Plex host: " + settings.Host, ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PlexSection> GetSections(PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Getting sections from Plex host: {0}", settings.Host);
|
||||||
|
|
||||||
|
return _plexServerProxy.GetTvSections(settings).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool PartialUpdatesAllowed(PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var rawVersion = GetVersion(settings);
|
||||||
|
var version = new Version(Regex.Match(rawVersion, @"^(\d+\.){4}").Value.Trim('.'));
|
||||||
|
|
||||||
|
if (version >= new Version(0, 9, 12, 0))
|
||||||
|
{
|
||||||
|
var preferences = GetPreferences(settings);
|
||||||
|
var partialScanPreference = preferences.SingleOrDefault(p => p.Id.Equals("FSEventLibraryPartialScanEnabled"));
|
||||||
|
|
||||||
|
if (partialScanPreference == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Convert.ToBoolean(partialScanPreference.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.WarnException("Unable to check if partial updates are allowed", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetVersion(PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Getting version from Plex host: {0}", settings.Host);
|
||||||
|
|
||||||
|
return _plexServerProxy.Version(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<PlexPreference> GetPreferences(PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Getting preferences from Plex host: {0}", settings.Host);
|
||||||
|
|
||||||
|
return _plexServerProxy.Preferences(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSection(int sectionId, PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Updating Plex host: {0}, Section: {1}", settings.Host, sectionId);
|
||||||
|
|
||||||
|
_plexServerProxy.Update(sectionId, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSeries(int sectionId, Series series, string language, PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Updating Plex host: {0}, Section: {1}, Series: {2}", settings.Host, sectionId, series);
|
||||||
|
|
||||||
|
var metadataId = GetMetadataId(sectionId, series, language, settings);
|
||||||
|
|
||||||
|
if (metadataId.HasValue)
|
||||||
|
{
|
||||||
|
_plexServerProxy.UpdateSeries(metadataId.Value, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
UpdateSection(sectionId, settings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int? GetMetadataId(int sectionId, Series series, string language, PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
_logger.Debug("Getting metadata from Plex host: {0} for series: {1}", settings.Host, series);
|
||||||
|
|
||||||
|
return _plexServerProxy.GetMetadataId(sectionId, series.TvdbId, language, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationFailure Test(PlexServerSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sections = GetSections(new PlexServerSettings
|
||||||
|
{
|
||||||
|
Host = settings.Host,
|
||||||
|
Port = settings.Port,
|
||||||
|
Username = settings.Username,
|
||||||
|
Password = settings.Password
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sections.Empty())
|
||||||
|
{
|
||||||
|
return new ValidationFailure("Host", "At least one TV library is required");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.ErrorException("Unable to connect to Plex Server: " + ex.Message, ex);
|
||||||
|
return new ValidationFailure("Host", "Unable to connect to Plex Server");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,7 @@ namespace NzbDrone.Core.Notifications.Plex
|
||||||
[FieldDefinition(1, Label = "Port")]
|
[FieldDefinition(1, Label = "Port")]
|
||||||
public Int32 Port { get; set; }
|
public Int32 Port { get; set; }
|
||||||
|
|
||||||
|
//TODO: Change username and password to token and get a plex.tv OAuth token properly
|
||||||
[FieldDefinition(2, Label = "Username")]
|
[FieldDefinition(2, Label = "Username")]
|
||||||
public String Username { get; set; }
|
public String Username { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -701,8 +701,13 @@
|
||||||
<Compile Include="Metadata\MetadataType.cs" />
|
<Compile Include="Metadata\MetadataType.cs" />
|
||||||
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
<Compile Include="MetadataSource\IProvideSeriesInfo.cs" />
|
||||||
<Compile Include="MetadataSource\ISearchForNewSeries.cs" />
|
<Compile Include="MetadataSource\ISearchForNewSeries.cs" />
|
||||||
|
<Compile Include="Notifications\Plex\Models\PlexIdentity.cs" />
|
||||||
|
<Compile Include="Notifications\Plex\Models\PlexPreferences.cs" />
|
||||||
|
<Compile Include="Notifications\Plex\Models\PlexSectionItem.cs" />
|
||||||
|
<Compile Include="Notifications\Plex\Models\PlexSection.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexHomeTheater.cs" />
|
<Compile Include="Notifications\Plex\PlexHomeTheater.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexHomeTheaterSettings.cs" />
|
<Compile Include="Notifications\Plex\PlexHomeTheaterSettings.cs" />
|
||||||
|
<Compile Include="Notifications\Plex\PlexClientService.cs" />
|
||||||
<Compile Include="Notifications\Synology\SynologyException.cs" />
|
<Compile Include="Notifications\Synology\SynologyException.cs" />
|
||||||
<Compile Include="Notifications\Synology\SynologyIndexer.cs" />
|
<Compile Include="Notifications\Synology\SynologyIndexer.cs" />
|
||||||
<Compile Include="Notifications\Synology\SynologyIndexerProxy.cs" />
|
<Compile Include="Notifications\Synology\SynologyIndexerProxy.cs" />
|
||||||
|
@ -754,11 +759,10 @@
|
||||||
<Compile Include="Notifications\Plex\PlexClientSettings.cs" />
|
<Compile Include="Notifications\Plex\PlexClientSettings.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexError.cs" />
|
<Compile Include="Notifications\Plex\PlexError.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexException.cs" />
|
<Compile Include="Notifications\Plex\PlexException.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexSection.cs" />
|
|
||||||
<Compile Include="Notifications\Plex\PlexServer.cs" />
|
<Compile Include="Notifications\Plex\PlexServer.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexServerProxy.cs" />
|
<Compile Include="Notifications\Plex\PlexServerProxy.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexServerSettings.cs" />
|
<Compile Include="Notifications\Plex\PlexServerSettings.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexService.cs" />
|
<Compile Include="Notifications\Plex\PlexServerService.cs" />
|
||||||
<Compile Include="Notifications\Plex\PlexUser.cs" />
|
<Compile Include="Notifications\Plex\PlexUser.cs" />
|
||||||
<Compile Include="Notifications\Prowl\InvalidApiKeyException.cs" />
|
<Compile Include="Notifications\Prowl\InvalidApiKeyException.cs" />
|
||||||
<Compile Include="Notifications\Prowl\Prowl.cs">
|
<Compile Include="Notifications\Prowl\Prowl.cs">
|
||||||
|
|
Loading…
Reference in New Issue