< 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: 41
Coverable lines: 59
Total lines: 219
Line coverage: 30.5%
Branch coverage
41%
Covered branches: 19
Total branches: 46
Branch coverage: 41.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 7/15/2025 - 12:11:28 AM Line coverage: 28.3% (15/53) Branch coverage: 42.1% (16/38) Total lines: 20610/5/2025 - 12:11:27 AM Line coverage: 30.9% (17/55) Branch coverage: 42.5% (17/40) Total lines: 20910/14/2025 - 12:11:23 AM Line coverage: 30.5% (18/59) Branch coverage: 41.3% (19/46) Total lines: 219 7/15/2025 - 12:11:28 AM Line coverage: 28.3% (15/53) Branch coverage: 42.1% (16/38) Total lines: 20610/5/2025 - 12:11:27 AM Line coverage: 30.9% (17/55) Branch coverage: 42.5% (17/40) Total lines: 20910/14/2025 - 12:11:23 AM Line coverage: 30.5% (18/59) Branch coverage: 41.3% (19/46) Total lines: 219

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%210%
CleanName(...)100%210%
MapCrewToPersonType(...)0%110100%
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.Contains("director", StringComparison.OrdinalIgnoreCase))
 74            {
 075                return PersonKind.Director;
 76            }
 77
 078            if (crew.Department.Equals("production", StringComparison.OrdinalIgnoreCase)
 079                && crew.Job.Contains("producer", StringComparison.OrdinalIgnoreCase))
 80            {
 081                return PersonKind.Producer;
 82            }
 83
 084            if (crew.Department.Equals("writing", StringComparison.OrdinalIgnoreCase))
 85            {
 086                return PersonKind.Writer;
 87            }
 88
 089            return PersonKind.Unknown;
 90        }
 91
 92        /// <summary>
 93        /// Determines whether a video is a trailer.
 94        /// </summary>
 95        /// <param name="video">The TMDb video.</param>
 96        /// <returns>A boolean indicating whether the video is a trailer.</returns>
 97        public static bool IsTrailerType(Video video)
 98        {
 099            return video.Site.Equals("youtube", StringComparison.OrdinalIgnoreCase)
 0100                   && (video.Type.Equals("trailer", StringComparison.OrdinalIgnoreCase)
 0101                       || video.Type.Equals("teaser", StringComparison.OrdinalIgnoreCase));
 102        }
 103
 104        /// <summary>
 105        /// Normalizes a language string for use with TMDb's include image language parameter.
 106        /// </summary>
 107        /// <param name="preferredLanguage">The preferred language as either a 2 letter code with or without country cod
 108        /// <param name="countryCode">The country code, ISO 3166-1.</param>
 109        /// <returns>The comma separated language string.</returns>
 110        public static string GetImageLanguagesParam(string preferredLanguage, string? countryCode = null)
 111        {
 0112            var languages = new List<string>();
 113
 0114            if (!string.IsNullOrEmpty(preferredLanguage))
 115            {
 0116                preferredLanguage = NormalizeLanguage(preferredLanguage, countryCode);
 117
 0118                languages.Add(preferredLanguage);
 119
 0120                if (preferredLanguage.Length == 5) // Like en-US
 121                {
 122                    // Currently, TMDb supports 2-letter language codes only.
 123                    // They are planning to change this in the future, thus we're
 124                    // supplying both codes if we're having a 5-letter code.
 0125                    languages.Add(preferredLanguage.Substring(0, 2));
 126                }
 127            }
 128
 0129            languages.Add("null");
 130
 131            // Always add English as fallback language
 0132            if (!string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase))
 133            {
 0134                languages.Add("en");
 135            }
 136
 0137            return string.Join(',', languages);
 138        }
 139
 140        /// <summary>
 141        /// Normalizes a language string for use with TMDb's language parameter.
 142        /// </summary>
 143        /// <param name="language">The language code.</param>
 144        /// <param name="countryCode">The country code.</param>
 145        /// <returns>The normalized language code.</returns>
 146        [return: NotNullIfNotNull(nameof(language))]
 147        public static string? NormalizeLanguage(string? language, string? countryCode = null)
 148        {
 7149            if (string.IsNullOrEmpty(language))
 150            {
 2151                return language;
 152            }
 153
 154            // Handle es-419 (Latin American Spanish) by converting to regional variant
 5155            if (string.Equals(language, "es-419", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(countryCo
 156            {
 0157                language = string.Equals(countryCode, "AR", StringComparison.OrdinalIgnoreCase)
 0158                    ? "es-AR"
 0159                    : "es-MX";
 160            }
 161
 162            // TMDb requires this to be uppercase
 163            // Everything after the hyphen must be written in uppercase due to a way TMDb wrote their API.
 164            // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab
 5165            var parts = language.Split('-');
 166
 5167            if (parts.Length == 2)
 168            {
 169                // TMDb doesn't support Switzerland (de-CH, it-CH or fr-CH) so use the language (de, it or fr) without c
 3170                if (string.Equals(parts[1], "CH", StringComparison.OrdinalIgnoreCase))
 171                {
 1172                    return parts[0];
 173                }
 174
 2175                language = parts[0] + "-" + parts[1].ToUpperInvariant();
 176            }
 177
 4178            return language;
 179        }
 180
 181        /// <summary>
 182        /// Adjusts the image's language code preferring the 5 letter language code eg. en-US.
 183        /// </summary>
 184        /// <param name="imageLanguage">The image's actual language code.</param>
 185        /// <param name="requestLanguage">The requested language code.</param>
 186        /// <returns>The language code.</returns>
 187        public static string AdjustImageLanguage(string imageLanguage, string requestLanguage)
 188        {
 5189            if (!string.IsNullOrEmpty(imageLanguage)
 5190                && !string.IsNullOrEmpty(requestLanguage)
 5191                && requestLanguage.Length > 2
 5192                && imageLanguage.Length == 2
 5193                && requestLanguage.StartsWith(imageLanguage, StringComparison.OrdinalIgnoreCase))
 194            {
 1195                return requestLanguage;
 196            }
 197
 198            // TMDb now returns xx for no language instead of an empty string.
 4199            return string.Equals(imageLanguage, "xx", StringComparison.OrdinalIgnoreCase)
 4200                ? string.Empty
 4201                : imageLanguage;
 202        }
 203
 204        /// <summary>
 205        /// Combines the metadata country code and the parental rating from the API into the value we store in our datab
 206        /// </summary>
 207        /// <param name="countryCode">The ISO 3166-1 country code of the rating country.</param>
 208        /// <param name="ratingValue">The rating value returned by the TMDb API.</param>
 209        /// <returns>The combined parental rating of country code+rating value.</returns>
 210        public static string BuildParentalRating(string countryCode, string ratingValue)
 211        {
 212            // Exclude US because we store US values as TV-14 without the country code.
 0213            var ratingPrefix = string.Equals(countryCode, "US", StringComparison.OrdinalIgnoreCase) ? string.Empty : cou
 0214            var newRating = ratingPrefix + ratingValue;
 215
 0216            return newRating.Replace("DE-", "FSK-", StringComparison.OrdinalIgnoreCase);
 217        }
 218    }
 219}