1
0
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:
Mark McDowall 2022-12-10 00:42:46 -08:00 committed by Qstick
parent 2947b244e4
commit 98ddd0386b
8 changed files with 299 additions and 328 deletions

View File

@ -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 =>

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
} }

View File

@ -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;
}
} }
} }

View File

@ -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;
}
}

View File

@ -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);
}
}
} }
} }

View File

@ -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;
}
}
}