mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-20 18:02:44 +01:00
Fixed: Trakt connection auth tokens not being refreshed
Closes #7873 (cherry picked from commit d09e5d8eb4097cbba1ee0a668dbb27f941cc4f68)
This commit is contained in:
parent
2947b244e4
commit
98ddd0386b
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -13,10 +14,10 @@
|
|||||||
namespace NzbDrone.Core.Test.NotificationTests
|
namespace NzbDrone.Core.Test.NotificationTests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class TraktServiceFixture : CoreTest<TraktService>
|
public class TraktServiceFixture : CoreTest<Trakt>
|
||||||
{
|
{
|
||||||
private DownloadMessage _downloadMessage;
|
private DownloadMessage _downloadMessage;
|
||||||
private TraktSettings _traktSettings;
|
private NotificationDefinition _traktDefinition;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
@ -34,11 +35,17 @@ public void Setup()
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_traktSettings = new TraktSettings
|
_traktDefinition = new NotificationDefinition
|
||||||
{
|
{
|
||||||
AccessToken = "",
|
Settings = new TraktSettings
|
||||||
RefreshToken = ""
|
{
|
||||||
|
AccessToken = "",
|
||||||
|
RefreshToken = "",
|
||||||
|
Expires = DateTime.Now.AddDays(1)
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Subject.Definition = _traktDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GiventValidMediaInfo(Quality quality, string audioChannels, string audioFormat, string scanType)
|
private void GiventValidMediaInfo(Quality quality, string audioChannels, string audioFormat, string scanType)
|
||||||
@ -56,7 +63,7 @@ private void GiventValidMediaInfo(Quality quality, string audioChannels, string
|
|||||||
[Test]
|
[Test]
|
||||||
public void should_add_collection_movie_if_null_mediainfo()
|
public void should_add_collection_movie_if_null_mediainfo()
|
||||||
{
|
{
|
||||||
Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile);
|
Subject.OnDownload(_downloadMessage);
|
||||||
|
|
||||||
Mocker.GetMock<ITraktProxy>()
|
Mocker.GetMock<ITraktProxy>()
|
||||||
.Verify(v => v.AddToCollection(It.IsAny<TraktCollectMoviesResource>(), It.IsAny<string>()), Times.Once());
|
.Verify(v => v.AddToCollection(It.IsAny<TraktCollectMoviesResource>(), It.IsAny<string>()), Times.Once());
|
||||||
@ -67,7 +74,7 @@ public void should_add_collection_movie_if_valid_mediainfo()
|
|||||||
{
|
{
|
||||||
GiventValidMediaInfo(Quality.Bluray1080p, "5.1", "DTS", "Progressive");
|
GiventValidMediaInfo(Quality.Bluray1080p, "5.1", "DTS", "Progressive");
|
||||||
|
|
||||||
Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile);
|
Subject.OnDownload(_downloadMessage);
|
||||||
|
|
||||||
Mocker.GetMock<ITraktProxy>()
|
Mocker.GetMock<ITraktProxy>()
|
||||||
.Verify(v => v.AddToCollection(It.Is<TraktCollectMoviesResource>(t =>
|
.Verify(v => v.AddToCollection(It.Is<TraktCollectMoviesResource>(t =>
|
||||||
@ -83,7 +90,7 @@ public void should_format_audio_channels_to_one_decimal_when_adding_collection_m
|
|||||||
{
|
{
|
||||||
GiventValidMediaInfo(Quality.Bluray1080p, "2.0", "DTS", "Progressive");
|
GiventValidMediaInfo(Quality.Bluray1080p, "2.0", "DTS", "Progressive");
|
||||||
|
|
||||||
Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile);
|
Subject.OnDownload(_downloadMessage);
|
||||||
|
|
||||||
Mocker.GetMock<ITraktProxy>()
|
Mocker.GetMock<ITraktProxy>()
|
||||||
.Verify(v => v.AddToCollection(It.Is<TraktCollectMoviesResource>(t =>
|
.Verify(v => v.AddToCollection(It.Is<TraktCollectMoviesResource>(t =>
|
||||||
|
@ -36,7 +36,7 @@ private IEnumerable<ImportListRequest> GetMoviesRequest()
|
|||||||
var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim(), true, "-", "-");
|
var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim(), true, "-", "-");
|
||||||
link += $"users/{Settings.Username.Trim()}/lists/{listName}/items/movies?limit={Settings.Limit}";
|
link += $"users/{Settings.Username.Trim()}/lists/{listName}/items/movies?limit={Settings.Limit}";
|
||||||
|
|
||||||
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
|
var request = new ImportListRequest(_traktProxy.BuildRequest(link, HttpMethod.Get, Settings.AccessToken));
|
||||||
|
|
||||||
yield return request;
|
yield return request;
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ private IEnumerable<ImportListRequest> GetMoviesRequest()
|
|||||||
|
|
||||||
link += filtersAndLimit;
|
link += filtersAndLimit;
|
||||||
|
|
||||||
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
|
var request = new ImportListRequest(_traktProxy.BuildRequest(link, HttpMethod.Get, Settings.AccessToken));
|
||||||
|
|
||||||
yield return request;
|
yield return request;
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ private IEnumerable<ImportListRequest> GetMoviesRequest()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var request = new ImportListRequest(_traktProxy.BuildTraktRequest(link, HttpMethod.Get, Settings.AccessToken));
|
var request = new ImportListRequest(_traktProxy.BuildRequest(link, HttpMethod.Get, Settings.AccessToken));
|
||||||
|
|
||||||
yield return request;
|
yield return request;
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,28 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
using FluentValidation.Results;
|
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.MediaFiles;
|
||||||
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.Notifications.Trakt.Resource;
|
||||||
|
using NzbDrone.Core.Qualities;
|
||||||
using NzbDrone.Core.Validation;
|
using NzbDrone.Core.Validation;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Trakt
|
namespace NzbDrone.Core.Notifications.Trakt
|
||||||
{
|
{
|
||||||
public class Trakt : NotificationBase<TraktSettings>
|
public class Trakt : NotificationBase<TraktSettings>
|
||||||
{
|
{
|
||||||
private readonly ITraktService _traktService;
|
private readonly ITraktProxy _proxy;
|
||||||
private readonly INotificationRepository _notificationRepository;
|
private readonly INotificationRepository _notificationRepository;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public Trakt(ITraktService traktService, INotificationRepository notificationRepository, Logger logger)
|
public Trakt(ITraktProxy proxy, INotificationRepository notificationRepository, Logger logger)
|
||||||
{
|
{
|
||||||
_traktService = traktService;
|
_proxy = proxy;
|
||||||
_notificationRepository = notificationRepository;
|
_notificationRepository = notificationRepository;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@ -26,27 +32,53 @@ public Trakt(ITraktService traktService, INotificationRepository notificationRep
|
|||||||
|
|
||||||
public override void OnDownload(DownloadMessage message)
|
public override void OnDownload(DownloadMessage message)
|
||||||
{
|
{
|
||||||
_traktService.AddMovieToCollection(Settings, message.Movie, message.MovieFile);
|
RefreshTokenIfNecessary();
|
||||||
|
AddMovieToCollection(Settings, message.Movie, message.MovieFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnMovieFileDelete(MovieFileDeleteMessage deleteMessage)
|
public override void OnMovieFileDelete(MovieFileDeleteMessage deleteMessage)
|
||||||
{
|
{
|
||||||
_traktService.RemoveMovieFromCollection(Settings, deleteMessage.Movie);
|
RefreshTokenIfNecessary();
|
||||||
|
RemoveMovieFromCollection(Settings, deleteMessage.Movie);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnMovieDelete(MovieDeleteMessage deleteMessage)
|
public override void OnMovieDelete(MovieDeleteMessage deleteMessage)
|
||||||
{
|
{
|
||||||
if (deleteMessage.DeletedFiles)
|
RefreshTokenIfNecessary();
|
||||||
{
|
RemoveMovieFromCollection(Settings, deleteMessage.Movie);
|
||||||
_traktService.RemoveMovieFromCollection(Settings, deleteMessage.Movie);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ValidationResult Test()
|
public override ValidationResult Test()
|
||||||
{
|
{
|
||||||
var failures = new List<ValidationFailure>();
|
var failures = new List<ValidationFailure>();
|
||||||
|
|
||||||
failures.AddIfNotNull(_traktService.Test(Settings));
|
RefreshTokenIfNecessary();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_proxy.GetUserName(Settings.AccessToken);
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
if (ex.Response.StatusCode == HttpStatusCode.Unauthorized)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Access Token is invalid: " + ex.Message);
|
||||||
|
|
||||||
|
failures.Add(new ValidationFailure("Token", "Access Token is invalid"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to send test message: " + ex.Message);
|
||||||
|
|
||||||
|
failures.Add(new ValidationFailure("Token", "Unable to send test message"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Error(ex, "Unable to send test message: " + ex.Message);
|
||||||
|
|
||||||
|
failures.Add(new ValidationFailure("", "Unable to send test message"));
|
||||||
|
}
|
||||||
|
|
||||||
return new ValidationResult(failures);
|
return new ValidationResult(failures);
|
||||||
}
|
}
|
||||||
@ -55,7 +87,7 @@ public override object RequestAction(string action, IDictionary<string, string>
|
|||||||
{
|
{
|
||||||
if (action == "startOAuth")
|
if (action == "startOAuth")
|
||||||
{
|
{
|
||||||
var request = _traktService.GetOAuthRequest(query["callbackUrl"]);
|
var request = _proxy.GetOAuthRequest(query["callbackUrl"]);
|
||||||
|
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
@ -69,14 +101,22 @@ public override object RequestAction(string action, IDictionary<string, string>
|
|||||||
accessToken = query["access_token"],
|
accessToken = query["access_token"],
|
||||||
expires = DateTime.UtcNow.AddSeconds(int.Parse(query["expires_in"])),
|
expires = DateTime.UtcNow.AddSeconds(int.Parse(query["expires_in"])),
|
||||||
refreshToken = query["refresh_token"],
|
refreshToken = query["refresh_token"],
|
||||||
authUser = _traktService.GetUserName(query["access_token"])
|
authUser = _proxy.GetUserName(query["access_token"])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new { };
|
return new { };
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshToken()
|
private void RefreshTokenIfNecessary()
|
||||||
|
{
|
||||||
|
if (Settings.Expires < DateTime.UtcNow.AddMinutes(5))
|
||||||
|
{
|
||||||
|
RefreshToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshToken()
|
||||||
{
|
{
|
||||||
_logger.Trace("Refreshing Token");
|
_logger.Trace("Refreshing Token");
|
||||||
|
|
||||||
@ -84,11 +124,12 @@ public void RefreshToken()
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = _traktService.RefreshAuthToken(Settings.RefreshToken);
|
var response = _proxy.RefreshAuthToken(Settings.RefreshToken);
|
||||||
|
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
var token = response;
|
var token = response;
|
||||||
|
|
||||||
Settings.AccessToken = token.AccessToken;
|
Settings.AccessToken = token.AccessToken;
|
||||||
Settings.Expires = DateTime.UtcNow.AddSeconds(token.ExpiresIn);
|
Settings.Expires = DateTime.UtcNow.AddSeconds(token.ExpiresIn);
|
||||||
Settings.RefreshToken = token.RefreshToken ?? Settings.RefreshToken;
|
Settings.RefreshToken = token.RefreshToken ?? Settings.RefreshToken;
|
||||||
@ -99,10 +140,193 @@ public void RefreshToken()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (HttpException)
|
catch (HttpException ex)
|
||||||
{
|
{
|
||||||
_logger.Warn($"Error refreshing trakt access token");
|
_logger.Warn(ex, "Error refreshing trakt access token");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AddMovieToCollection(TraktSettings settings, Movie movie, MovieFile movieFile)
|
||||||
|
{
|
||||||
|
var payload = new TraktCollectMoviesResource
|
||||||
|
{
|
||||||
|
Movies = new List<TraktCollectMovie>()
|
||||||
|
};
|
||||||
|
|
||||||
|
var traktResolution = MapResolution(movieFile.Quality.Quality.Resolution, movieFile.MediaInfo?.ScanType);
|
||||||
|
var mediaType = MapMediaType(movieFile.Quality.Quality.Source);
|
||||||
|
var audio = MapAudio(movieFile);
|
||||||
|
var audioChannels = MapAudioChannels(movieFile);
|
||||||
|
|
||||||
|
payload.Movies.Add(new TraktCollectMovie
|
||||||
|
{
|
||||||
|
Title = movie.Title,
|
||||||
|
Year = movie.Year,
|
||||||
|
CollectedAt = DateTime.Now,
|
||||||
|
Resolution = traktResolution,
|
||||||
|
MediaType = mediaType,
|
||||||
|
AudioChannels = audioChannels,
|
||||||
|
Audio = audio,
|
||||||
|
Ids = new TraktMovieIdsResource
|
||||||
|
{
|
||||||
|
Tmdb = movie.MovieMetadata.Value.TmdbId,
|
||||||
|
Imdb = movie.MovieMetadata.Value.ImdbId ?? "",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_proxy.AddToCollection(payload, settings.AccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveMovieFromCollection(TraktSettings settings, Movie movie)
|
||||||
|
{
|
||||||
|
var payload = new TraktCollectMoviesResource
|
||||||
|
{
|
||||||
|
Movies = new List<TraktCollectMovie>()
|
||||||
|
};
|
||||||
|
|
||||||
|
payload.Movies.Add(new TraktCollectMovie
|
||||||
|
{
|
||||||
|
Title = movie.Title,
|
||||||
|
Year = movie.Year,
|
||||||
|
Ids = new TraktMovieIdsResource
|
||||||
|
{
|
||||||
|
Tmdb = movie.MovieMetadata.Value.TmdbId,
|
||||||
|
Imdb = movie.MovieMetadata.Value.ImdbId ?? "",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_proxy.RemoveFromCollection(payload, settings.AccessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapMediaType(Source source)
|
||||||
|
{
|
||||||
|
var traktSource = string.Empty;
|
||||||
|
|
||||||
|
switch (source)
|
||||||
|
{
|
||||||
|
case Source.BLURAY:
|
||||||
|
traktSource = "bluray";
|
||||||
|
break;
|
||||||
|
case Source.WEBDL:
|
||||||
|
traktSource = "digital";
|
||||||
|
break;
|
||||||
|
case Source.WEBRIP:
|
||||||
|
traktSource = "digital";
|
||||||
|
break;
|
||||||
|
case Source.DVD:
|
||||||
|
traktSource = "dvd";
|
||||||
|
break;
|
||||||
|
case Source.TV:
|
||||||
|
traktSource = "dvd";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return traktSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapResolution(int resolution, string scanType)
|
||||||
|
{
|
||||||
|
var traktResolution = string.Empty;
|
||||||
|
|
||||||
|
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(MovieFile movieFile)
|
||||||
|
{
|
||||||
|
var traktAudioFormat = string.Empty;
|
||||||
|
|
||||||
|
var audioCodec = movieFile.MediaInfo != null ? MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, movieFile.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(MovieFile movieFile)
|
||||||
|
{
|
||||||
|
var audioChannels = movieFile.MediaInfo != null ? MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo).ToString("0.0") : string.Empty;
|
||||||
|
|
||||||
|
if (audioChannels == "0.0")
|
||||||
|
{
|
||||||
|
audioChannels = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioChannels;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ public interface ITraktProxy
|
|||||||
TraktAuthRefreshResource RefreshAuthToken(string refreshToken);
|
TraktAuthRefreshResource RefreshAuthToken(string refreshToken);
|
||||||
void AddToCollection(TraktCollectMoviesResource payload, string accessToken);
|
void AddToCollection(TraktCollectMoviesResource payload, string accessToken);
|
||||||
void RemoveFromCollection(TraktCollectMoviesResource payload, string accessToken);
|
void RemoveFromCollection(TraktCollectMoviesResource payload, string accessToken);
|
||||||
HttpRequest BuildTraktRequest(string resource, HttpMethod method, string accessToken);
|
HttpRequest BuildRequest(string resource, HttpMethod method, string accessToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TraktProxy : ITraktProxy
|
public class TraktProxy : ITraktProxy
|
||||||
@ -36,59 +36,30 @@ public TraktProxy(IHttpClient httpClient, Logger logger)
|
|||||||
|
|
||||||
public void AddToCollection(TraktCollectMoviesResource payload, string accessToken)
|
public void AddToCollection(TraktCollectMoviesResource payload, string accessToken)
|
||||||
{
|
{
|
||||||
var request = BuildTraktRequest("sync/collection", HttpMethod.Post, accessToken);
|
var request = BuildRequest("sync/collection", HttpMethod.Post, accessToken);
|
||||||
|
|
||||||
request.Headers.ContentType = "application/json";
|
request.Headers.ContentType = "application/json";
|
||||||
request.SetContent(payload.ToJson());
|
request.SetContent(payload.ToJson());
|
||||||
|
|
||||||
try
|
MakeRequest(request);
|
||||||
{
|
|
||||||
_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(TraktCollectMoviesResource payload, string accessToken)
|
public void RemoveFromCollection(TraktCollectMoviesResource payload, string accessToken)
|
||||||
{
|
{
|
||||||
var request = BuildTraktRequest("sync/collection/remove", HttpMethod.Post, accessToken);
|
var request = BuildRequest("sync/collection/remove", HttpMethod.Post, accessToken);
|
||||||
|
|
||||||
request.Headers.ContentType = "application/json";
|
request.Headers.ContentType = "application/json";
|
||||||
request.SetContent(payload.ToJson());
|
request.SetContent(payload.ToJson());
|
||||||
|
|
||||||
try
|
MakeRequest(request);
|
||||||
{
|
|
||||||
_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)
|
public string GetUserName(string accessToken)
|
||||||
{
|
{
|
||||||
var request = BuildTraktRequest("users/settings", HttpMethod.Get, accessToken);
|
var request = BuildRequest("users/settings", HttpMethod.Get, accessToken);
|
||||||
|
var response = _httpClient.Get<TraktUserSettingsResource>(request);
|
||||||
|
|
||||||
try
|
return response?.Resource?.User?.Ids?.Slug;
|
||||||
{
|
|
||||||
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)
|
public HttpRequest GetOAuthRequest(string callbackUrl)
|
||||||
@ -110,7 +81,7 @@ public TraktAuthRefreshResource RefreshAuthToken(string refreshToken)
|
|||||||
return _httpClient.Get<TraktAuthRefreshResource>(request)?.Resource ?? null;
|
return _httpClient.Get<TraktAuthRefreshResource>(request)?.Resource ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpRequest BuildTraktRequest(string resource, HttpMethod method, string accessToken)
|
public HttpRequest BuildRequest(string resource, HttpMethod method, string accessToken)
|
||||||
{
|
{
|
||||||
var request = new HttpRequestBuilder(URL).Resource(resource).Build();
|
var request = new HttpRequestBuilder(URL).Resource(resource).Build();
|
||||||
|
|
||||||
@ -127,5 +98,17 @@ public HttpRequest BuildTraktRequest(string resource, HttpMethod method, string
|
|||||||
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MakeRequest(HttpRequest request)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.Execute(request);
|
||||||
|
}
|
||||||
|
catch (HttpException ex)
|
||||||
|
{
|
||||||
|
throw new TraktException("Unable to send payload", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,263 +0,0 @@
|
|||||||
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.Movies;
|
|
||||||
using NzbDrone.Core.Notifications.Trakt.Resource;
|
|
||||||
using NzbDrone.Core.Qualities;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Notifications.Trakt
|
|
||||||
{
|
|
||||||
public interface ITraktService
|
|
||||||
{
|
|
||||||
HttpRequest GetOAuthRequest(string callbackUrl);
|
|
||||||
TraktAuthRefreshResource RefreshAuthToken(string refreshToken);
|
|
||||||
void AddMovieToCollection(TraktSettings settings, Movie movie, MovieFile movieFile);
|
|
||||||
void RemoveMovieFromCollection(TraktSettings settings, Movie movie);
|
|
||||||
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 RemoveMovieFromCollection(TraktSettings settings, Movie movie)
|
|
||||||
{
|
|
||||||
var payload = new TraktCollectMoviesResource
|
|
||||||
{
|
|
||||||
Movies = new List<TraktCollectMovie>()
|
|
||||||
};
|
|
||||||
|
|
||||||
payload.Movies.Add(new TraktCollectMovie
|
|
||||||
{
|
|
||||||
Title = movie.Title,
|
|
||||||
Year = movie.Year,
|
|
||||||
Ids = new TraktMovieIdsResource
|
|
||||||
{
|
|
||||||
Tmdb = movie.MovieMetadata.Value.TmdbId,
|
|
||||||
Imdb = movie.MovieMetadata.Value.ImdbId ?? "",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_proxy.RemoveFromCollection(payload, settings.AccessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddMovieToCollection(TraktSettings settings, Movie movie, MovieFile movieFile)
|
|
||||||
{
|
|
||||||
var payload = new TraktCollectMoviesResource
|
|
||||||
{
|
|
||||||
Movies = new List<TraktCollectMovie>()
|
|
||||||
};
|
|
||||||
|
|
||||||
var traktResolution = MapResolution(movieFile.Quality.Quality.Resolution, movieFile.MediaInfo?.ScanType);
|
|
||||||
var mediaType = MapMediaType(movieFile.Quality.Quality.Source);
|
|
||||||
var audio = MapAudio(movieFile);
|
|
||||||
var audioChannels = MapAudioChannels(movieFile, audio);
|
|
||||||
|
|
||||||
payload.Movies.Add(new TraktCollectMovie
|
|
||||||
{
|
|
||||||
Title = movie.Title,
|
|
||||||
Year = movie.Year,
|
|
||||||
CollectedAt = DateTime.Now,
|
|
||||||
Resolution = traktResolution,
|
|
||||||
MediaType = mediaType,
|
|
||||||
AudioChannels = audioChannels,
|
|
||||||
Audio = audio,
|
|
||||||
Ids = new TraktMovieIdsResource
|
|
||||||
{
|
|
||||||
Tmdb = movie.MovieMetadata.Value.TmdbId,
|
|
||||||
Imdb = movie.MovieMetadata.Value.ImdbId ?? "",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_proxy.AddToCollection(payload, settings.AccessToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string MapMediaType(Source source)
|
|
||||||
{
|
|
||||||
var traktSource = string.Empty;
|
|
||||||
|
|
||||||
switch (source)
|
|
||||||
{
|
|
||||||
case Source.BLURAY:
|
|
||||||
traktSource = "bluray";
|
|
||||||
break;
|
|
||||||
case Source.WEBDL:
|
|
||||||
traktSource = "digital";
|
|
||||||
break;
|
|
||||||
case Source.WEBRIP:
|
|
||||||
traktSource = "digital";
|
|
||||||
break;
|
|
||||||
case Source.DVD:
|
|
||||||
traktSource = "dvd";
|
|
||||||
break;
|
|
||||||
case Source.TV:
|
|
||||||
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() && interlacedTypes.Contains(scanType) ? "i" : "p";
|
|
||||||
|
|
||||||
switch (resolution)
|
|
||||||
{
|
|
||||||
case 2160:
|
|
||||||
traktResolution = "uhd_4k";
|
|
||||||
break;
|
|
||||||
case 1080:
|
|
||||||
traktResolution = string.Format("hd_1080{0}", scanIdentifier);
|
|
||||||
break;
|
|
||||||
case 720:
|
|
||||||
traktResolution = "hd_720p";
|
|
||||||
break;
|
|
||||||
case 576:
|
|
||||||
traktResolution = string.Format("sd_576{0}", scanIdentifier);
|
|
||||||
break;
|
|
||||||
case 480:
|
|
||||||
traktResolution = string.Format("sd_480{0}", scanIdentifier);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return traktResolution;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string MapAudio(MovieFile movieFile)
|
|
||||||
{
|
|
||||||
var traktAudioFormat = string.Empty;
|
|
||||||
|
|
||||||
var audioCodec = movieFile.MediaInfo != null ? MediaInfoFormatter.FormatAudioCodec(movieFile.MediaInfo, movieFile.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(MovieFile movieFile, string audioFormat)
|
|
||||||
{
|
|
||||||
var audioChannels = movieFile.MediaInfo != null ? MediaInfoFormatter.FormatAudioChannels(movieFile.MediaInfo).ToString("0.0") : string.Empty;
|
|
||||||
|
|
||||||
if (audioChannels == "0.0")
|
|
||||||
{
|
|
||||||
audioChannels = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
return audioChannels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user