From 98ddd0386bc018a65873c54d03a2a2daeb04e54e Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sat, 10 Dec 2022 00:42:46 -0800 Subject: [PATCH] Fixed: Trakt connection auth tokens not being refreshed Closes #7873 (cherry picked from commit d09e5d8eb4097cbba1ee0a668dbb27f941cc4f68) --- .../TraktTests/TraktServiceFixture.cs | 23 +- .../Trakt/List/TraktListRequestGenerator.cs | 2 +- .../Popular/TraktPopularRequestGenerator.cs | 2 +- .../Trakt/User/TraktUserRequestGenerator.cs | 2 +- .../Notifications/Trakt/Trakt.cs | 256 +++++++++++++++-- .../Trakt/TraktInterlacedTypes.cs | 20 ++ .../Notifications/Trakt/TraktProxy.cs | 59 ++-- .../Notifications/Trakt/TraktService.cs | 263 ------------------ 8 files changed, 299 insertions(+), 328 deletions(-) create mode 100644 src/NzbDrone.Core/Notifications/Trakt/TraktInterlacedTypes.cs delete mode 100644 src/NzbDrone.Core/Notifications/Trakt/TraktService.cs diff --git a/src/NzbDrone.Core.Test/NotificationTests/TraktTests/TraktServiceFixture.cs b/src/NzbDrone.Core.Test/NotificationTests/TraktTests/TraktServiceFixture.cs index 74f6a2f562..c1ba4cae99 100644 --- a/src/NzbDrone.Core.Test/NotificationTests/TraktTests/TraktServiceFixture.cs +++ b/src/NzbDrone.Core.Test/NotificationTests/TraktTests/TraktServiceFixture.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using Moq; using NUnit.Framework; @@ -13,10 +14,10 @@ namespace NzbDrone.Core.Test.NotificationTests { [TestFixture] - public class TraktServiceFixture : CoreTest + public class TraktServiceFixture : CoreTest { private DownloadMessage _downloadMessage; - private TraktSettings _traktSettings; + private NotificationDefinition _traktDefinition; [SetUp] public void Setup() @@ -34,11 +35,17 @@ public void Setup() } }; - _traktSettings = new TraktSettings + _traktDefinition = new NotificationDefinition { - AccessToken = "", - RefreshToken = "" + Settings = new TraktSettings + { + AccessToken = "", + RefreshToken = "", + Expires = DateTime.Now.AddDays(1) + } }; + + Subject.Definition = _traktDefinition; } private void GiventValidMediaInfo(Quality quality, string audioChannels, string audioFormat, string scanType) @@ -56,7 +63,7 @@ private void GiventValidMediaInfo(Quality quality, string audioChannels, string [Test] public void should_add_collection_movie_if_null_mediainfo() { - Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile); + Subject.OnDownload(_downloadMessage); Mocker.GetMock() .Verify(v => v.AddToCollection(It.IsAny(), It.IsAny()), Times.Once()); @@ -67,7 +74,7 @@ public void should_add_collection_movie_if_valid_mediainfo() { GiventValidMediaInfo(Quality.Bluray1080p, "5.1", "DTS", "Progressive"); - Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile); + Subject.OnDownload(_downloadMessage); Mocker.GetMock() .Verify(v => v.AddToCollection(It.Is(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"); - Subject.AddMovieToCollection(_traktSettings, _downloadMessage.Movie, _downloadMessage.MovieFile); + Subject.OnDownload(_downloadMessage); Mocker.GetMock() .Verify(v => v.AddToCollection(It.Is(t => diff --git a/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs index 42ee40d08f..745a571af6 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs @@ -36,7 +36,7 @@ private IEnumerable GetMoviesRequest() var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim(), true, "-", "-"); 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; } diff --git a/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs index 82f1cf6139..660f42fae3 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs @@ -71,7 +71,7 @@ private IEnumerable GetMoviesRequest() 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; } diff --git a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs index 24017d23ff..f68ef6813d 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs @@ -42,7 +42,7 @@ private IEnumerable GetMoviesRequest() 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; } diff --git a/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs b/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs index f68d6cec2f..91c8785846 100644 --- a/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs +++ b/src/NzbDrone.Core/Notifications/Trakt/Trakt.cs @@ -1,22 +1,28 @@ using System; using System.Collections.Generic; +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; using NzbDrone.Core.Validation; namespace NzbDrone.Core.Notifications.Trakt { public class Trakt : NotificationBase { - private readonly ITraktService _traktService; + private readonly ITraktProxy _proxy; private readonly INotificationRepository _notificationRepository; 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; _logger = logger; } @@ -26,27 +32,53 @@ public Trakt(ITraktService traktService, INotificationRepository notificationRep 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) { - _traktService.RemoveMovieFromCollection(Settings, deleteMessage.Movie); + RefreshTokenIfNecessary(); + RemoveMovieFromCollection(Settings, deleteMessage.Movie); } public override void OnMovieDelete(MovieDeleteMessage deleteMessage) { - if (deleteMessage.DeletedFiles) - { - _traktService.RemoveMovieFromCollection(Settings, deleteMessage.Movie); - } + RefreshTokenIfNecessary(); + RemoveMovieFromCollection(Settings, deleteMessage.Movie); } public override ValidationResult Test() { var failures = new List(); - 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); } @@ -55,7 +87,7 @@ public override object RequestAction(string action, IDictionary { if (action == "startOAuth") { - var request = _traktService.GetOAuthRequest(query["callbackUrl"]); + var request = _proxy.GetOAuthRequest(query["callbackUrl"]); return new { @@ -69,14 +101,22 @@ public override object RequestAction(string action, IDictionary accessToken = query["access_token"], expires = DateTime.UtcNow.AddSeconds(int.Parse(query["expires_in"])), refreshToken = query["refresh_token"], - authUser = _traktService.GetUserName(query["access_token"]) + authUser = _proxy.GetUserName(query["access_token"]) }; } return new { }; } - public void RefreshToken() + private void RefreshTokenIfNecessary() + { + if (Settings.Expires < DateTime.UtcNow.AddMinutes(5)) + { + RefreshToken(); + } + } + + private void RefreshToken() { _logger.Trace("Refreshing Token"); @@ -84,11 +124,12 @@ public void RefreshToken() try { - var response = _traktService.RefreshAuthToken(Settings.RefreshToken); + var response = _proxy.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; @@ -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() + }; + + 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() + }; + + 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; + } } } diff --git a/src/NzbDrone.Core/Notifications/Trakt/TraktInterlacedTypes.cs b/src/NzbDrone.Core/Notifications/Trakt/TraktInterlacedTypes.cs new file mode 100644 index 0000000000..7459a83d9c --- /dev/null +++ b/src/NzbDrone.Core/Notifications/Trakt/TraktInterlacedTypes.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.Notifications.Trakt +{ + public static class TraktInterlacedTypes + { + private static HashSet _interlacedTypes; + + static TraktInterlacedTypes() + { + _interlacedTypes = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Interlaced", "MBAFF", "PAFF" + }; + } + + public static HashSet interlacedTypes => _interlacedTypes; + } +} diff --git a/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs b/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs index 2120eb3415..337c240f77 100644 --- a/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs +++ b/src/NzbDrone.Core/Notifications/Trakt/TraktProxy.cs @@ -14,7 +14,7 @@ public interface ITraktProxy TraktAuthRefreshResource RefreshAuthToken(string refreshToken); void AddToCollection(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 @@ -36,59 +36,30 @@ public TraktProxy(IHttpClient httpClient, Logger logger) 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.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); - } + MakeRequest(request); } 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.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); - } + MakeRequest(request); } 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(request); - try - { - var response = _httpClient.Get(request); - - if (response != null && response.Resource != null) - { - return response.Resource.User.Ids.Slug; - } - } - catch (HttpException) - { - _logger.Warn($"Error refreshing trakt access token"); - } - - return null; + return response?.Resource?.User?.Ids?.Slug; } public HttpRequest GetOAuthRequest(string callbackUrl) @@ -110,7 +81,7 @@ public TraktAuthRefreshResource RefreshAuthToken(string refreshToken) return _httpClient.Get(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(); @@ -127,5 +98,17 @@ public HttpRequest BuildTraktRequest(string resource, HttpMethod method, string return request; } + + private void MakeRequest(HttpRequest request) + { + try + { + _httpClient.Execute(request); + } + catch (HttpException ex) + { + throw new TraktException("Unable to send payload", ex); + } + } } } diff --git a/src/NzbDrone.Core/Notifications/Trakt/TraktService.cs b/src/NzbDrone.Core/Notifications/Trakt/TraktService.cs deleted file mode 100644 index 339d0cf929..0000000000 --- a/src/NzbDrone.Core/Notifications/Trakt/TraktService.cs +++ /dev/null @@ -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() - }; - - 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() - }; - - 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; - } - } -}