parent
5267e15c17
commit
86fa6036d0
@ -0,0 +1,20 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktAuthRefreshResource
|
||||||
|
{
|
||||||
|
[JsonProperty(PropertyName = "access_token")]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "token_type")]
|
||||||
|
public string TokenType { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "expires_in")]
|
||||||
|
public int ExpiresIn { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "refresh_token")]
|
||||||
|
public string RefreshToken { get; set; }
|
||||||
|
public string Scope { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktCollectShow : TraktShowResource
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktCollectShowsResource
|
||||||
|
{
|
||||||
|
public List<TraktCollectShow> Shows { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktEpisodeResource
|
||||||
|
{
|
||||||
|
public int Number { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "collected_at")]
|
||||||
|
public DateTime CollectedAt { get; set; }
|
||||||
|
public string Resolution { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "audio_channels")]
|
||||||
|
public string AudioChannels { get; set; }
|
||||||
|
public string Audio { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "media_type")]
|
||||||
|
public string MediaType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktListResource
|
||||||
|
{
|
||||||
|
public int? Rank { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "listed_at")]
|
||||||
|
public string ListedAt { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "watcher_count")]
|
||||||
|
public long? WatcherCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "play_count")]
|
||||||
|
public long? PlayCount { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "collected_count")]
|
||||||
|
public long? CollectedCount { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public int? Watchers { get; set; }
|
||||||
|
public long? Revenue { get; set; }
|
||||||
|
public TraktEpisodeResource Episode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktSeasonResource
|
||||||
|
{
|
||||||
|
public int Number { get; set; }
|
||||||
|
public List<TraktEpisodeResource> Episodes {get; set;}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktShowIdsResource
|
||||||
|
{
|
||||||
|
public int Trakt { get; set; }
|
||||||
|
public string Slug { get; set; }
|
||||||
|
public string Imdb { get; set; }
|
||||||
|
public int Tvdb { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktShowResource
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public int? Year { get; set; }
|
||||||
|
public TraktShowIdsResource Ids { get; set; }
|
||||||
|
public List<TraktSeasonResource> Seasons { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktUserIdsResource
|
||||||
|
{
|
||||||
|
public string Slug { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using NzbDrone.Core.Notifications.Trakt.Resource;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
|
{
|
||||||
|
public class TraktUserResource
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public TraktUserIdsResource Ids { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace NzbDrone.Core.Notifications.Trakt.Resource
|
||||||
|
{
|
||||||
|
public class TraktUserSettingsResource
|
||||||
|
{
|
||||||
|
public TraktUserResource User { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
|
{
|
||||||
|
public class Trakt : NotificationBase<TraktSettings>
|
||||||
|
{
|
||||||
|
private readonly ITraktService _traktService;
|
||||||
|
private readonly INotificationRepository _notificationRepository;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public Trakt(ITraktService traktService, INotificationRepository notificationRepository, Logger logger)
|
||||||
|
{
|
||||||
|
_traktService = traktService;
|
||||||
|
_notificationRepository = notificationRepository;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string Link => "https://trakt.tv/";
|
||||||
|
public override string Name => "Trakt";
|
||||||
|
|
||||||
|
public override void OnDownload(DownloadMessage message)
|
||||||
|
{
|
||||||
|
_traktService.AddEpisodeToCollection(Settings, message.Series, message.EpisodeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEpisodeFileDelete(EpisodeDeleteMessage deleteMessage)
|
||||||
|
{
|
||||||
|
_traktService.RemoveEpisodeFromCollection(Settings, deleteMessage.Series, deleteMessage.EpisodeFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSeriesDelete(SeriesDeleteMessage deleteMessage)
|
||||||
|
{
|
||||||
|
_traktService.RemoveSeriesFromCollection(Settings, deleteMessage.Series);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ValidationResult Test()
|
||||||
|
{
|
||||||
|
var failures = new List<ValidationFailure>();
|
||||||
|
|
||||||
|
failures.AddIfNotNull(_traktService.Test(Settings));
|
||||||
|
|
||||||
|
return new ValidationResult(failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||||
|
{
|
||||||
|
if (action == "startOAuth")
|
||||||
|
{
|
||||||
|
var request = _traktService.GetOAuthRequest(query["callbackUrl"]);
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
OauthUrl = request.Url.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (action == "getOAuthToken")
|
||||||
|
{
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
accessToken = query["access_token"],
|
||||||
|
expires = DateTime.UtcNow.AddSeconds(int.Parse(query["expires_in"])),
|
||||||
|
refreshToken = query["refresh_token"],
|
||||||
|
authUser = _traktService.GetUserName(query["access_token"])
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return new { };
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshToken()
|
||||||
|
{
|
||||||
|
_logger.Trace("Refreshing Token");
|
||||||
|
|
||||||
|
Settings.Validate().Filter("RefreshToken").ThrowOnError();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = _traktService.RefreshAuthToken(Settings.RefreshToken);
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
var token = response;
|
||||||
|
Settings.AccessToken = token.AccessToken;
|
||||||
|
Settings.Expires = DateTime.UtcNow.AddSeconds(token.ExpiresIn);
|
||||||
|
Settings.RefreshToken = token.RefreshToken ?? Settings.RefreshToken;
|
||||||
|
|
||||||
|
if (Definition.Id > 0)
|
||||||
|
{
|
||||||
|
_notificationRepository.UpdateSettings((NotificationDefinition)Definition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HttpException)
|
||||||
|
{
|
||||||
|
_logger.Warn($"Error refreshing trakt access token");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using NzbDrone.Common.Exceptions;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
|
{
|
||||||
|
public class TraktException : NzbDroneException
|
||||||
|
{
|
||||||
|
public TraktException(string message)
|
||||||
|
: base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TraktException(string message, Exception innerException, params object[] args)
|
||||||
|
: base(message, innerException, args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
|
{
|
||||||
|
public static class TraktInterlacedTypes
|
||||||
|
{
|
||||||
|
private static HashSet<string> _interlacedTypes;
|
||||||
|
|
||||||
|
static TraktInterlacedTypes()
|
||||||
|
{
|
||||||
|
_interlacedTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
"Interlaced", "MBAFF", "PAFF"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HashSet<string> interlacedTypes => _interlacedTypes;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Common.Serializer;
|
||||||
|
using NzbDrone.Core.Notifications.Trakt.Resource;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
|
{
|
||||||
|
public interface ITraktProxy
|
||||||
|
{
|
||||||
|
string GetUserName(string accessToken);
|
||||||
|
HttpRequest GetOAuthRequest(string callbackUrl);
|
||||||
|
TraktAuthRefreshResource RefreshAuthToken(string refreshToken);
|
||||||
|
void AddToCollection(TraktCollectShowsResource payload, string accessToken);
|
||||||
|
void RemoveFromCollection(TraktCollectShowsResource payload, string accessToken);
|
||||||
|
HttpRequest BuildTraktRequest(string resource, HttpMethod method, string accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TraktProxy : ITraktProxy
|
||||||
|
{
|
||||||
|
private const string URL = "https://api.trakt.tv";
|
||||||
|
private const string OAuthUrl = "https://trakt.tv/oauth/authorize";
|
||||||
|
private const string RedirectUri = "https://auth.servarr.com/v1/trakt_sonarr/auth";
|
||||||
|
private const string RenewUri = "https://auth.servarr.com/v1/trakt_sonarr/renew";
|
||||||
|
private const string ClientId = "d44ba57cab40c31eb3f797dcfccd203500796539125b333883ec1d94aa62ed4c";
|
||||||
|
|
||||||
|
private readonly IHttpClient _httpClient;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public TraktProxy(IHttpClient httpClient, Logger logger)
|
||||||
|
{
|
||||||
|
_httpClient = httpClient;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddToCollection(TraktCollectShowsResource payload, string accessToken)
|
||||||
|
{
|
||||||
|
var request = BuildTraktRequest("sync/collection", HttpMethod.POST, accessToken);
|
||||||
|
|
||||||
|
request.Headers.ContentType = "application/json";
|
||||||
|
request.SetContent(payload.ToJson());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.Execute(request);
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to post payload {0}", payload);
|
||||||
|
throw new TraktException("Unable to post payload", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveFromCollection(TraktCollectShowsResource payload, string accessToken)
|
||||||
|
{
|
||||||
|
var request = BuildTraktRequest("sync/collection/remove", HttpMethod.POST, accessToken);
|
||||||
|
|
||||||
|
request.Headers.ContentType = "application/json";
|
||||||
|
var temp = payload.ToJson();
|
||||||
|
request.SetContent(payload.ToJson());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.Execute(request);
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to post payload {0}", payload);
|
||||||
|
throw new TraktException("Unable to post payload", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetUserName(string accessToken)
|
||||||
|
{
|
||||||
|
var request = BuildTraktRequest("users/settings", HttpMethod.GET, accessToken);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = _httpClient.Get<TraktUserSettingsResource>(request);
|
||||||
|
|
||||||
|
if (response != null && response.Resource != null)
|
||||||
|
{
|
||||||
|
return response.Resource.User.Ids.Slug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HttpException)
|
||||||
|
{
|
||||||
|
_logger.Warn($"Error refreshing trakt access token");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest GetOAuthRequest(string callbackUrl)
|
||||||
|
{
|
||||||
|
return new HttpRequestBuilder(OAuthUrl)
|
||||||
|
.AddQueryParam("client_id", ClientId)
|
||||||
|
.AddQueryParam("response_type", "code")
|
||||||
|
.AddQueryParam("redirect_uri", RedirectUri)
|
||||||
|
.AddQueryParam("state", callbackUrl)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TraktAuthRefreshResource RefreshAuthToken(string refreshToken)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestBuilder(RenewUri)
|
||||||
|
.AddQueryParam("refresh_token", refreshToken)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return _httpClient.Get<TraktAuthRefreshResource>(request)?.Resource ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest BuildTraktRequest(string resource, HttpMethod method, string accessToken)
|
||||||
|
{
|
||||||
|
var request = new HttpRequestBuilder(URL).Resource(resource).Build();
|
||||||
|
request.Method = method;
|
||||||
|
|
||||||
|
request.Headers.Accept = HttpAccept.Json.Value;
|
||||||
|
request.Headers.Add("trakt-api-version", "2");
|
||||||
|
request.Headers.Add("trakt-api-key", ClientId);
|
||||||
|
|
||||||
|
if (accessToken.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
request.Headers.Add("Authorization", "Bearer " + accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,323 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using FluentValidation.Results;
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Http;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
|
using NzbDrone.Core.Tv;
|
||||||
|
using NzbDrone.Core.Notifications.Trakt.Resource;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
|
using NzbDrone.Core.MetadataSource.SkyHook.Resource;
|
||||||
|
using NzbDrone.Core.Indexers.HDBits;
|
||||||
|
using NzbDrone.Core.IndexerSearch;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
|
{
|
||||||
|
public interface ITraktService
|
||||||
|
{
|
||||||
|
HttpRequest GetOAuthRequest(string callbackUrl);
|
||||||
|
TraktAuthRefreshResource RefreshAuthToken(string refreshToken);
|
||||||
|
void AddEpisodeToCollection(TraktSettings settings, Series series, EpisodeFile episodeFile);
|
||||||
|
void RemoveEpisodeFromCollection(TraktSettings settings, Series series, EpisodeFile episodeFile);
|
||||||
|
void RemoveSeriesFromCollection(TraktSettings settings, Series series);
|
||||||
|
string GetUserName(string accessToken);
|
||||||
|
ValidationFailure Test(TraktSettings settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TraktService : ITraktService
|
||||||
|
{
|
||||||
|
private readonly ITraktProxy _proxy;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public TraktService(ITraktProxy proxy,
|
||||||
|
Logger logger)
|
||||||
|
{
|
||||||
|
_proxy = proxy;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetUserName(string accessToken)
|
||||||
|
{
|
||||||
|
return _proxy.GetUserName(accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpRequest GetOAuthRequest(string callbackUrl)
|
||||||
|
{
|
||||||
|
return _proxy.GetOAuthRequest(callbackUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TraktAuthRefreshResource RefreshAuthToken(string refreshToken)
|
||||||
|
{
|
||||||
|
return _proxy.RefreshAuthToken(refreshToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationFailure Test(TraktSettings settings)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
GetUserName(settings.AccessToken);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Access Token is invalid: " + ex.Message);
|
||||||
|
|
||||||
|
return new ValidationFailure("Token", "Access Token is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Error(ex, "Unable to send test message: " + ex.Message);
|
||||||
|
|
||||||
|
return new ValidationFailure("Token", "Unable to send test message");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to send test message: " + ex.Message);
|
||||||
|
|
||||||
|
return new ValidationFailure("", "Unable to send test message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddEpisodeToCollection(TraktSettings settings, Series series, EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
var payload = new TraktCollectShowsResource
|
||||||
|
{
|
||||||
|
Shows = new List<TraktCollectShow>()
|
||||||
|
};
|
||||||
|
|
||||||
|
var traktResolution = MapResolution(episodeFile.Quality.Quality.Resolution, episodeFile.MediaInfo?.ScanType);
|
||||||
|
var mediaType = MapMediaType(episodeFile.Quality.Quality.Source);
|
||||||
|
var audio = MapAudio(episodeFile);
|
||||||
|
var audioChannels = MapAudioChannels(episodeFile, audio);
|
||||||
|
|
||||||
|
var payloadEpisodes = new List<TraktEpisodeResource>();
|
||||||
|
|
||||||
|
foreach (var episode in episodeFile.Episodes.Value)
|
||||||
|
{
|
||||||
|
payloadEpisodes.Add(new TraktEpisodeResource
|
||||||
|
{
|
||||||
|
Number = episode.EpisodeNumber,
|
||||||
|
CollectedAt = DateTime.Now,
|
||||||
|
Resolution = traktResolution,
|
||||||
|
MediaType = mediaType,
|
||||||
|
AudioChannels = audioChannels,
|
||||||
|
Audio = audio,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var payloadSeasons = new List<TraktSeasonResource>();
|
||||||
|
payloadSeasons.Add(new TraktSeasonResource
|
||||||
|
{
|
||||||
|
Number = episodeFile.SeasonNumber,
|
||||||
|
Episodes = payloadEpisodes
|
||||||
|
});
|
||||||
|
|
||||||
|
payload.Shows.Add(new TraktCollectShow
|
||||||
|
{
|
||||||
|
Title = series.Title,
|
||||||
|
Year = series.Year,
|
||||||
|
Ids = new TraktShowIdsResource
|
||||||
|
{
|
||||||
|
Tvdb = series.TvdbId,
|
||||||
|
Imdb = series.ImdbId ?? "",
|
||||||
|
},
|
||||||
|
Seasons = payloadSeasons,
|
||||||
|
}); ;
|
||||||
|
|
||||||
|
_proxy.AddToCollection(payload, settings.AccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveEpisodeFromCollection(TraktSettings settings, Series series, EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
var payload = new TraktCollectShowsResource
|
||||||
|
{
|
||||||
|
Shows = new List<TraktCollectShow>()
|
||||||
|
};
|
||||||
|
|
||||||
|
var payloadEpisodes = new List<TraktEpisodeResource>();
|
||||||
|
|
||||||
|
foreach (var episode in episodeFile.Episodes.Value)
|
||||||
|
{
|
||||||
|
payloadEpisodes.Add(new TraktEpisodeResource
|
||||||
|
{
|
||||||
|
Number = episode.EpisodeNumber
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var payloadSeasons = new List<TraktSeasonResource>();
|
||||||
|
payloadSeasons.Add(new TraktSeasonResource
|
||||||
|
{
|
||||||
|
Number = episodeFile.SeasonNumber,
|
||||||
|
Episodes = payloadEpisodes
|
||||||
|
});
|
||||||
|
|
||||||
|
payload.Shows.Add(new TraktCollectShow
|
||||||
|
{
|
||||||
|
Title = series.Title,
|
||||||
|
Year = series.Year,
|
||||||
|
Ids = new TraktShowIdsResource
|
||||||
|
{
|
||||||
|
Tvdb = series.TvdbId,
|
||||||
|
Imdb = series.ImdbId ?? "",
|
||||||
|
},
|
||||||
|
Seasons = payloadSeasons,
|
||||||
|
}); ;
|
||||||
|
|
||||||
|
_proxy.RemoveFromCollection(payload, settings.AccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveSeriesFromCollection(TraktSettings settings, Series series)
|
||||||
|
{
|
||||||
|
var payload = new TraktCollectShowsResource
|
||||||
|
{
|
||||||
|
Shows = new List<TraktCollectShow>()
|
||||||
|
};
|
||||||
|
|
||||||
|
payload.Shows.Add(new TraktCollectShow
|
||||||
|
{
|
||||||
|
Title = series.Title,
|
||||||
|
Year = series.Year,
|
||||||
|
Ids = new TraktShowIdsResource
|
||||||
|
{
|
||||||
|
Tvdb = series.TvdbId,
|
||||||
|
Imdb = series.ImdbId ?? "",
|
||||||
|
},
|
||||||
|
}); ;
|
||||||
|
|
||||||
|
_proxy.RemoveFromCollection(payload, settings.AccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapMediaType(QualitySource source)
|
||||||
|
{
|
||||||
|
var traktSource = string.Empty;
|
||||||
|
|
||||||
|
switch (source)
|
||||||
|
{
|
||||||
|
case QualitySource.Web:
|
||||||
|
case QualitySource.WebRip:
|
||||||
|
traktSource = "digital";
|
||||||
|
break;
|
||||||
|
case QualitySource.BlurayRaw:
|
||||||
|
case QualitySource.Bluray:
|
||||||
|
traktSource = "bluray";
|
||||||
|
break;
|
||||||
|
case QualitySource.Television:
|
||||||
|
case QualitySource.TelevisionRaw:
|
||||||
|
traktSource = "vhs";
|
||||||
|
break;
|
||||||
|
case QualitySource.DVD:
|
||||||
|
traktSource = "dvd";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return traktSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapResolution(int resolution, string scanType)
|
||||||
|
{
|
||||||
|
var traktResolution = string.Empty;
|
||||||
|
//var interlacedTypes = new string[] { "Interlaced", "MBAFF", "PAFF" };
|
||||||
|
|
||||||
|
var scanIdentifier = scanType.IsNotNullOrWhiteSpace() && TraktInterlacedTypes.interlacedTypes.Contains(scanType) ? "i" : "p";
|
||||||
|
|
||||||
|
switch (resolution)
|
||||||
|
{
|
||||||
|
case 2160:
|
||||||
|
traktResolution = "uhd_4k";
|
||||||
|
break;
|
||||||
|
case 1080:
|
||||||
|
traktResolution = $"hd_1080{scanIdentifier}";
|
||||||
|
break;
|
||||||
|
case 720:
|
||||||
|
traktResolution = "hd_720p";
|
||||||
|
break;
|
||||||
|
case 576:
|
||||||
|
traktResolution = $"sd_576{scanIdentifier}";
|
||||||
|
break;
|
||||||
|
case 480:
|
||||||
|
traktResolution = $"sd_480{scanIdentifier}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return traktResolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapAudio(EpisodeFile episodeFile)
|
||||||
|
{
|
||||||
|
var traktAudioFormat = string.Empty;
|
||||||
|
|
||||||
|
var audioCodec = episodeFile.MediaInfo != null ? MediaInfoFormatter.FormatAudioCodec(episodeFile.MediaInfo, episodeFile.SceneName) : string.Empty;
|
||||||
|
|
||||||
|
switch (audioCodec)
|
||||||
|
{
|
||||||
|
case "AC3":
|
||||||
|
traktAudioFormat = "dolby_digital";
|
||||||
|
break;
|
||||||
|
case "EAC3":
|
||||||
|
traktAudioFormat = "dolby_digital_plus";
|
||||||
|
break;
|
||||||
|
case "TrueHD":
|
||||||
|
traktAudioFormat = "dolby_truehd";
|
||||||
|
break;
|
||||||
|
case "EAC3 Atmos":
|
||||||
|
traktAudioFormat = "dolby_digital_plus_atmos";
|
||||||
|
break;
|
||||||
|
case "TrueHD Atmos":
|
||||||
|
traktAudioFormat = "dolby_atmos";
|
||||||
|
break;
|
||||||
|
case "DTS":
|
||||||
|
case "DTS-ES":
|
||||||
|
traktAudioFormat = "dts";
|
||||||
|
break;
|
||||||
|
case "DTS-HD MA":
|
||||||
|
traktAudioFormat = "dts_ma";
|
||||||
|
break;
|
||||||
|
case "DTS-HD HRA":
|
||||||
|
traktAudioFormat = "dts_hr";
|
||||||
|
break;
|
||||||
|
case "DTS-X":
|
||||||
|
traktAudioFormat = "dts_x";
|
||||||
|
break;
|
||||||
|
case "MP3":
|
||||||
|
traktAudioFormat = "mp3";
|
||||||
|
break;
|
||||||
|
case "MP2":
|
||||||
|
traktAudioFormat = "mp2";
|
||||||
|
break;
|
||||||
|
case "Vorbis":
|
||||||
|
traktAudioFormat = "ogg";
|
||||||
|
break;
|
||||||
|
case "WMA":
|
||||||
|
traktAudioFormat = "wma";
|
||||||
|
break;
|
||||||
|
case "AAC":
|
||||||
|
traktAudioFormat = "aac";
|
||||||
|
break;
|
||||||
|
case "PCM":
|
||||||
|
traktAudioFormat = "lpcm";
|
||||||
|
break;
|
||||||
|
case "FLAC":
|
||||||
|
traktAudioFormat = "flac";
|
||||||
|
break;
|
||||||
|
case "Opus":
|
||||||
|
traktAudioFormat = "ogg_opus";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return traktAudioFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapAudioChannels(EpisodeFile episodeFile, string audioFormat)
|
||||||
|
{
|
||||||
|
var audioChannels = episodeFile.MediaInfo != null ? MediaInfoFormatter.FormatAudioChannels(episodeFile.MediaInfo).ToString("0.0") : string.Empty;
|
||||||
|
|
||||||
|
return audioChannels;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using FluentValidation;
|
||||||
|
using NzbDrone.Core.Annotations;
|
||||||
|
using NzbDrone.Core.ThingiProvider;
|
||||||
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
|
{
|
||||||
|
public class TraktSettingsValidator : AbstractValidator<TraktSettings>
|
||||||
|
{
|
||||||
|
public TraktSettingsValidator()
|
||||||
|
{
|
||||||
|
RuleFor(c => c.AccessToken).NotEmpty();
|
||||||
|
RuleFor(c => c.RefreshToken).NotEmpty();
|
||||||
|
RuleFor(c => c.Expires).NotEmpty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TraktSettings : IProviderConfig
|
||||||
|
{
|
||||||
|
private static readonly TraktSettingsValidator Validator = new TraktSettingsValidator();
|
||||||
|
|
||||||
|
public TraktSettings()
|
||||||
|
{
|
||||||
|
SignIn = "startOAuth";
|
||||||
|
}
|
||||||
|
|
||||||
|
[FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string AccessToken { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(1, Label = "Refresh Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string RefreshToken { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(2, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public DateTime Expires { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(3, Label = "Auth User", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||||
|
public string AuthUser { get; set; }
|
||||||
|
|
||||||
|
[FieldDefinition(4, Label = "Authenticate with Trakt", Type = FieldType.OAuth)]
|
||||||
|
public string SignIn { get; set; }
|
||||||
|
|
||||||
|
public NzbDroneValidationResult Validate()
|
||||||
|
{
|
||||||
|
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue