mirror of https://github.com/Sonarr/Sonarr
Fixed: Plex server authentication
This commit is contained in:
parent
af75813347
commit
5bc820efed
|
@ -33,79 +33,6 @@ namespace NzbDrone.Core.Test.NotificationTests
|
|||
};
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetSectionKeys_should_return_single_section_key_when_only_one_show_section()
|
||||
{
|
||||
var response = "<MediaContainer size=\"1\" mediaTagPrefix=\"/system/bundle/media/flags/\" mediaTagVersion=\"1329809559\" title1=\"Plex Library\" identifier=\"com.plexapp.plugins.library\"><Directory refreshing=\"0\" key=\"5\" type=\"show\" title=\"TV Shows\" art=\"/:/resources/show-fanart.jpg\" agent=\"com.plexapp.agents.thetvdb\" scanner=\"Plex Series Scanner\" language=\"en\" updatedAt=\"1329810350\"><Location path=\"C:/Test/TV\"/></Directory></MediaContainer>";
|
||||
Stream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(response));
|
||||
|
||||
Mocker.GetMock<IHttpProvider>().Setup(s => s.DownloadStream("http://localhost:32400/library/sections", null))
|
||||
.Returns(stream);
|
||||
|
||||
var result = Mocker.Resolve<PlexService>().GetSectionKeys(new PlexServerSettings { Host = "localhost", Port = 32400 });
|
||||
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
result.First().Should().Be(5);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetSectionKeys_should_return_single_section_key_when_only_one_show_section_with_other_sections()
|
||||
{
|
||||
|
||||
|
||||
var response = "<MediaContainer size=\"1\" mediaTagPrefix=\"/system/bundle/media/flags/\" mediaTagVersion=\"1329809559\" title1=\"Plex Library\" identifier=\"com.plexapp.plugins.library\"><Directory refreshing=\"0\" key=\"5\" type=\"show\" title=\"TV Shows\" art=\"/:/resources/show-fanart.jpg\" agent=\"com.plexapp.agents.thetvdb\" scanner=\"Plex Series Scanner\" language=\"en\" updatedAt=\"1329810350\"><Location path=\"C:/Test/TV\"/></Directory><Directory refreshing=\"0\" key=\"7\" type=\"movie\" title=\"TV Shows\" art=\"/:/resources/show-fanart.jpg\" agent=\"com.plexapp.agents.thetvdb\" scanner=\"Plex Series Scanner\" language=\"en\" updatedAt=\"1329810350\"><Location path=\"C:/Test/TV\"/></Directory></MediaContainer>";
|
||||
Stream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(response));
|
||||
|
||||
Mocker.GetMock<IHttpProvider>().Setup(s => s.DownloadStream("http://localhost:32400/library/sections", null))
|
||||
.Returns(stream);
|
||||
|
||||
|
||||
var result = Mocker.Resolve<PlexService>().GetSectionKeys(new PlexServerSettings { Host = "localhost", Port = 32400 });
|
||||
|
||||
|
||||
result.Should().HaveCount(1);
|
||||
result.First().Should().Be(5);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetSectionKeys_should_return_multiple_section_keys_when_there_are_multiple_show_sections()
|
||||
{
|
||||
|
||||
|
||||
var response = "<MediaContainer size=\"1\" mediaTagPrefix=\"/system/bundle/media/flags/\" mediaTagVersion=\"1329809559\" title1=\"Plex Library\" identifier=\"com.plexapp.plugins.library\"><Directory refreshing=\"0\" key=\"5\" type=\"show\" title=\"TV Shows\" art=\"/:/resources/show-fanart.jpg\" agent=\"com.plexapp.agents.thetvdb\" scanner=\"Plex Series Scanner\" language=\"en\" updatedAt=\"1329810350\"><Location path=\"C:/Test/TV\"/></Directory><Directory refreshing=\"0\" key=\"6\" type=\"show\" title=\"TV Shows\" art=\"/:/resources/show-fanart.jpg\" agent=\"com.plexapp.agents.thetvdb\" scanner=\"Plex Series Scanner\" language=\"en\" updatedAt=\"1329810350\"><Location path=\"C:/Test/TV\"/></Directory></MediaContainer>";
|
||||
Stream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(response));
|
||||
|
||||
Mocker.GetMock<IHttpProvider>().Setup(s => s.DownloadStream("http://localhost:32400/library/sections", null))
|
||||
.Returns(stream);
|
||||
|
||||
|
||||
var result = Mocker.Resolve<PlexService>().GetSectionKeys(new PlexServerSettings { Host = "localhost", Port = 32400 });
|
||||
|
||||
|
||||
result.Should().HaveCount(2);
|
||||
result.First().Should().Be(5);
|
||||
result.Last().Should().Be(6);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UpdateSection_should_update_section()
|
||||
{
|
||||
|
||||
|
||||
var response = "";
|
||||
Stream stream = new MemoryStream(ASCIIEncoding.Default.GetBytes(response));
|
||||
|
||||
Mocker.GetMock<IHttpProvider>().Setup(s => s.DownloadString("http://localhost:32400/library/sections/5/refresh"))
|
||||
.Returns(response);
|
||||
|
||||
|
||||
Mocker.Resolve<PlexService>().UpdateSection(new PlexServerSettings { Host = "localhost", Port = 32400 }, 5);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Notify_should_send_notification()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public class PlexError
|
||||
{
|
||||
public String Error { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NzbDrone.Common.Exceptions;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public class PlexException : NzbDroneException
|
||||
{
|
||||
public PlexException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
public PlexException(string message, params object[] args) : base(message, args)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
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 class PlexMediaContainer
|
||||
{
|
||||
[JsonProperty("_children")]
|
||||
public List<PlexDirectory> Directories { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using RestSharp;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public interface IPlexServerProxy
|
||||
{
|
||||
List<PlexSection> GetTvSections(PlexServerSettings settings);
|
||||
void Update(int sectionId, PlexServerSettings settings);
|
||||
}
|
||||
|
||||
public class PlexServerProxy : IPlexServerProxy
|
||||
{
|
||||
private readonly ICached<String> _authCache;
|
||||
|
||||
public PlexServerProxy(ICacheManager cacheManager)
|
||||
{
|
||||
_authCache = cacheManager.GetCache<String>(GetType(), "authCache");
|
||||
}
|
||||
|
||||
public List<PlexSection> GetTvSections(PlexServerSettings settings)
|
||||
{
|
||||
var request = GetPlexServerRequest("library/sections", Method.GET, settings);
|
||||
var client = GetPlexServerClient(settings);
|
||||
|
||||
var response = client.Execute(request);
|
||||
|
||||
return Json.Deserialize<PlexMediaContainer>(response.Content)
|
||||
.Directories
|
||||
.Where(d => d.Type == "show")
|
||||
.SelectMany(d => d.Sections)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void Update(int sectionId, PlexServerSettings settings)
|
||||
{
|
||||
var resource = String.Format("library/sections/{2}/refresh");
|
||||
var request = GetPlexServerRequest(resource, Method.GET, settings);
|
||||
var client = GetPlexServerClient(settings);
|
||||
|
||||
var response = client.Execute(request);
|
||||
}
|
||||
|
||||
private String Authenticate(string username, string password)
|
||||
{
|
||||
var request = GetMyPlexRequest("users/sign_in.json", Method.POST);
|
||||
var client = GetMyPlexClient(username, password);
|
||||
|
||||
var response = client.Execute(request);
|
||||
CheckForError(response.Content);
|
||||
|
||||
var user = Json.Deserialize<PlexUser>(JObject.Parse(response.Content).SelectToken("user").ToString());
|
||||
|
||||
_authCache.Set(username, user.AuthenticationToken);
|
||||
|
||||
return user.AuthenticationToken;
|
||||
}
|
||||
|
||||
private RestClient GetMyPlexClient(string username, string password)
|
||||
{
|
||||
var client = new RestClient("https://my.plexapp.com");
|
||||
client.Authenticator = new HttpBasicAuthenticator(username, password);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private RestRequest GetMyPlexRequest(string resource, Method method)
|
||||
{
|
||||
var request = new RestRequest(resource, method);
|
||||
request.AddHeader("X-Plex-Platform", "Windows");
|
||||
request.AddHeader("X-Plex-Platform-Version", "7");
|
||||
request.AddHeader("X-Plex-Provides", "player");
|
||||
request.AddHeader("X-Plex-Client-Identifier", "AB6CCCC7-5CF5-4523-826A-B969E0FFD8A0");
|
||||
request.AddHeader("X-Plex-Product", "PlexWMC");
|
||||
request.AddHeader("X-Plex-Version", "0");
|
||||
|
||||
return request;
|
||||
|
||||
}
|
||||
|
||||
private RestClient GetPlexServerClient(PlexServerSettings settings)
|
||||
{
|
||||
return new RestClient(String.Format("http://{0}:{1}", settings.Host, settings.Port));
|
||||
}
|
||||
|
||||
private RestRequest GetPlexServerRequest(string resource, Method method, PlexServerSettings settings)
|
||||
{
|
||||
var request = new RestRequest(resource, method);
|
||||
request.AddHeader("Accept", "application/json");
|
||||
|
||||
if (!settings.Username.IsNullOrWhiteSpace())
|
||||
{
|
||||
request.AddParameter("X-Plex-Token", GetAuthenticationToken(settings.Username, settings.Password));
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private string GetAuthenticationToken(string username, string password)
|
||||
{
|
||||
return _authCache.Get(username, () => Authenticate(username, password));
|
||||
}
|
||||
|
||||
private void CheckForError(string response)
|
||||
{
|
||||
var error = Json.Deserialize<PlexError>(response);
|
||||
|
||||
if (error != null && !error.Error.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new PlexException(error.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ namespace NzbDrone.Core.Notifications.Plex
|
|||
[FieldDefinition(2, Label = "Username")]
|
||||
public String Username { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "Password")]
|
||||
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
|
||||
public String Password { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Update Library", Type = FieldType.Checkbox)]
|
||||
|
|
|
@ -18,11 +18,13 @@ namespace NzbDrone.Core.Notifications.Plex
|
|||
public class PlexService : IPlexService, IExecute<TestPlexClientCommand>, IExecute<TestPlexServerCommand>
|
||||
{
|
||||
private readonly IHttpProvider _httpProvider;
|
||||
private readonly IPlexServerProxy _plexServerProxy;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public PlexService(IHttpProvider httpProvider, Logger logger)
|
||||
public PlexService(IHttpProvider httpProvider, IPlexServerProxy plexServerProxy, Logger logger)
|
||||
{
|
||||
_httpProvider = httpProvider;
|
||||
_plexServerProxy = plexServerProxy;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
@ -45,7 +47,7 @@ namespace NzbDrone.Core.Notifications.Plex
|
|||
{
|
||||
_logger.Debug("Sending Update Request to Plex Server");
|
||||
var sections = GetSectionKeys(settings);
|
||||
sections.ForEach(s => UpdateSection(settings, s));
|
||||
sections.ForEach(s => UpdateSection(s, settings));
|
||||
}
|
||||
|
||||
catch(Exception ex)
|
||||
|
@ -55,26 +57,21 @@ namespace NzbDrone.Core.Notifications.Plex
|
|||
}
|
||||
}
|
||||
|
||||
public List<int> GetSectionKeys(PlexServerSettings settings)
|
||||
private List<int> GetSectionKeys(PlexServerSettings settings)
|
||||
{
|
||||
_logger.Debug("Getting sections from Plex host: {0}", settings.Host);
|
||||
var url = String.Format("http://{0}:{1}/library/sections", settings.Host, settings.Port);
|
||||
var xmlStream = _httpProvider.DownloadStream(url, GetCredentials(settings));
|
||||
var xDoc = XDocument.Load(xmlStream);
|
||||
var mediaContainer = xDoc.Descendants("MediaContainer").FirstOrDefault();
|
||||
var directories = mediaContainer.Descendants("Directory").Where(x => x.Attribute("type").Value == "show");
|
||||
|
||||
return directories.Select(d => Int32.Parse(d.Attribute("key").Value)).ToList();
|
||||
return _plexServerProxy.GetTvSections(settings).Select(s => s.Id).ToList();
|
||||
}
|
||||
|
||||
public void UpdateSection(PlexServerSettings settings, int key)
|
||||
private void UpdateSection(int key, PlexServerSettings settings)
|
||||
{
|
||||
_logger.Debug("Updating Plex host: {0}, Section: {1}", settings.Host, key);
|
||||
var url = String.Format("http://{0}:{1}/library/sections/{2}/refresh", settings.Host, settings.Port, key);
|
||||
_httpProvider.DownloadString(url, GetCredentials(settings));
|
||||
|
||||
_plexServerProxy.Update(key, settings);
|
||||
}
|
||||
|
||||
public 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);
|
||||
|
||||
|
@ -86,13 +83,6 @@ namespace NzbDrone.Core.Notifications.Plex
|
|||
return _httpProvider.DownloadString(url);
|
||||
}
|
||||
|
||||
private NetworkCredential GetCredentials(PlexServerSettings settings)
|
||||
{
|
||||
if (settings.Username.IsNullOrWhiteSpace()) return null;
|
||||
|
||||
return new NetworkCredential(settings.Username, settings.Password);
|
||||
}
|
||||
|
||||
public void Execute(TestPlexClientCommand message)
|
||||
{
|
||||
_logger.Debug("Sending Test Notifcation to Plex Client: {0}", message.Host);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public class PlexUser
|
||||
{
|
||||
[JsonProperty("authentication_token")]
|
||||
public String AuthenticationToken { get; set; }
|
||||
}
|
||||
}
|
|
@ -377,6 +377,11 @@
|
|||
<Compile Include="Notifications\NotificationFactory.cs" />
|
||||
<Compile Include="Notifications\NotificationService.cs" />
|
||||
<Compile Include="Notifications\DownloadMessage.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexError.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexException.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexSection.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexServerProxy.cs" />
|
||||
<Compile Include="Notifications\Plex\PlexUser.cs" />
|
||||
<Compile Include="Notifications\PushBullet\PushBullet.cs" />
|
||||
<Compile Include="Notifications\PushBullet\PushBulletProxy.cs" />
|
||||
<Compile Include="Notifications\PushBullet\PushBulletSettings.cs" />
|
||||
|
|
Loading…
Reference in New Issue