< 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
30%
Covered lines: 18
Uncovered lines: 42
Coverable lines: 60
Total lines: 220
Line coverage: 30%
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 10/17/2025 - 12:10:14 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: 220 10/17/2025 - 12:10:14 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: 220

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
CleanName(...)100%210%
MapCrewToPersonType(...)0%156120%
IsTrailerType(...)0%2040%
GetImageLanguagesParam(...)0%4260%
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("production", 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))
 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
 0121                if (preferredLanguage.Length == 5) // Like en-US
 122                {
 123                    // Currently, TMDb supports 2-letter language codes only.
 124                    // They are planning to change this in the future, thus we're
 125                    // supplying both codes if we're having a 5-letter code.
 0126                    languages.Add(preferredLanguage.Substring(0, 2));
 127                }
 128            }
 129
 0130            languages.Add("null");
 131
 132            // Always add English as fallback language
 0133            if (!string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase))
 134            {
 0135                languages.Add("en");
 136            }
 137
 0138            return string.Join(',', languages);
 139        }
 140
 141        /// <summary>
 142        /// Normalizes a language string for use with TMDb's language parameter.
 143        /// </summary>
 144        /// <param name="language">The language code.</param>
 145        /// <param name="countryCode">The country code.</param>
 146        /// <returns>The normalized language code.</returns>
 147        [return: NotNullIfNotNull(nameof(language))]
 148        public static string? NormalizeLanguage(string? language, string? countryCode = null)
 149        {
 7150            if (string.IsNullOrEmpty(language))
 151            {
 2152                return language;
 153            }
 154
 155            // Handle es-419 (Latin American Spanish) by converting to regional variant
 5156            if (string.Equals(language, "es-419", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(countryCo
 157            {
 0158                language = string.Equals(countryCode, "AR", StringComparison.OrdinalIgnoreCase)
 0159                    ? "es-AR"
 0160                    : "es-MX";
 161            }
 162
 163            // TMDb requires this to be uppercase
 164            // Everything after the hyphen must be written in uppercase due to a way TMDb wrote their API.
 165            // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab
 5166            var parts = language.Split('-');
 167
 5168            if (parts.Length == 2)
 169            {
 170                // TMDb doesn't support Switzerland (de-CH, it-CH or fr-CH) so use the language (de, it or fr) without c
 3171                if (string.Equals(parts[1], "CH", StringComparison.OrdinalIgnoreCase))
 172                {
 1173                    return parts[0];
 174                }
 175
 2176                language = parts[0] + "-" + parts[1].ToUpperInvariant();
 177            }
 178
 4179            return language;
 180        }
 181
 182        /// <summary>
 183        /// Adjusts the image's language code preferring the 5 letter language code eg. en-US.
 184        /// </summary>
 185        /// <param name="imageLanguage">The image's actual language code.</param>
 186        /// <param name="requestLanguage">The requested language code.</param>
 187        /// <returns>The language code.</returns>
 188        public static string AdjustImageLanguage(string imageLanguage, string requestLanguage)
 189        {
 5190            if (!string.IsNullOrEmpty(imageLanguage)
 5191                && !string.IsNullOrEmpty(requestLanguage)
 5192                && requestLanguage.Length > 2
 5193                && imageLanguage.Length == 2
 5194                && requestLanguage.StartsWith(imageLanguage, StringComparison.OrdinalIgnoreCase))
 195            {
 1196                return requestLanguage;
 197            }
 198
 199            // TMDb now returns xx for no language instead of an empty string.
 4200            return string.Equals(imageLanguage, "xx", StringComparison.OrdinalIgnoreCase)
 4201                ? string.Empty
 4202                : imageLanguage;
 203        }
 204
 205        /// <summary>
 206        /// Combines the metadata country code and the parental rating from the API into the value we store in our datab
 207        /// </summary>
 208        /// <param name="countryCode">The ISO 3166-1 country code of the rating country.</param>
 209        /// <param name="ratingValue">The rating value returned by the TMDb API.</param>
 210        /// <returns>The combined parental rating of country code+rating value.</returns>
 211        public static string BuildParentalRating(string countryCode, string ratingValue)
 212        {
 213            // Exclude US because we store US values as TV-14 without the country code.
 0214            var ratingPrefix = string.Equals(countryCode, "US", StringComparison.OrdinalIgnoreCase) ? string.Empty : cou
 0215            var newRating = ratingPrefix + ratingValue;
 216
 0217            return newRating.Replace("DE-", "FSK-", StringComparison.OrdinalIgnoreCase);
 218        }
 219    }
 220}