< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Plugins.Tmdb.TV.TmdbSeriesProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
Line coverage
5%
Covered lines: 4
Uncovered lines: 67
Coverable lines: 71
Total lines: 384
Line coverage: 5.6%
Branch coverage
0%
Covered branches: 0
Total branches: 40
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%210%
get_Order()100%210%
MapTvShowToRemoteSearchResult(...)0%4260%
MapSearchTvToRemoteSearchResult(...)0%2040%
MapTvShowToSeries(...)0%930300%
GetImageResponse(...)100%210%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Globalization;
 4using System.Linq;
 5using System.Net.Http;
 6using System.Threading;
 7using System.Threading.Tasks;
 8using Jellyfin.Data.Enums;
 9using Jellyfin.Extensions;
 10using MediaBrowser.Common.Net;
 11using MediaBrowser.Controller.Entities;
 12using MediaBrowser.Controller.Entities.TV;
 13using MediaBrowser.Controller.Library;
 14using MediaBrowser.Controller.Providers;
 15using MediaBrowser.Model.Entities;
 16using MediaBrowser.Model.Providers;
 17using TMDbLib.Objects.Find;
 18using TMDbLib.Objects.Search;
 19using TMDbLib.Objects.TvShows;
 20
 21namespace MediaBrowser.Providers.Plugins.Tmdb.TV
 22{
 23    /// <summary>
 24    /// TV series provider powered by TheMovieDb.
 25    /// </summary>
 26    public class TmdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
 27    {
 28        private readonly IHttpClientFactory _httpClientFactory;
 29        private readonly ILibraryManager _libraryManager;
 30        private readonly TmdbClientManager _tmdbClientManager;
 31
 32        /// <summary>
 33        /// Initializes a new instance of the <see cref="TmdbSeriesProvider"/> class.
 34        /// </summary>
 35        /// <param name="libraryManager">The <see cref="ILibraryManager"/>.</param>
 36        /// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/>.</param>
 37        /// <param name="tmdbClientManager">The <see cref="TmdbClientManager"/>.</param>
 38        public TmdbSeriesProvider(
 39            ILibraryManager libraryManager,
 40            IHttpClientFactory httpClientFactory,
 41            TmdbClientManager tmdbClientManager)
 42        {
 2243            _libraryManager = libraryManager;
 2244            _httpClientFactory = httpClientFactory;
 2245            _tmdbClientManager = tmdbClientManager;
 2246        }
 47
 48        /// <inheritdoc />
 049        public string Name => TmdbUtils.ProviderName;
 50
 51        /// <inheritdoc />
 052        public int Order => 1;
 53
 54        /// <inheritdoc />
 55        public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken can
 56        {
 57            if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId))
 58            {
 59                var series = await _tmdbClientManager
 60                    .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, 
 61                    .ConfigureAwait(false);
 62
 63                if (series is not null)
 64                {
 65                    var remoteResult = MapTvShowToRemoteSearchResult(series);
 66
 67                    return new[] { remoteResult };
 68                }
 69            }
 70
 71            if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out var imdbId))
 72            {
 73                var findResult = await _tmdbClientManager
 74                    .FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, searchInfo.MetadataLanguage, cancellationTok
 75                    .ConfigureAwait(false);
 76
 77                var tvResults = findResult?.TvResults;
 78                if (tvResults is not null)
 79                {
 80                    var imdbIdResults = new RemoteSearchResult[tvResults.Count];
 81                    for (var i = 0; i < tvResults.Count; i++)
 82                    {
 83                        var remoteResult = MapSearchTvToRemoteSearchResult(tvResults[i]);
 84                        remoteResult.SetProviderId(MetadataProvider.Imdb, imdbId);
 85                        imdbIdResults[i] = remoteResult;
 86                    }
 87
 88                    return imdbIdResults;
 89                }
 90            }
 91
 92            if (searchInfo.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId))
 93            {
 94                var findResult = await _tmdbClientManager
 95                    .FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, searchInfo.MetadataLanguage, cancellationTok
 96                    .ConfigureAwait(false);
 97
 98                var tvResults = findResult?.TvResults;
 99                if (tvResults is not null)
 100                {
 101                    var tvIdResults = new RemoteSearchResult[tvResults.Count];
 102                    for (var i = 0; i < tvResults.Count; i++)
 103                    {
 104                        var remoteResult = MapSearchTvToRemoteSearchResult(tvResults[i]);
 105                        remoteResult.SetProviderId(MetadataProvider.Tvdb, tvdbId);
 106                        tvIdResults[i] = remoteResult;
 107                    }
 108
 109                    return tvIdResults;
 110                }
 111            }
 112
 113            var tvSearchResults = await _tmdbClientManager.SearchSeriesAsync(searchInfo.Name, searchInfo.MetadataLanguag
 114                .ConfigureAwait(false);
 115
 116            var remoteResults = new RemoteSearchResult[tvSearchResults.Count];
 117            for (var i = 0; i < tvSearchResults.Count; i++)
 118            {
 119                remoteResults[i] = MapSearchTvToRemoteSearchResult(tvSearchResults[i]);
 120            }
 121
 122            return remoteResults;
 123        }
 124
 125        private RemoteSearchResult MapTvShowToRemoteSearchResult(TvShow series)
 126        {
 0127            var remoteResult = new RemoteSearchResult
 0128            {
 0129                Name = series.Name ?? series.OriginalName,
 0130                SearchProviderName = Name,
 0131                ImageUrl = _tmdbClientManager.GetPosterUrl(series.PosterPath),
 0132                Overview = series.Overview
 0133            };
 134
 0135            remoteResult.SetProviderId(MetadataProvider.Tmdb, series.Id.ToString(CultureInfo.InvariantCulture));
 0136            if (series.ExternalIds is not null)
 137            {
 0138                remoteResult.TrySetProviderId(MetadataProvider.Imdb, series.ExternalIds.ImdbId);
 139
 0140                remoteResult.TrySetProviderId(MetadataProvider.Tvdb, series.ExternalIds.TvdbId);
 141            }
 142
 0143            remoteResult.PremiereDate = series.FirstAirDate?.ToUniversalTime();
 144
 0145            return remoteResult;
 146        }
 147
 148        private RemoteSearchResult MapSearchTvToRemoteSearchResult(SearchTv series)
 149        {
 0150            var remoteResult = new RemoteSearchResult
 0151            {
 0152                Name = series.Name ?? series.OriginalName,
 0153                SearchProviderName = Name,
 0154                ImageUrl = _tmdbClientManager.GetPosterUrl(series.PosterPath),
 0155                Overview = series.Overview
 0156            };
 157
 0158            remoteResult.SetProviderId(MetadataProvider.Tmdb, series.Id.ToString(CultureInfo.InvariantCulture));
 0159            remoteResult.PremiereDate = series.FirstAirDate?.ToUniversalTime();
 160
 0161            return remoteResult;
 162        }
 163
 164        /// <inheritdoc />
 165        public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
 166        {
 167            var result = new MetadataResult<Series>
 168            {
 169                QueriedById = true
 170            };
 171
 172            var tmdbId = info.GetProviderId(MetadataProvider.Tmdb);
 173
 174            if (string.IsNullOrEmpty(tmdbId) && info.TryGetProviderId(MetadataProvider.Imdb, out var imdbId))
 175            {
 176                var searchResult = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.
 177                if (searchResult?.TvResults.Count > 0)
 178                {
 179                    tmdbId = searchResult.TvResults[0].Id.ToString(CultureInfo.InvariantCulture);
 180                }
 181            }
 182
 183            if (string.IsNullOrEmpty(tmdbId) && info.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId))
 184            {
 185                var searchResult = await _tmdbClientManager.FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, info.
 186                if (searchResult?.TvResults.Count > 0)
 187                {
 188                    tmdbId = searchResult.TvResults[0].Id.ToString(CultureInfo.InvariantCulture);
 189                }
 190            }
 191
 192            if (string.IsNullOrEmpty(tmdbId))
 193            {
 194                result.QueriedById = false;
 195                // ParseName is required here.
 196                // Caller provides the filename with extension stripped and NOT the parsed filename
 197                var parsedName = _libraryManager.ParseName(info.Name);
 198                var cleanedName = TmdbUtils.CleanName(parsedName.Name);
 199                var searchResults = await _tmdbClientManager.SearchSeriesAsync(cleanedName, info.MetadataLanguage, info.
 200
 201                if (searchResults.Count > 0)
 202                {
 203                    tmdbId = searchResults[0].Id.ToString(CultureInfo.InvariantCulture);
 204                }
 205            }
 206
 207            if (!int.TryParse(tmdbId, CultureInfo.InvariantCulture, out int tmdbIdInt))
 208            {
 209                return result;
 210            }
 211
 212            cancellationToken.ThrowIfCancellationRequested();
 213
 214            var tvShow = await _tmdbClientManager
 215                .GetSeriesAsync(tmdbIdInt, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage
 216                .ConfigureAwait(false);
 217
 218            if (tvShow is null)
 219            {
 220                return result;
 221            }
 222
 223            result = new MetadataResult<Series>
 224            {
 225                Item = MapTvShowToSeries(tvShow, info.MetadataCountryCode),
 226                ResultLanguage = info.MetadataLanguage ?? tvShow.OriginalLanguage
 227            };
 228
 229            foreach (var person in GetPersons(tvShow))
 230            {
 231                result.AddPerson(person);
 232            }
 233
 234            result.HasMetadata = result.Item is not null;
 235
 236            return result;
 237        }
 238
 239        private static Series MapTvShowToSeries(TvShow seriesResult, string preferredCountryCode)
 240        {
 0241            var series = new Series
 0242            {
 0243                Name = seriesResult.Name,
 0244                OriginalTitle = seriesResult.OriginalName
 0245            };
 246
 0247            series.SetProviderId(MetadataProvider.Tmdb, seriesResult.Id.ToString(CultureInfo.InvariantCulture));
 248
 0249            series.CommunityRating = Convert.ToSingle(seriesResult.VoteAverage);
 250
 0251            series.Overview = seriesResult.Overview;
 252
 0253            if (seriesResult.Networks is not null)
 254            {
 0255                series.Studios = seriesResult.Networks.Select(i => i.Name).ToArray();
 256            }
 257
 0258            if (seriesResult.Genres is not null)
 259            {
 0260                series.Genres = seriesResult.Genres.Select(i => i.Name).ToArray();
 261            }
 262
 0263            if (seriesResult.Keywords?.Results is not null)
 264            {
 0265                for (var i = 0; i < seriesResult.Keywords.Results.Count; i++)
 266                {
 0267                    series.AddTag(seriesResult.Keywords.Results[i].Name);
 268                }
 269            }
 270
 0271            series.HomePageUrl = seriesResult.Homepage;
 272
 0273            series.RunTimeTicks = seriesResult.EpisodeRunTime.Select(i => TimeSpan.FromMinutes(i).Ticks).FirstOrDefault(
 274
 0275            if (Emby.Naming.TV.TvParserHelpers.TryParseSeriesStatus(seriesResult.Status, out var seriesStatus))
 276            {
 0277                series.Status = seriesStatus;
 278            }
 279
 0280            series.EndDate = seriesResult.LastAirDate;
 0281            series.PremiereDate = seriesResult.FirstAirDate;
 282
 0283            var ids = seriesResult.ExternalIds;
 0284            if (ids is not null)
 285            {
 0286                series.TrySetProviderId(MetadataProvider.Imdb, ids.ImdbId);
 0287                series.TrySetProviderId(MetadataProvider.TvRage, ids.TvrageId);
 0288                series.TrySetProviderId(MetadataProvider.Tvdb, ids.TvdbId);
 289            }
 290
 0291            var contentRatings = seriesResult.ContentRatings.Results ?? new List<ContentRating>();
 292
 0293            var ourRelease = contentRatings.FirstOrDefault(c => string.Equals(c.Iso_3166_1, preferredCountryCode, String
 0294            var usRelease = contentRatings.FirstOrDefault(c => string.Equals(c.Iso_3166_1, "US", StringComparison.Ordina
 0295            var minimumRelease = contentRatings.FirstOrDefault();
 296
 0297            if (ourRelease is not null)
 298            {
 0299                series.OfficialRating = TmdbUtils.BuildParentalRating(ourRelease.Iso_3166_1, ourRelease.Rating);
 300            }
 0301            else if (usRelease is not null)
 302            {
 0303                series.OfficialRating = usRelease.Rating;
 304            }
 0305            else if (minimumRelease is not null)
 306            {
 0307                series.OfficialRating = minimumRelease.Rating;
 308            }
 309
 0310            if (seriesResult.Videos?.Results is not null)
 311            {
 0312                foreach (var video in seriesResult.Videos.Results)
 313                {
 0314                    if (TmdbUtils.IsTrailerType(video))
 315                    {
 0316                        series.AddTrailerUrl("https://www.youtube.com/watch?v=" + video.Key);
 317                    }
 318                }
 319            }
 320
 0321            return series;
 322        }
 323
 324        private IEnumerable<PersonInfo> GetPersons(TvShow seriesResult)
 325        {
 326            if (seriesResult.Credits?.Cast is not null)
 327            {
 328                foreach (var actor in seriesResult.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration
 329                {
 330                    var personInfo = new PersonInfo
 331                    {
 332                        Name = actor.Name.Trim(),
 333                        Role = actor.Character,
 334                        Type = PersonKind.Actor,
 335                        SortOrder = actor.Order,
 336                        ImageUrl = _tmdbClientManager.GetPosterUrl(actor.ProfilePath)
 337                    };
 338
 339                    if (actor.Id > 0)
 340                    {
 341                        personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture))
 342                    }
 343
 344                    yield return personInfo;
 345                }
 346            }
 347
 348            if (seriesResult.Credits?.Crew is not null)
 349            {
 350                var keepTypes = new[]
 351                {
 352                    PersonType.Director,
 353                    PersonType.Writer,
 354                    PersonType.Producer
 355                };
 356
 357                foreach (var person in seriesResult.Credits.Crew)
 358                {
 359                    // Normalize this
 360                    var type = TmdbUtils.MapCrewToPersonType(person);
 361
 362                    if (!TmdbUtils.WantedCrewKinds.Contains(type)
 363                        && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnor
 364                    {
 365                        continue;
 366                    }
 367
 368                    yield return new PersonInfo
 369                    {
 370                        Name = person.Name.Trim(),
 371                        Role = person.Job,
 372                        Type = type
 373                    };
 374                }
 375            }
 376        }
 377
 378        /// <inheritdoc />
 379        public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
 380        {
 0381            return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
 382        }
 383    }
 384}