parent
5923b4ae0d
commit
f6fbd3cfee
@ -0,0 +1,37 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.ImportLists;
|
||||
using NzbDrone.Core.ImportLists.Plex;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ImportList.Plex
|
||||
{
|
||||
public class PlexTest : CoreTest<PlexParser>
|
||||
{
|
||||
private ImportListResponse CreateResponse(string url, string content)
|
||||
{
|
||||
var httpRequest = new HttpRequest(url);
|
||||
var httpResponse = new HttpResponse(httpRequest, new HttpHeader(), Encoding.UTF8.GetBytes(content));
|
||||
|
||||
return new ImportListResponse(new ImportListRequest(httpRequest), httpResponse);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_parse_plex_watchlist()
|
||||
{
|
||||
var json = ReadAllText("Files/plex_watchlist.json");
|
||||
|
||||
var result = Subject.ParseResponse(CreateResponse("https://metadata.provider.plex.tv/library/sections/watchlist/all", json));
|
||||
|
||||
result.First().Title.Should().Be("30 Rock");
|
||||
result.First().Year.Should().Be(2006);
|
||||
result.First().TvdbId.Should().Be(79488);
|
||||
|
||||
result[1].TmdbId.Should().Be(4533);
|
||||
result[1].ImdbId.Should().Be("tt0475900");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Exceptions;
|
||||
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Plex
|
||||
{
|
||||
public class PlexImport : HttpImportListBase<PlexListSettings>
|
||||
{
|
||||
public readonly IPlexTvService _plexTvService;
|
||||
public override ImportListType ListType => ImportListType.Plex;
|
||||
|
||||
public PlexImport(IPlexTvService plexTvService,
|
||||
IHttpClient httpClient,
|
||||
IImportListStatusService importListStatusService,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
Logger logger)
|
||||
: base(httpClient, importListStatusService, configService, parsingService, logger)
|
||||
{
|
||||
_plexTvService = plexTvService;
|
||||
}
|
||||
|
||||
public override string Name => "Plex Watchlist";
|
||||
|
||||
public override IList<ImportListItemInfo> Fetch()
|
||||
{
|
||||
Settings.Validate().Filter("AccessToken").ThrowOnError();
|
||||
|
||||
// var generator = GetRequestGenerator();
|
||||
|
||||
return FetchItems(g =>g.GetListItems());
|
||||
}
|
||||
|
||||
public override IParseImportListResponse GetParser()
|
||||
{
|
||||
return new PlexParser();
|
||||
}
|
||||
|
||||
public override IImportListRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new PlexListRequestGenerator(_plexTvService)
|
||||
{
|
||||
Settings = Settings
|
||||
};
|
||||
}
|
||||
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
{
|
||||
if (action == "startOAuth")
|
||||
{
|
||||
Settings.Validate().Filter("ConsumerKey", "ConsumerSecret").ThrowOnError();
|
||||
|
||||
return _plexTvService.GetPinUrl();
|
||||
}
|
||||
else if (action == "continueOAuth")
|
||||
{
|
||||
Settings.Validate().Filter("ConsumerKey", "ConsumerSecret").ThrowOnError();
|
||||
|
||||
if (query["callbackUrl"].IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new BadRequestException("QueryParam callbackUrl invalid.");
|
||||
}
|
||||
|
||||
if (query["id"].IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new BadRequestException("QueryParam id invalid.");
|
||||
}
|
||||
|
||||
if (query["code"].IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new BadRequestException("QueryParam code invalid.");
|
||||
}
|
||||
|
||||
return _plexTvService.GetSignInUrl(query["callbackUrl"], Convert.ToInt32(query["id"]), query["code"]);
|
||||
}
|
||||
else if (action == "getOAuthToken")
|
||||
{
|
||||
Settings.Validate().Filter("ConsumerKey", "ConsumerSecret").ThrowOnError();
|
||||
|
||||
if (query["pinId"].IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new BadRequestException("QueryParam pinId invalid.");
|
||||
}
|
||||
|
||||
var accessToken = _plexTvService.GetAuthToken(Convert.ToInt32(query["pinId"]));
|
||||
|
||||
return new
|
||||
{
|
||||
accessToken
|
||||
};
|
||||
}
|
||||
|
||||
return new { };
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Notifications.Plex.PlexTv;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Plex
|
||||
{
|
||||
public class PlexListRequestGenerator : IImportListRequestGenerator
|
||||
{
|
||||
private readonly IPlexTvService _plexTvService;
|
||||
public PlexListSettings Settings { get; set; }
|
||||
|
||||
public PlexListRequestGenerator(IPlexTvService plexTvService)
|
||||
{
|
||||
_plexTvService = plexTvService;
|
||||
}
|
||||
|
||||
public virtual ImportListPageableRequestChain GetListItems()
|
||||
{
|
||||
var pageableRequests = new ImportListPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetMoviesRequest());
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<ImportListRequest> GetMoviesRequest()
|
||||
{
|
||||
var request = new ImportListRequest(_plexTvService.GetWatchlist(Settings.AccessToken));
|
||||
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Plex
|
||||
{
|
||||
public class PlexListSettingsValidator : AbstractValidator<PlexListSettings>
|
||||
{
|
||||
public PlexListSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.AccessToken).NotEmpty()
|
||||
.OverridePropertyName("SignIn")
|
||||
.WithMessage("Must authenticate with Plex");
|
||||
}
|
||||
}
|
||||
|
||||
public class PlexListSettings : IImportListSettings
|
||||
{
|
||||
protected virtual PlexListSettingsValidator Validator => new PlexListSettingsValidator();
|
||||
|
||||
public PlexListSettings()
|
||||
{
|
||||
SignIn = "startOAuth";
|
||||
}
|
||||
|
||||
public virtual string Scope => "";
|
||||
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AccessToken { get; set; }
|
||||
|
||||
[FieldDefinition(99, Label = "Authenticate with Plex.tv", Type = FieldType.OAuth)]
|
||||
public string SignIn { get; set; }
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using NzbDrone.Common.Serializer;
|
||||
using NzbDrone.Core.ImportLists.Exceptions;
|
||||
using NzbDrone.Core.Notifications.Plex.Server;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Plex
|
||||
{
|
||||
public class PlexParser : IParseImportListResponse
|
||||
{
|
||||
private ImportListResponse _importResponse;
|
||||
|
||||
public virtual IList<ImportListItemInfo> ParseResponse(ImportListResponse importResponse)
|
||||
{
|
||||
List<PlexSectionItem> items;
|
||||
|
||||
_importResponse = importResponse;
|
||||
|
||||
var series = new List<ImportListItemInfo>();
|
||||
|
||||
if (!PreProcess(_importResponse))
|
||||
{
|
||||
return series;
|
||||
}
|
||||
|
||||
items = Json.Deserialize<PlexResponse<PlexSectionResponse>>(_importResponse.Content)
|
||||
.MediaContainer
|
||||
.Items;
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var tvdbIdString = FindGuid(item.Guids, "tvdb");
|
||||
var tmdbIdString = FindGuid(item.Guids, "tmdb");
|
||||
var imdbId = FindGuid(item.Guids, "imdb");
|
||||
|
||||
int.TryParse(tvdbIdString, out int tvdbId);
|
||||
int.TryParse(tmdbIdString, out int tmdbId);
|
||||
|
||||
series.Add(new ImportListItemInfo
|
||||
{
|
||||
TvdbId = tvdbId,
|
||||
TmdbId = tmdbId,
|
||||
ImdbId = imdbId,
|
||||
Title = item.Title,
|
||||
Year = item.Year
|
||||
});
|
||||
}
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
protected virtual bool PreProcess(ImportListResponse importListResponse)
|
||||
{
|
||||
if (importListResponse.HttpResponse.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new ImportListException(importListResponse, "Plex API call resulted in an unexpected StatusCode [{0}]", importListResponse.HttpResponse.StatusCode);
|
||||
}
|
||||
|
||||
if (importListResponse.HttpResponse.Headers.ContentType != null && importListResponse.HttpResponse.Headers.ContentType.Contains("text/json") &&
|
||||
importListResponse.HttpRequest.Headers.Accept != null && !importListResponse.HttpRequest.Headers.Accept.Contains("text/json"))
|
||||
{
|
||||
throw new ImportListException(importListResponse, "Plex API responded with html content. Site is likely blocked or unavailable.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string FindGuid(List<PlexSectionItemGuid> guids, string prefix)
|
||||
{
|
||||
var scheme = $"{prefix}://";
|
||||
|
||||
return guids.FirstOrDefault((guid) => guid.Id.StartsWith(scheme))?.Id.Replace(scheme, "");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace NzbDrone.Core.Notifications.Plex
|
||||
{
|
||||
public enum PlexMediaType
|
||||
{
|
||||
None,
|
||||
Movie,
|
||||
Show
|
||||
}
|
||||
}
|
Loading…
Reference in new issue