< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Plugins.Tmdb.TmdbUtils
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
Line coverage
31%
Covered lines: 18
Uncovered lines: 40
Coverable lines: 58
Total lines: 212
Line coverage: 31%
Branch coverage
39%
Covered branches: 19
Total branches: 48
Branch coverage: 39.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 11/7/2025 - 12:11:55 AM Line coverage: 30.5% (18/59) Branch coverage: 41.3% (19/46) Total lines: 2191/19/2026 - 12:13:54 AM Line coverage: 30% (18/60) Branch coverage: 39.5% (19/48) Total lines: 2201/29/2026 - 12:13:32 AM Line coverage: 30% (18/60) Branch coverage: 38% (19/50) Total lines: 2202/3/2026 - 12:13:02 AM Line coverage: 31% (18/58) Branch coverage: 39.5% (19/48) Total lines: 212 11/7/2025 - 12:11:55 AM Line coverage: 30.5% (18/59) Branch coverage: 41.3% (19/46) Total lines: 2191/19/2026 - 12:13:54 AM Line coverage: 30% (18/60) Branch coverage: 39.5% (19/48) Total lines: 2201/29/2026 - 12:13:32 AM Line coverage: 30% (18/60) Branch coverage: 38% (19/50) Total lines: 2202/3/2026 - 12:13:02 AM Line coverage: 31% (18/58) Branch coverage: 39.5% (19/48) Total lines: 212

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
CleanName(...)100%210%
MapCrewToPersonType(...)0%210140%
IsTrailerType(...)0%2040%
GetImageLanguagesParam(...)0%2040%
NormalizeLanguage(...)66.66%141275%
AdjustImageLanguage(...)91.66%1212100%
BuildParentalRating(...)0%620%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Diagnostics.CodeAnalysis;
 4using System.Text.RegularExpressions;
 5using Jellyfin.Data.Enums;
 6using MediaBrowser.Model.Entities;
 7using TMDbLib.Objects.General;
 8
 9namespace MediaBrowser.Providers.Plugins.Tmdb
 10{
 11    /// <summary>
 12    /// Utilities for the TMDb provider.
 13    /// </summary>
 14    public static partial class TmdbUtils
 15    {
 16        /// <summary>
 17        /// URL of the TMDb instance to use.
 18        /// </summary>
 19        public const string BaseTmdbUrl = "https://www.themoviedb.org/";
 20
 21        /// <summary>
 22        /// Name of the provider.
 23        /// </summary>
 24        public const string ProviderName = "TheMovieDb";
 25
 26        /// <summary>
 27        /// API key to use when performing an API call.
 28        /// </summary>
 29        public const string ApiKey = "4219e299c89411838049ab0dab19ebd5";
 30
 31        /// <summary>
 32        /// The crew types to keep.
 33        /// </summary>
 034        public static readonly string[] WantedCrewTypes =
 035        {
 036            PersonType.Director,
 037            PersonType.Writer,
 038            PersonType.Producer
 039        };
 40
 41        /// <summary>
 42        /// The crew kinds to keep.
 43        /// </summary>
 044        public static readonly PersonKind[] WantedCrewKinds =
 045        {
 046            PersonKind.Director,
 047            PersonKind.Writer,
 048            PersonKind.Producer
 049        };
 50
 51        [GeneratedRegex(@"[\W_-[·]]+")]
 52        private static partial Regex NonWordRegex();
 53
 54        /// <summary>
 55        /// Cleans the name according to TMDb requirements.
 56        /// </summary>
 57        /// <param name="name">The name of the entity.</param>
 58        /// <returns>The cleaned name.</returns>
 59        public static string CleanName(string name)
 60        {
 61            // TMDb expects a space separated list of words make sure that is the case
 062            return NonWordRegex().Replace(name, " ");
 63        }
 64
 65        /// <summary>
 66        /// Maps the TMDb provided roles for crew members to Jellyfin roles.
 67        /// </summary>
 68        /// <param name="crew">Crew member to map against the Jellyfin person types.</param>
 69        /// <returns>The Jellyfin person type.</returns>
 70        public static PersonKind MapCrewToPersonType(Crew crew)
 71        {
 072            if (crew.Department.Equals("directing", StringComparison.OrdinalIgnoreCase)
 073                && crew.Job.Equals("director", StringComparison.OrdinalIgnoreCase))
 74            {
 075                return PersonKind.Director;
 76            }
 77
 078            if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase)
 079                && crew.Job.Equals("producer", StringComparison.OrdinalIgnoreCase))
 80            {
 081                return PersonKind.Producer;
 82            }
 83
 084            if (crew.Department.Equals("writing", StringComparison.OrdinalIgnoreCase)
 085                && (crew.Job.Equals("writer", StringComparison.OrdinalIgnoreCase) || crew.Job.Equals("screenplay", Strin
 86            {
 087                return PersonKind.Writer;
 88            }
 89
 090            return PersonKind.Unknown;
 91        }
 92
 93        /// <summary>
 94        /// Determines whether a video is a trailer.
 95        /// </summary>
 96        /// <param name="video">The TMDb video.</param>
 97        /// <returns>A boolean indicating whether the video is a trailer.</returns>
 98        public static bool IsTrailerType(Video video)
 99        {
 0100            return video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase)
 0101                   && (video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase)
 0102                       || video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase));
 103        }
 104
 105        /// <summary>
 106        /// Normalizes a language string for use with TMDb's include image language parameter.
 107        /// </summary>
 108        /// <param name="preferredLanguage">The preferred language as either a 2 letter code with or without country cod
 109        /// <param name="countryCode">The country code, ISO 3166-1.</param>
 110        /// <returns>The comma separated language string.</returns>
 111        public static string GetImageLanguagesParam(string preferredLanguage, string? countryCode = null)
 112        {
 0113            var languages = new List<string>();
 114
 0115            if (!string.IsNullOrEmpty(preferredLanguage))
 116            {
 0117                preferredLanguage = NormalizeLanguage(preferredLanguage, countryCode);
 118
 0119                languages.Add(preferredLanguage);
 120            }
 121
 0122            languages.Add("null");
 123
 124            // Always add English as fallback language
 0125            if (!string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase))
 126            {
 0127                languages.Add("en");
 128            }
 129
 0130            return string.Join(',', languages);
 131        }
 132
 133        /// <summary>
 134        /// Normalizes a language string for use with TMDb's language parameter.
 135        /// </summary>
 136        /// <param name="language">The language code.</param>
 137        /// <param name="countryCode">The country code.</param>
 138        /// <returns>The normalized language code.</returns>
 139        [return: NotNullIfNotNull(nameof(language))]
 140        public static string? NormalizeLanguage(string? language, string? countryCode = null)
 141        {
 7142            if (string.IsNullOrEmpty(language))
 143            {
 2144                return language;
 145            }
 146
 147            // Handle es-419 (Latin American Spanish) by converting to regional variant
 5148            if (string.Equals(language, "es-419", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(countryCo
 149            {
 0150                language = string.Equals(countryCode, "AR", StringComparison.OrdinalIgnoreCase)
 0151                    ? "es-AR"
 0152                    : "es-MX";
 153            }
 154
 155            // TMDb requires this to be uppercase
 156            // Everything after the hyphen must be written in uppercase due to a way TMDb wrote their API.
 157            // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab
 5158            var parts = language.Split('-');
 159
 5160            if (parts.Length == 2)
 161            {
 162                // TMDb doesn't support Switzerland (de-CH, it-CH or fr-CH) so use the language (de, it or fr) without c
 3163                if (string.Equals(parts[1], "CH", StringComparison.OrdinalIgnoreCase))
 164                {
 1165                    return parts[0];
 166                }
 167
 2168                language = parts[0] + "-" + parts[1].ToUpperInvariant();
 169            }
 170
 4171            return language;
 172        }
 173
 174        /// <summary>
 175        /// Adjusts the image's language code preferring the 5 letter language code eg. en-US.
 176        /// </summary>
 177        /// <param name="imageLanguage">The image's actual language code.</param>
 178        /// <param name="requestLanguage">The requested language code.</param>
 179        /// <returns>The language code.</returns>
 180        public static string AdjustImageLanguage(string imageLanguage, string requestLanguage)
 181        {
 5182            if (!string.IsNullOrEmpty(imageLanguage)
 5183                && !string.IsNullOrEmpty(requestLanguage)
 5184                && requestLanguage.Length > 2
 5185                && imageLanguage.Length == 2
 5186                && requestLanguage.StartsWith(imageLanguage, StringComparison.OrdinalIgnoreCase))
 187            {
 1188                return requestLanguage;
 189            }
 190
 191            // TMDb now returns xx for no language instead of an empty string.
 4192            return string.Equals(imageLanguage, "xx", StringComparison.OrdinalIgnoreCase)
 4193                ? string.Empty
 4194                : imageLanguage;
 195        }
 196
 197        /// <summary>
 198        /// Combines the metadata country code and the parental rating from the API into the value we store in our datab
 199        /// </summary>
 200        /// <param name="countryCode">The ISO 3166-1 country code of the rating country.</param>
 201        /// <param name="ratingValue">The rating value returned by the TMDb API.</param>
 202        /// <returns>The combined parental rating of country code+rating value.</returns>
 203        public static string BuildParentalRating(string countryCode, string ratingValue)
 204        {
 205            // Exclude US because we store US values as TV-14 without the country code.
 0206            var ratingPrefix = string.Equals(countryCode, "US", StringComparison.OrdinalIgnoreCase) ? string.Empty : cou
 0207            var newRating = ratingPrefix + ratingValue;
 208
 0209            return newRating.Replace("DE-", "FSK-", StringComparison.OrdinalIgnoreCase);
 210        }
 211    }
 212}