Co-Authored-By: Mark McDowall <markus101@users.noreply.github.com>pull/481/head
parent
aab70b512d
commit
6b45758fde
@ -1,52 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Music;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public class PlexServer : NotificationBase<PlexServerSettings>
|
||||
{
|
||||
private readonly IPlexServerService _plexServerService;
|
||||
|
||||
public PlexServer(IPlexServerService plexServerService)
|
||||
{
|
||||
_plexServerService = plexServerService;
|
||||
}
|
||||
|
||||
public override string Link => "https://www.plex.tv/";
|
||||
public override string Name => "Plex Media Server";
|
||||
|
||||
public override void OnDownload(TrackDownloadMessage message)
|
||||
{
|
||||
UpdateIfEnabled(message.Artist);
|
||||
}
|
||||
|
||||
public override void OnAlbumDownload(AlbumDownloadMessage message)
|
||||
{
|
||||
UpdateIfEnabled(message.Artist);
|
||||
}
|
||||
|
||||
public override void OnRename(Artist artist)
|
||||
{
|
||||
UpdateIfEnabled(artist);
|
||||
}
|
||||
|
||||
private void UpdateIfEnabled(Artist artist)
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_plexServerService.UpdateLibrary(artist, Settings);
|
||||
}
|
||||
}
|
||||
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
|
||||
failures.AddIfNotNull(_plexServerService.Test(Settings));
|
||||
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Cache;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Notifications.Plex.Models;
|
||||
using NzbDrone.Core.Rest;
|
||||
using RestSharp;
|
||||
using RestSharp.Authenticators;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public interface IPlexServerProxy
|
||||
{
|
||||
List<PlexSection> GetArtistSections(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, string mdId, string language, PlexServerSettings settings);
|
||||
}
|
||||
|
||||
public class PlexServerProxy : IPlexServerProxy
|
||||
{
|
||||
private readonly ICached<string> _authCache;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public PlexServerProxy(ICacheManager cacheManager, Logger logger)
|
||||
{
|
||||
_authCache = cacheManager.GetCache<string>(GetType(), "authCache");
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<PlexSection> GetArtistSections(PlexServerSettings settings)
|
||||
{
|
||||
var request = GetPlexServerRequest("library/sections", Method.GET, settings);
|
||||
var client = GetPlexServerClient(settings);
|
||||
var response = client.Execute(request);
|
||||
|
||||
_logger.Trace("Sections response: {0}", response.Content);
|
||||
CheckForError(response, settings);
|
||||
|
||||
if (response.Content.Contains("_children"))
|
||||
{
|
||||
return Json.Deserialize<PlexMediaContainerLegacy>(response.Content)
|
||||
.Sections
|
||||
.Where(d => d.Type == "artist")
|
||||
.Select(s => new PlexSection
|
||||
{
|
||||
Id = s.Id,
|
||||
Language = s.Language,
|
||||
Locations = s.Locations,
|
||||
Type = s.Type
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
return Json.Deserialize<PlexResponse<PlexSectionsContainer>>(response.Content)
|
||||
.MediaContainer
|
||||
.Sections
|
||||
.Where(d => d.Type == "artist")
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void Update(int sectionId, PlexServerSettings settings)
|
||||
{
|
||||
var resource = string.Format("library/sections/{0}/refresh", sectionId);
|
||||
var request = GetPlexServerRequest(resource, Method.GET, settings);
|
||||
var client = GetPlexServerClient(settings);
|
||||
var response = client.Execute(request);
|
||||
|
||||
_logger.Trace("Update response: {0}", response.Content);
|
||||
CheckForError(response, settings);
|
||||
}
|
||||
|
||||
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, settings);
|
||||
}
|
||||
|
||||
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, settings);
|
||||
|
||||
if (response.Content.Contains("_children"))
|
||||
{
|
||||
return Json.Deserialize<PlexIdentity>(response.Content)
|
||||
.Version;
|
||||
}
|
||||
|
||||
return Json.Deserialize<PlexResponse<PlexIdentity>>(response.Content)
|
||||
.MediaContainer
|
||||
.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, settings);
|
||||
|
||||
if (response.Content.Contains("_children"))
|
||||
{
|
||||
return Json.Deserialize<PlexPreferencesLegacy>(response.Content)
|
||||
.Preferences;
|
||||
}
|
||||
|
||||
return Json.Deserialize<PlexResponse<PlexPreferences>>(response.Content)
|
||||
.MediaContainer
|
||||
.Preferences;
|
||||
}
|
||||
|
||||
public int? GetMetadataId(int sectionId, string mbId, string language, PlexServerSettings settings)
|
||||
{
|
||||
var guid = string.Format("com.plexapp.agents.lastfm://{0}?lang={1}", mbId, language); // TODO Plex Route for MB? LastFM?
|
||||
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, settings);
|
||||
|
||||
List<PlexSectionItem> items;
|
||||
|
||||
if (response.Content.Contains("_children"))
|
||||
{
|
||||
items = Json.Deserialize<PlexSectionResponseLegacy>(response.Content)
|
||||
.Items;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
items = Json.Deserialize<PlexResponse<PlexSectionResponse>>(response.Content)
|
||||
.MediaContainer
|
||||
.Items;
|
||||
}
|
||||
|
||||
if (items == null || items.Empty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return items.First().Id;
|
||||
}
|
||||
|
||||
private string Authenticate(PlexServerSettings settings)
|
||||
{
|
||||
var request = GetPlexTvRequest("users/sign_in.json", Method.POST);
|
||||
var client = GetPlexTvClient(settings.Username, settings.Password);
|
||||
|
||||
var response = client.Execute(request);
|
||||
|
||||
_logger.Debug("Authentication Response: {0}", response.Content);
|
||||
CheckForError(response, settings);
|
||||
|
||||
var user = Json.Deserialize<PlexUser>(JObject.Parse(response.Content).SelectToken("user").ToString());
|
||||
|
||||
return user.AuthenticationToken;
|
||||
}
|
||||
|
||||
private RestClient GetPlexTvClient(string username, string password)
|
||||
{
|
||||
var client = RestClientFactory.BuildClient("https://plex.tv");
|
||||
client.Authenticator = new HttpBasicAuthenticator(username, password);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private RestRequest GetPlexTvRequest(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-Device-Name", "Lidarr");
|
||||
request.AddHeader("X-Plex-Product", "Lidarr");
|
||||
request.AddHeader("X-Plex-Version", BuildInfo.Version.ToString());
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private RestClient GetPlexServerClient(PlexServerSettings settings)
|
||||
{
|
||||
var protocol = settings.UseSsl ? "https" : "http";
|
||||
|
||||
return RestClientFactory.BuildClient(string.Format("{0}://{1}:{2}", protocol, 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.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.AddParameter("X-Plex-Token", GetAuthenticationToken(settings), ParameterType.HttpHeader);
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private string GetAuthenticationToken(PlexServerSettings settings)
|
||||
{
|
||||
var token = _authCache.Get(settings.Username + settings.Password, () => Authenticate(settings));
|
||||
|
||||
if (token.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new PlexAuthenticationException("Invalid Token - Update your username and password");
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
private void CheckForError(IRestResponse response, PlexServerSettings settings)
|
||||
{
|
||||
_logger.Trace("Checking for error");
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
if (settings.Username.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new PlexAuthenticationException("Unauthorized - Username and password required");
|
||||
}
|
||||
|
||||
//Set the token to null in the cache so we don't keep trying with bad credentials
|
||||
_authCache.Set(settings.Username + settings.Password, null);
|
||||
throw new PlexAuthenticationException("Unauthorized - Username or password is incorrect");
|
||||
}
|
||||
|
||||
if (response.Content.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Trace("No response body returned, no error detected");
|
||||
return;
|
||||
}
|
||||
|
||||
var error = response.Content.Contains("_children") ?
|
||||
Json.Deserialize<PlexError>(response.Content) :
|
||||
Json.Deserialize<PlexResponse<PlexError>>(response.Content).MediaContainer;
|
||||
|
||||
if (error != null && !error.Error.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new PlexException(error.Error);
|
||||
}
|
||||
|
||||
_logger.Trace("No error detected");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
{
|
||||
public class PlexTvPinResponse
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Code { get; set; }
|
||||
public string AuthToken { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
{
|
||||
public interface IPlexTvProxy
|
||||
{
|
||||
PlexTvPinResponse GetPinCode(string clientIdentifier);
|
||||
string GetAuthToken(string clientIdentifier, int pinId);
|
||||
}
|
||||
public class PlexTvProxy : IPlexTvProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly Logger _logger;
|
||||
public PlexTvProxy(IHttpClient httpClient, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_logger = logger;
|
||||
}
|
||||
public PlexTvPinResponse GetPinCode(string clientIdentifier)
|
||||
{
|
||||
var request = BuildRequest(clientIdentifier);
|
||||
request.Method = HttpMethod.POST;
|
||||
request.ResourceUrl = "/api/v2/pins";
|
||||
request.AddQueryParam("strong", true);
|
||||
PlexTvPinResponse response;
|
||||
if (!Json.TryDeserialize<PlexTvPinResponse>(ProcessRequest(request), out response))
|
||||
{
|
||||
response = new PlexTvPinResponse();
|
||||
}
|
||||
return response;
|
||||
}
|
||||
public string GetAuthToken(string clientIdentifier, int pinId)
|
||||
{
|
||||
var request = BuildRequest(clientIdentifier);
|
||||
request.ResourceUrl = $"/api/v2/pins/{pinId}";
|
||||
PlexTvPinResponse response;
|
||||
if (!Json.TryDeserialize<PlexTvPinResponse>(ProcessRequest(request), out response))
|
||||
{
|
||||
response = new PlexTvPinResponse();
|
||||
}
|
||||
return response.AuthToken;
|
||||
}
|
||||
private HttpRequestBuilder BuildRequest(string clientIdentifier)
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder("https://plex.tv")
|
||||
.Accept(HttpAccept.Json)
|
||||
.AddQueryParam("X-Plex-Client-Identifier", clientIdentifier)
|
||||
.AddQueryParam("X-Plex-Product", "Lidarr")
|
||||
.AddQueryParam("X-Plex-Platform", "Windows")
|
||||
.AddQueryParam("X-Plex-Platform-Version", "7")
|
||||
.AddQueryParam("X-Plex-Device-Name", "Lidarr")
|
||||
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString());
|
||||
return requestBuilder;
|
||||
}
|
||||
private string ProcessRequest(HttpRequestBuilder requestBuilder)
|
||||
{
|
||||
var httpRequest = requestBuilder.Build();
|
||||
HttpResponse response;
|
||||
_logger.Debug("Url: {0}", httpRequest.Url);
|
||||
try
|
||||
{
|
||||
response = _httpClient.Execute(httpRequest);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
throw new NzbDroneClientException(ex.Response.StatusCode, "Unable to connect to plex.tv");
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new NzbDroneClientException(HttpStatusCode.BadRequest, "Unable to connect to plex.tv");
|
||||
}
|
||||
return response.Content;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
using System.Text;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Core.Configuration;
|
||||
namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
{
|
||||
public interface IPlexTvService
|
||||
{
|
||||
PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl);
|
||||
string GetAuthToken(int pinId);
|
||||
}
|
||||
public class PlexTvService : IPlexTvService
|
||||
{
|
||||
private readonly IPlexTvProxy _proxy;
|
||||
private readonly IConfigService _configService;
|
||||
public PlexTvService(IPlexTvProxy proxy, IConfigService configService)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_configService = configService;
|
||||
}
|
||||
public PlexTvSignInUrlResponse GetSignInUrl(string callbackUrl)
|
||||
{
|
||||
var clientIdentifier = _configService.PlexClientIdentifier;
|
||||
var pin = _proxy.GetPinCode(clientIdentifier);
|
||||
var url = new StringBuilder();
|
||||
url.Append("https://app.plex.tv/auth/#!");
|
||||
url.Append($"?clientID={clientIdentifier}");
|
||||
url.Append($"&forwardUrl={callbackUrl}");
|
||||
url.Append($"&code={pin.Code}");
|
||||
url.Append($"&context[device][version]=${BuildInfo.Version.ToString()}");
|
||||
url.Append("&context[device][product]=Lidarr");
|
||||
url.Append("&context[device][platform]=Windows");
|
||||
url.Append("&context[device][platformVersion]=7");
|
||||
return new PlexTvSignInUrlResponse
|
||||
{
|
||||
OauthUrl = url.ToString(),
|
||||
PinId = pin.Id
|
||||
};
|
||||
}
|
||||
public string GetAuthToken(int pinId)
|
||||
{
|
||||
var authToken = _proxy.GetAuthToken(_configService.PlexClientIdentifier, pinId);
|
||||
return authToken;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace NzbDrone.Core.Notifications.Plex.PlexTv
|
||||
{
|
||||
public class PlexTvSignInUrlResponse
|
||||
{
|
||||
public string OauthUrl { get; set; }
|
||||
public int PinId { get; set; }
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace NzbDrone.Core.Notifications.Plex.Models
|
||||
namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
{
|
||||
public class PlexSectionLocation
|
||||
{
|
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentValidation.Results;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.Validation;
|
||||
namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
{
|
||||
public class PlexServer : NotificationBase<PlexServerSettings>
|
||||
{
|
||||
private readonly IPlexServerService _plexServerService;
|
||||
private readonly IPlexTvService _plexTvService;
|
||||
public PlexServer(IPlexServerService plexServerService, IPlexTvService plexTvService)
|
||||
{
|
||||
_plexServerService = plexServerService;
|
||||
_plexTvService = plexTvService;
|
||||
}
|
||||
public override string Link => "https://www.plex.tv/";
|
||||
public override string Name => "Plex Media Server";
|
||||
public override void OnDownload(TrackDownloadMessage message)
|
||||
{
|
||||
UpdateIfEnabled(message.Artist);
|
||||
}
|
||||
public override void OnAlbumDownload(AlbumDownloadMessage message)
|
||||
{
|
||||
UpdateIfEnabled(message.Artist);
|
||||
}
|
||||
public override void OnRename(Artist artist)
|
||||
{
|
||||
UpdateIfEnabled(artist);
|
||||
}
|
||||
private void UpdateIfEnabled(Artist artist)
|
||||
{
|
||||
if (Settings.UpdateLibrary)
|
||||
{
|
||||
_plexServerService.UpdateLibrary(artist, Settings);
|
||||
}
|
||||
}
|
||||
public override ValidationResult Test()
|
||||
{
|
||||
var failures = new List<ValidationFailure>();
|
||||
failures.AddIfNotNull(_plexServerService.Test(Settings));
|
||||
return new ValidationResult(failures);
|
||||
}
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
{
|
||||
if (action == "startOAuth")
|
||||
{
|
||||
Settings.Validate().Filter("ConsumerKey", "ConsumerSecret").ThrowOnError();
|
||||
if (query["callbackUrl"].IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new BadRequestException("QueryParam callbackUrl invalid.");
|
||||
}
|
||||
return _plexTvService.GetSignInUrl(query["callbackUrl"]);
|
||||
}
|
||||
else if (action == "getOAuthToken")
|
||||
{
|
||||
Settings.Validate().Filter("ConsumerKey", "ConsumerSecret").ThrowOnError();
|
||||
if (query["pinId"].IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new BadRequestException("QueryParam pinId invalid.");
|
||||
}
|
||||
var authToken = _plexTvService.GetAuthToken(Convert.ToInt32(query["pinId"]));
|
||||
return new
|
||||
{
|
||||
authToken
|
||||
};
|
||||
}
|
||||
return new { };
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,184 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NLog;
|
||||
using NzbDrone.Common.EnvironmentInfo;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.Configuration;
|
||||
namespace NzbDrone.Core.Notifications.Plex.Server
|
||||
{
|
||||
public interface IPlexServerProxy
|
||||
{
|
||||
List<PlexSection> GetArtistSections(PlexServerSettings settings);
|
||||
void Update(int sectionId, PlexServerSettings settings);
|
||||
void UpdateArtist(int metadataId, PlexServerSettings settings);
|
||||
string Version(PlexServerSettings settings);
|
||||
List<PlexPreference> Preferences(PlexServerSettings settings);
|
||||
int? GetMetadataId(int sectionId, string mbId, string language, PlexServerSettings settings);
|
||||
}
|
||||
public class PlexServerProxy : IPlexServerProxy
|
||||
{
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
public PlexServerProxy(IHttpClient httpClient, IConfigService configService, Logger logger)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
public List<PlexSection> GetArtistSections(PlexServerSettings settings)
|
||||
{
|
||||
var request = BuildRequest("library/sections", HttpMethod.GET, settings);
|
||||
var response = ProcessRequest(request);
|
||||
CheckForError(response);
|
||||
if (response.Contains("_children"))
|
||||
{
|
||||
return Json.Deserialize<PlexMediaContainerLegacy>(response)
|
||||
.Sections
|
||||
.Where(d => d.Type == "artist")
|
||||
.Select(s => new PlexSection
|
||||
{
|
||||
Id = s.Id,
|
||||
Language = s.Language,
|
||||
Locations = s.Locations,
|
||||
Type = s.Type
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
return Json.Deserialize<PlexResponse<PlexSectionsContainer>>(response)
|
||||
.MediaContainer
|
||||
.Sections
|
||||
.Where(d => d.Type == "artist")
|
||||
.ToList();
|
||||
}
|
||||
public void Update(int sectionId, PlexServerSettings settings)
|
||||
{
|
||||
var resource = $"library/sections/{sectionId}/refresh";
|
||||
var request = BuildRequest(resource, HttpMethod.GET, settings);
|
||||
var response = ProcessRequest(request);
|
||||
CheckForError(response);
|
||||
}
|
||||
public void UpdateArtist(int metadataId, PlexServerSettings settings)
|
||||
{
|
||||
var resource = $"library/metadata/{metadataId}/refresh";
|
||||
var request = BuildRequest(resource, HttpMethod.PUT, settings);
|
||||
var response = ProcessRequest(request);
|
||||
CheckForError(response);
|
||||
}
|
||||
public string Version(PlexServerSettings settings)
|
||||
{
|
||||
var request = BuildRequest("identity", HttpMethod.GET, settings);
|
||||
var response = ProcessRequest(request);
|
||||
CheckForError(response);
|
||||
if (response.Contains("_children"))
|
||||
{
|
||||
return Json.Deserialize<PlexIdentity>(response)
|
||||
.Version;
|
||||
}
|
||||
return Json.Deserialize<PlexResponse<PlexIdentity>>(response)
|
||||
.MediaContainer
|
||||
.Version;
|
||||
}
|
||||
public List<PlexPreference> Preferences(PlexServerSettings settings)
|
||||
{
|
||||
var request = BuildRequest(":/prefs", HttpMethod.GET, settings);
|
||||
var response = ProcessRequest(request);
|
||||
CheckForError(response);
|
||||
if (response.Contains("_children"))
|
||||
{
|
||||
return Json.Deserialize<PlexPreferencesLegacy>(response)
|
||||
.Preferences;
|
||||
}
|
||||
return Json.Deserialize<PlexResponse<PlexPreferences>>(response)
|
||||
.MediaContainer
|
||||
.Preferences;
|
||||
}
|
||||
public int? GetMetadataId(int sectionId, string mbId, string language, PlexServerSettings settings)
|
||||
{
|
||||
var guid = string.Format("com.plexapp.agents.lastfm://{0}?lang={1}", mbId, language); // TODO Plex Route for MB? LastFM?
|
||||
var resource = $"library/sections/{sectionId}/all?guid={System.Web.HttpUtility.UrlEncode(guid)}";
|
||||
var request = BuildRequest(resource, HttpMethod.GET, settings);
|
||||
var response = ProcessRequest(request);
|
||||
CheckForError(response);
|
||||
List<PlexSectionItem> items;
|
||||
if (response.Contains("_children"))
|
||||
{
|
||||
items = Json.Deserialize<PlexSectionResponseLegacy>(response)
|
||||
.Items;
|
||||
}
|
||||
else
|
||||
{
|
||||
items = Json.Deserialize<PlexResponse<PlexSectionResponse>>(response)
|
||||
.MediaContainer
|
||||
.Items;
|
||||
}
|
||||
if (items == null || items.Empty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return items.First().Id;
|
||||
}
|
||||
private HttpRequestBuilder BuildRequest(string resource, HttpMethod method, PlexServerSettings settings)
|
||||
{
|
||||
var scheme = settings.UseSsl ? "https" : "http";
|
||||
var requestBuilder = new HttpRequestBuilder($"{scheme}://{settings.Host}:{settings.Port}")
|
||||
.Accept(HttpAccept.Json)
|
||||
.AddQueryParam("X-Plex-Client-Identifier", _configService.PlexClientIdentifier)
|
||||
.AddQueryParam("X-Plex-Product", "Lidarr")
|
||||
.AddQueryParam("X-Plex-Platform", "Windows")
|
||||
.AddQueryParam("X-Plex-Platform-Version", "7")
|
||||
.AddQueryParam("X-Plex-Device-Name", "Lidarr")
|
||||
.AddQueryParam("X-Plex-Version", BuildInfo.Version.ToString());
|
||||
if (settings.AuthToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
requestBuilder.AddQueryParam("X-Plex-Token", settings.AuthToken);
|
||||
}
|
||||
requestBuilder.ResourceUrl = resource;
|
||||
requestBuilder.Method = method;
|
||||
return requestBuilder;
|
||||
}
|
||||
private string ProcessRequest(HttpRequestBuilder requestBuilder)
|
||||
{
|
||||
var httpRequest = requestBuilder.Build();
|
||||
HttpResponse response;
|
||||
_logger.Debug("Url: {0}", httpRequest.Url);
|
||||
try
|
||||
{
|
||||
response = _httpClient.Execute(httpRequest);
|
||||
}
|
||||
catch (HttpException ex)
|
||||
{
|
||||
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||
{
|
||||
throw new PlexAuthenticationException("Unauthorized - AuthToken is invalid");
|
||||
}
|
||||
throw new PlexException("Unable to connect to Plex Media Server");
|
||||
}
|
||||
catch (WebException ex)
|
||||
{
|
||||
throw new PlexException("Unable to connect to Plex Media Server");
|
||||
}
|
||||
return response.Content;
|
||||
}
|
||||
private void CheckForError(string response)
|
||||
{
|
||||
_logger.Trace("Checking for error");
|
||||
if (response.IsNullOrWhiteSpace())
|
||||
{
|
||||
_logger.Trace("No response body returned, no error detected");
|
||||
return;
|
||||
}
|
||||
var error = response.Contains("_children") ?
|
||||
Json.Deserialize<PlexError>(response) :
|
||||
Json.Deserialize<PlexResponse<PlexError>>(response).MediaContainer;
|
||||
if (error != null && !error.Error.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new PlexException(error.Error);
|
||||
}
|
||||
_logger.Trace("No error detected");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue