< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Plugins.Omdb.OmdbItemProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
Line coverage
4%
Covered lines: 5
Uncovered lines: 96
Coverable lines: 101
Total lines: 311
Line coverage: 4.9%
Branch coverage
0%
Covered branches: 0
Total branches: 50
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/16/2026 - 12:15:55 AM Line coverage: 6.9% (7/101) Branch coverage: 0% (0/50) Total lines: 31210/29/2026 - 12:17:11 AM Line coverage: 4.9% (5/101) Branch coverage: 0% (0/50) Total lines: 311 5/16/2026 - 12:15:55 AM Line coverage: 6.9% (7/101) Branch coverage: 0% (0/50) Total lines: 31210/29/2026 - 12:17:11 AM Line coverage: 4.9% (5/101) Branch coverage: 0% (0/50) Total lines: 311

Coverage delta

Coverage delta 2 -2

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%210%
get_Order()100%210%
GetSearchResults(...)100%210%
GetSearchResults(...)100%210%
GetSearchResults(...)100%210%
GetSearchResults(...)100%210%
GetSearchResultsInternal()0%1332360%
GetMetadata(...)100%210%
GetMetadata(...)100%210%
GetMetadata(...)100%210%
ResultToMetadataResult(...)0%7280%
GetResult()0%2040%
GetImdbId()0%620%
GetImageResponse(...)100%210%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591, SA1300
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Globalization;
 8using System.Linq;
 9using System.Net;
 10using System.Net.Http;
 11using System.Net.Http.Json;
 12using System.Text;
 13using System.Text.Json;
 14using System.Threading;
 15using System.Threading.Tasks;
 16using MediaBrowser.Common.Net;
 17using MediaBrowser.Controller.Configuration;
 18using MediaBrowser.Controller.Entities;
 19using MediaBrowser.Controller.Entities.Movies;
 20using MediaBrowser.Controller.Entities.TV;
 21using MediaBrowser.Controller.Library;
 22using MediaBrowser.Controller.Providers;
 23using MediaBrowser.Model.Entities;
 24using MediaBrowser.Model.IO;
 25using MediaBrowser.Model.Providers;
 26
 27namespace MediaBrowser.Providers.Plugins.Omdb
 28{
 29    public class OmdbItemProvider : IRemoteMetadataProvider<Series, SeriesInfo>,
 30        IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder
 31    {
 32        private readonly IHttpClientFactory _httpClientFactory;
 33        private readonly ILibraryManager _libraryManager;
 34        private readonly JsonSerializerOptions _jsonOptions;
 35        private readonly OmdbProvider _omdbProvider;
 36
 37        public OmdbItemProvider(
 38            IHttpClientFactory httpClientFactory,
 39            ILibraryManager libraryManager,
 40            IFileSystem fileSystem,
 41            IServerConfigurationManager configurationManager)
 42        {
 5443            _httpClientFactory = httpClientFactory;
 5444            _libraryManager = libraryManager;
 5445            _omdbProvider = new OmdbProvider(_httpClientFactory, fileSystem, configurationManager);
 46
 5447            _jsonOptions = OmdbProvider.CreateJsonOptions();
 5448        }
 49
 050        public string Name => "The Open Movie Database";
 51
 52        // After primary option
 053        public int Order => 2;
 54
 55        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(TrailerInfo searchInfo, CancellationToken cancella
 56        {
 057            return GetSearchResultsInternal(searchInfo, true, cancellationToken);
 58        }
 59
 60        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellat
 61        {
 062            return GetSearchResultsInternal(searchInfo, true, cancellationToken);
 63        }
 64
 65        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellati
 66        {
 067            return GetSearchResultsInternal(searchInfo, true, cancellationToken);
 68        }
 69
 70        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancella
 71        {
 072            return GetSearchResultsInternal(searchInfo, true, cancellationToken);
 73        }
 74
 75        private async Task<IEnumerable<RemoteSearchResult>> GetSearchResultsInternal(ItemLookupInfo searchInfo, bool isS
 76        {
 077            var type = searchInfo switch
 078            {
 079                EpisodeInfo => "episode",
 080                SeriesInfo => "series",
 081                _ => "movie"
 082            };
 83
 84            // This is a bit hacky?
 085            var episodeSearchInfo = searchInfo as EpisodeInfo;
 086            var indexNumberEnd = episodeSearchInfo?.IndexNumberEnd;
 87
 088            var imdbId = searchInfo.GetProviderId(MetadataProvider.Imdb);
 89
 090            var urlQuery = new StringBuilder("plot=full&r=json");
 091            if (episodeSearchInfo is not null)
 92            {
 093                episodeSearchInfo.SeriesProviderIds.TryGetValue(MetadataProvider.Imdb.ToString(), out imdbId);
 094                if (searchInfo.IndexNumber.HasValue)
 95                {
 096                    urlQuery.Append("&Episode=").Append(searchInfo.IndexNumber.Value);
 97                }
 98
 099                if (searchInfo.ParentIndexNumber.HasValue)
 100                {
 0101                    urlQuery.Append("&Season=").Append(searchInfo.ParentIndexNumber.Value);
 102                }
 103            }
 104
 0105            if (string.IsNullOrWhiteSpace(imdbId))
 106            {
 0107                var name = searchInfo.Name;
 0108                var year = searchInfo.Year;
 0109                if (!string.IsNullOrWhiteSpace(name))
 110                {
 0111                    var parsedName = _libraryManager.ParseName(name);
 0112                    var yearInName = parsedName.Year;
 0113                    name = parsedName.Name;
 0114                    year ??= yearInName;
 115                }
 116
 0117                if (year.HasValue)
 118                {
 0119                    urlQuery.Append("&y=").Append(year);
 120                }
 121
 122                // &s means search and returns a list of results as opposed to t
 0123                urlQuery.Append(isSearch ? "&s=" : "&t=");
 0124                urlQuery.Append(WebUtility.UrlEncode(name));
 0125                urlQuery.Append("&type=")
 0126                    .Append(type);
 127            }
 128            else
 129            {
 0130                urlQuery.Append("&i=")
 0131                    .Append(imdbId);
 0132                isSearch = false;
 133            }
 134
 0135            var url = OmdbProvider.GetOmdbUrl(urlQuery.ToString());
 136
 0137            using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationTo
 0138            if (isSearch)
 139            {
 0140                var searchResultList = await response.Content.ReadFromJsonAsync<SearchResultList>(_jsonOptions, cancella
 0141                if (searchResultList?.Search is not null)
 142                {
 0143                    var resultCount = searchResultList.Search.Count;
 0144                    var result = new RemoteSearchResult[resultCount];
 0145                    for (var i = 0; i < resultCount; i++)
 146                    {
 0147                        result[i] = ResultToMetadataResult(searchResultList.Search[i], searchInfo, indexNumberEnd);
 148                    }
 149
 0150                    return result;
 151                }
 152            }
 153            else
 154            {
 0155                var result = await response.Content.ReadFromJsonAsync<SearchResult>(_jsonOptions, cancellationToken).Con
 0156                if (string.Equals(result?.Response, "true", StringComparison.OrdinalIgnoreCase))
 157                {
 0158                    return new[] { ResultToMetadataResult(result, searchInfo, indexNumberEnd) };
 159                }
 160            }
 161
 0162            return Enumerable.Empty<RemoteSearchResult>();
 0163        }
 164
 165        public Task<MetadataResult<Trailer>> GetMetadata(TrailerInfo info, CancellationToken cancellationToken)
 166        {
 0167            return GetResult<Trailer>(info, cancellationToken);
 168        }
 169
 170        public Task<MetadataResult<Series>> GetMetadata(SeriesInfo info, CancellationToken cancellationToken)
 171        {
 0172            return GetResult<Series>(info, cancellationToken);
 173        }
 174
 175        public Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken)
 176        {
 0177            return GetResult<Movie>(info, cancellationToken);
 178        }
 179
 180        private RemoteSearchResult ResultToMetadataResult(SearchResult result, ItemLookupInfo searchInfo, int? indexNumb
 181        {
 0182            var item = new RemoteSearchResult
 0183            {
 0184                IndexNumber = searchInfo.IndexNumber,
 0185                Name = result.Title,
 0186                ParentIndexNumber = searchInfo.ParentIndexNumber,
 0187                SearchProviderName = Name,
 0188                IndexNumberEnd = indexNumberEnd
 0189            };
 190
 0191            item.SetProviderId(MetadataProvider.Imdb, result.imdbID);
 192
 0193            if (OmdbProvider.TryParseYear(result.Year, out var parsedYear))
 194            {
 0195                item.ProductionYear = parsedYear;
 196            }
 197
 0198            if (!string.IsNullOrEmpty(result.Released)
 0199                && DateTime.TryParse(result.Released, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces, out
 200            {
 0201                item.PremiereDate = released;
 202            }
 203
 0204            if (!string.IsNullOrWhiteSpace(result.Poster))
 205            {
 0206                item.ImageUrl = result.Poster;
 207            }
 208
 0209            return item;
 210        }
 211
 212        private async Task<MetadataResult<T>> GetResult<T>(ItemLookupInfo info, CancellationToken cancellationToken)
 213            where T : BaseItem, new()
 214        {
 0215            var result = new MetadataResult<T>
 0216            {
 0217                Item = new T(),
 0218                QueriedById = true,
 0219                // OMDb is not localized, everything it returns is English
 0220                ResultLanguage = "en"
 0221            };
 222
 0223            var imdbId = info.GetProviderId(MetadataProvider.Imdb);
 0224            if (string.IsNullOrWhiteSpace(imdbId))
 225            {
 0226                imdbId = await GetImdbId(info, cancellationToken).ConfigureAwait(false);
 0227                result.QueriedById = false;
 228            }
 229
 0230            if (!string.IsNullOrEmpty(imdbId))
 231            {
 0232                result.Item.SetProviderId(MetadataProvider.Imdb, imdbId);
 0233                result.HasMetadata = true;
 234
 0235                await _omdbProvider.Fetch(result, imdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationT
 236            }
 237
 0238            return result;
 0239        }
 240
 241        private async Task<string> GetImdbId(ItemLookupInfo info, CancellationToken cancellationToken)
 242        {
 0243            var results = await GetSearchResultsInternal(info, false, cancellationToken).ConfigureAwait(false);
 0244            var first = results.FirstOrDefault();
 0245            return first?.GetProviderId(MetadataProvider.Imdb);
 0246        }
 247
 248        public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
 249        {
 0250            return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
 251        }
 252
 253        private class SearchResult
 254        {
 255            public string Title { get; set; }
 256
 257            public string Year { get; set; }
 258
 259            public string Rated { get; set; }
 260
 261            public string Released { get; set; }
 262
 263            public string Season { get; set; }
 264
 265            public string Episode { get; set; }
 266
 267            public string Runtime { get; set; }
 268
 269            public string Genre { get; set; }
 270
 271            public string Director { get; set; }
 272
 273            public string Writer { get; set; }
 274
 275            public string Actors { get; set; }
 276
 277            public string Plot { get; set; }
 278
 279            public string Language { get; set; }
 280
 281            public string Country { get; set; }
 282
 283            public string Awards { get; set; }
 284
 285            public string Poster { get; set; }
 286
 287            public string Metascore { get; set; }
 288
 289            public string imdbRating { get; set; }
 290
 291            public string imdbVotes { get; set; }
 292
 293            public string imdbID { get; set; }
 294
 295            public string seriesID { get; set; }
 296
 297            public string Type { get; set; }
 298
 299            public string Response { get; set; }
 300        }
 301
 302        private class SearchResultList
 303        {
 304            /// <summary>
 305            /// Gets or sets the results.
 306            /// </summary>
 307            /// <value>The results.</value>
 308            public List<SearchResult> Search { get; set; }
 309        }
 310    }
 311}