public async Task <MetadataResult <Season> > GetMetadata(SeasonInfo info,
                                                                 CancellationToken cancellationToken)
        {
            _logger.LogInformation($"Douban:GetMetadata for {info.Name}");
            var result = new MetadataResult <Season>();

            info.SeriesProviderIds.TryGetValue(ProviderID, out string sid);
            if (string.IsNullOrEmpty(sid))
            {
                _logger.LogInformation("No douban sid found, just skip");
                return(result);
            }

            if (info.IndexNumber.HasValue && info.IndexNumber.Value > 0)
            {
                // We can not give more information from Douban right now.
                return(result);
            }

            var subject = await GetDoubanSubject(sid, cancellationToken).
                          ConfigureAwait(false);

            if (subject.Current_Season.HasValue)
            {
                result.Item = new Season
                {
                    IndexNumber    = subject.Current_Season.Value,
                    ProductionYear = int.Parse(subject.Year)
                };
                result.HasMetadata = true;
            }
            return(result);
        }
Beispiel #2
0
        public void CacheExtractedItem(Guid extractedItemId, IDictionary <Guid, IList <MediaItemAspect> > extractedAspects)
        {
            SeasonInfo season = new SeasonInfo();

            season.FromMetadata(extractedAspects);
            AddToCache(extractedItemId, season, false);
        }
        /// <summary>
        /// Constructor.
        /// </summary>
        public SeriesSeasonInfo(MediaItem mediaItem)
        {
            try
            {
                SeriesId = Guid.Empty;
                SeasonId = mediaItem.MediaItemId;
                if (mediaItem.Aspects.ContainsKey(EpisodeAspect.ASPECT_ID))
                {
                    if (MediaItemAspect.TryGetAspects(mediaItem.Aspects, RelationshipAspect.Metadata, out IList <MultipleMediaItemAspect> relationAspects))
                    {
                        foreach (MultipleMediaItemAspect relation in relationAspects)
                        {
                            if ((Guid?)relation[RelationshipAspect.ATTR_LINKED_ROLE] == SeriesAspect.ROLE_SERIES)
                            {
                                SeriesId = (Guid)relation[RelationshipAspect.ATTR_LINKED_ID];
                            }
                        }
                    }
                }

                SeasonInfo season = new SeasonInfo();
                season.FromMetadata(mediaItem.Aspects);

                Season    = season.SeasonNumber.Value;
                Summary   = season.Description.Text;
                Title     = $"Season {season.SeasonNumber}";
                Series    = season.SeriesName.Text;
                ImageName = Helper.GetImageBaseURL(mediaItem, FanArtMediaTypes.SeriesSeason, FanArtTypes.Thumbnail);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Error("WifiRemote: Error getting season info", e);
            }
        }
        public override bool UpdateFromOnlineSeriesSeason(SeasonInfo season, string language, bool cacheOnly)
        {
            try
            {
                OmDbSeason seasonDetail = null;
                if (!string.IsNullOrEmpty(season.SeriesImdbId) && season.SeasonNumber.HasValue)
                {
                    seasonDetail = _omDbHandler.GetSeriesSeason(season.SeriesImdbId, season.SeasonNumber.Value, cacheOnly);
                }
                if (seasonDetail == null)
                {
                    return(false);
                }

                season.SeriesName    = new SimpleTitle(seasonDetail.Title, true);
                season.FirstAired    = seasonDetail.Episodes != null && seasonDetail.Episodes.Count > 0 ? seasonDetail.Episodes[0].Released : default(DateTime?);
                season.SeasonNumber  = seasonDetail.SeasonNumber;
                season.TotalEpisodes = seasonDetail.Episodes.Count;

                return(true);
            }
            catch (Exception ex)
            {
                ServiceRegistration.Get <ILogger>().Debug("OmDbWrapper: Exception while processing season {0}", ex, season.ToString());
                return(false);
            }
        }
        public async Task GetMetadata_TvDbLibraryStructure_UsesNameFromLibraryStructureSource(
            string fileStructureSourceName)
        {
            Plugin.Instance.Configuration.LibraryStructureSourceName = SourceNames.TvDb;
            Plugin.Instance.Configuration.FileStructureSourceName    = fileStructureSourceName;

            var seasonInfo = new SeasonInfo
            {
                Name              = "Season Unknown",
                IndexNumber       = 2,
                SeriesProviderIds = new Dictionary <string, string>
                {
                    { SourceNames.AniDb, "959" },
                    { SourceNames.TvDb, "78914" },
                    { SourceNames.AniList, "72" }
                }
            };

            var seasonEntryPoint = new SeasonProviderEntryPoint(this.applicationHost);

            var result = await seasonEntryPoint.GetMetadata(seasonInfo, CancellationToken.None);

            result.HasMetadata.Should().BeTrue();
            result.Item.Name.Should().BeEquivalentTo("Season 2");
        }
            public void Setup()
            {
                _seasonInfo = new SeasonInfo
                {
                    Name              = "SeasonName",
                    IndexNumber       = 3,
                    ParentIndexNumber = 1,
                    ProviderIds       = new Dictionary <string, string>
                    {
                        { "Source", "66" }
                    }
                };
                _mediaItemProcessorResult = Left <ProcessFailedResult, IMetadataFoundResult <Season> >(
                    new ProcessFailedResult("FailedSource",
                                            "MediaItemName", MediaItemTypes.Season, "Failure reason"));

                _mediaItemProcessor = Substitute.For <IMediaItemProcessor>();
                _mediaItemProcessor.GetResultAsync(_seasonInfo, MediaItemTypes.Season, Arg.Any <IEnumerable <EmbyItemId> >())
                .Returns(x => _mediaItemProcessorResult);

                _logManager = Substitute.For <ILogManager>();

                _logger = Substitute.For <ILogger>();
                _logger.WhenForAnyArgs(l => l.Debug(null, null)).Do(c => Console.WriteLine($"Debug: {c.Arg<string>()}"));

                _logManager.GetLogger("SeasonProvider").Returns(_logger);

                _seasonProvider = new SeasonProvider(_logManager, _mediaItemProcessor);
            }
        public IDictionary <Guid, IList <MediaItemAspect> > GetBaseChildAspectsFromExistingAspects(IDictionary <Guid, IList <MediaItemAspect> > existingChildAspects, IDictionary <Guid, IList <MediaItemAspect> > existingParentAspects)
        {
            if (existingParentAspects.ContainsKey(SeriesAspect.ASPECT_ID))
            {
                SeriesInfo series = new SeriesInfo();
                series.FromMetadata(existingParentAspects);

                if (existingChildAspects.ContainsKey(SeasonAspect.ASPECT_ID))
                {
                    SeasonInfo season = new SeasonInfo();
                    season.FromMetadata(existingChildAspects);

                    SeasonInfo basicSeason = series.CloneBasicInstance <SeasonInfo>();
                    basicSeason.SeasonNumber = season.SeasonNumber;
                    IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                    basicSeason.SetMetadata(aspects, true);
                    return(aspects);
                }
                else if (existingChildAspects.ContainsKey(EpisodeAspect.ASPECT_ID))
                {
                    EpisodeInfo episode = new EpisodeInfo();
                    episode.FromMetadata(existingChildAspects);

                    EpisodeInfo basicEpisode = series.CloneBasicInstance <EpisodeInfo>();
                    basicEpisode.SeasonNumber   = episode.SeasonNumber;
                    basicEpisode.EpisodeNumbers = episode.EpisodeNumbers.ToList();
                    IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                    basicEpisode.SetMetadata(aspects, true);
                    return(aspects);
                }
            }
            return(null);
        }
 public Task <IEnumerable <RemoteSearchResult> > GetSearchResults(
     SeasonInfo info, CancellationToken cancellationToken)
 {
     _logger.LogInformation("Douban:Search for season {0}", info.Name);
     // It's needless for season to do search
     return(Task.FromResult <IEnumerable <RemoteSearchResult> >(
                new List <RemoteSearchResult>()));
 }
Beispiel #9
0
 public static void Update(SeasonInfo seasonInfo)
 {
     using (var db = new FFStatsDbContext())
     {
         db.SeasonInfo.Update(seasonInfo);
         db.SaveChanges();
     }
 }
 public override bool UpdateFromOnlineSeriesSeason(SeasonInfo season, string language, bool cacheOnly)
 {
     if (season.SeriesTvdbId > 0)
     {
         return(true);
     }
     return(false);
 }
Beispiel #11
0
        public ActionResult DeleteConfirmed(int id)
        {
            SeasonInfo seasonInfo = db.SeasonInfo.Find(id);

            db.SeasonInfo.Remove(seasonInfo);
            db.SaveChanges();
            Success("Deleted successfully", true);
            return(RedirectToAction("Index"));
        }
Beispiel #12
0
        public static SeasonInfo Add(SeasonInfo seasonInfo)
        {
            using (var db = new FFStatsDbContext())
            {
                db.SeasonInfo.Add(seasonInfo);
                db.SaveChanges();
            }

            return(seasonInfo);
        }
        public async Task <bool> UpdateSeasonAsync(SeasonInfo seasonInfo)
        {
            bool success = false;

            foreach (ISeriesMatcher matcher in SERIES_MATCHERS.Where(m => m.Enabled))
            {
                success |= await matcher.UpdateSeasonAsync(seasonInfo).ConfigureAwait(false);
            }
            return(success);
        }
Beispiel #14
0
        public async Task <bool> UpdateSeasonAsync(SeasonInfo seasonInfo, string category)
        {
            bool success = false;

            foreach (ISeriesMatcher matcher in SERIES_MATCHERS.Where(m => category == null || m.Key == category).SelectMany(m => m.Value).Where(m => m.Enabled))
            {
                success |= await matcher.UpdateSeasonAsync(seasonInfo).ConfigureAwait(false);
            }
            return(success);
        }
        public bool UpdateSeason(SeasonInfo seasonInfo, bool importOnly)
        {
            bool success = false;

            foreach (ISeriesMatcher matcher in SERIES_MATCHERS.Where(m => m.Enabled))
            {
                success |= matcher.UpdateSeason(seasonInfo, importOnly);
            }
            return(success);
        }
Beispiel #16
0
 public static LeagueViewModel Create(LeagueInfo league, SeasonInfo season)
 {
     return(new LeagueViewModel(SimpleIoc.Default.GetInstance <IOpenLigaService>())
     {
         LeagueKey = league.Key,
         LeagueName = league.Name,
         SeasonKey = season.Key,
         SeasonName = season.Name
     });
 }
        public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects)
        {
            SeasonInfo seasonInfo = new SeasonInfo();

            if (!seasonInfo.FromMetadata(aspects))
            {
                return(false);
            }

            SeriesInfo seriesInfo = RelationshipExtractorUtils.TryCreateInfoFromLinkedAspects(extractedLinkedAspects, out List <SeriesInfo> series) ?
                                    series[0] : seasonInfo.CloneBasicInstance <SeriesInfo>();

            if (!SeriesMetadataExtractor.SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.UpdateSeriesAsync(seriesInfo, false).ConfigureAwait(false);
            }

            if (seriesInfo.Genres.Count > 0)
            {
                IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                foreach (var genre in seriesInfo.Genres)
                {
                    if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId))
                    {
                        genre.Id = genreId;
                        seriesInfo.HasChanged = true;
                    }
                }
            }

            IDictionary <Guid, IList <MediaItemAspect> > seriesAspects = seriesInfo.LinkedAspects != null ?
                                                                         seriesInfo.LinkedAspects : new Dictionary <Guid, IList <MediaItemAspect> >();

            seriesInfo.SetMetadata(seriesAspects);

            if (aspects.ContainsKey(EpisodeAspect.ASPECT_ID))
            {
                bool episodeVirtual = true;
                if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out episodeVirtual))
                {
                    MediaItemAspect.SetAttribute(seriesAspects, MediaAspect.ATTR_ISVIRTUAL, episodeVirtual);
                }
            }

            if (!seriesAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID))
            {
                return(false);
            }

            if (seriesInfo.LinkedAspects == null)
            {
                extractedLinkedAspects.Add(seriesAspects);
            }
            return(true);
        }
Beispiel #18
0
        public override async Task <bool> UpdateFromOnlineSeriesSeasonAsync(SeasonInfo season, TvdbLanguage language, bool cacheOnly)
        {
            try
            {
                language = language ?? PreferredLanguage;

                TvdbSeries seriesDetail = null;
                if (season.SeriesTvdbId > 0)
                {
                    seriesDetail = await _tvdbHandler.GetSeriesAsync(season.SeriesTvdbId, language, true, false, false).ConfigureAwait(false);
                }
                if (seriesDetail == null && !cacheOnly && !string.IsNullOrEmpty(season.SeriesImdbId))
                {
                    TvdbSearchResult foundSeries = await _tvdbHandler.GetSeriesByRemoteIdAsync(ExternalId.ImdbId, season.SeriesImdbId).ConfigureAwait(false);

                    if (foundSeries != null)
                    {
                        seriesDetail = await _tvdbHandler.GetSeriesAsync(foundSeries.Id, language, true, false, false).ConfigureAwait(false);
                    }
                }
                if (seriesDetail == null)
                {
                    return(false);
                }
                if (!season.SeasonNumber.HasValue)
                {
                    return(false);
                }
                var episode = seriesDetail.Episodes.Where(e => e.SeasonNumber == season.SeasonNumber).ToList().FirstOrDefault();
                if (episode == null)
                {
                    return(false);
                }

                season.TvdbId        = episode.SeasonId;
                season.SeriesTvdbId  = seriesDetail.Id;
                season.SeriesImdbId  = seriesDetail.ImdbId;
                season.FirstAired    = episode.FirstAired;
                season.SeriesName    = new SimpleTitle(seriesDetail.SeriesName, false);
                season.SeasonNumber  = season.SeasonNumber.Value;
                season.Description   = new SimpleTitle(seriesDetail.Overview, false);
                season.TotalEpisodes = seriesDetail.Episodes.FindAll(e => e.SeasonNumber == season.SeasonNumber).Count;
                if (!season.DataProviders.Contains(_name))
                {
                    season.DataProviders.Add(_name);
                }

                return(true);
            }
            catch (Exception ex)
            {
                ServiceRegistration.Get <ILogger>().Debug("TvDbWrapper: Exception while processing season {0}", ex, season.ToString());
                return(false);
            }
        }
Beispiel #19
0
        public void TestGetSeasonMetadata()
        {
            SeasonInfo seasonInfo = new SeasonInfo()
            {
                Name = "老友记 第二季"
            };

            seasonInfo.SeriesProviderIds[FrodoUtils.ProviderId] = "1393859";
            var metadataResult = _doubanProvider.GetMetadata(seasonInfo, CancellationToken.None).Result;

            Assert.True(metadataResult.HasMetadata);
        }
Beispiel #20
0
        public override void Update(MediaItem mediaItem)
        {
            base.Update(mediaItem);
            if (mediaItem == null)
            {
                return;
            }

            SeasonInfo seasonInfo = new SeasonInfo();

            if (!seasonInfo.FromMetadata(mediaItem.Aspects))
            {
                return;
            }

            Season    = seasonInfo.SeasonNumber.HasValue ? seasonInfo.SeasonNumber.Value.ToString() : "";
            Series    = seasonInfo.SeriesName.Text ?? "";
            StoryPlot = seasonInfo.Description.Text ?? "";

            int?count;

            if (mediaItem.Aspects.ContainsKey(SeasonAspect.ASPECT_ID))
            {
                if (MediaItemAspect.TryGetAttribute(mediaItem.Aspects, SeasonAspect.ATTR_AVAILABLE_EPISODES, out count))
                {
                    AvailableEpisodes = count.Value.ToString();
                }
                else
                {
                    AvailableEpisodes = "";
                }

                if (MediaItemAspect.TryGetAttribute(mediaItem.Aspects, SeasonAspect.ATTR_NUM_EPISODES, out count))
                {
                    TotalEpisodes = count.Value.ToString();
                }
                else
                {
                    TotalEpisodes = "";
                }

                if (VirtualMediaHelper.ShowVirtualSeriesMedia)
                {
                    Episodes = string.IsNullOrEmpty(TotalEpisodes) ? AvailableEpisodes : TotalEpisodes;
                }
                else
                {
                    Episodes = AvailableEpisodes;
                }
            }

            FireChange();
        }
Beispiel #21
0
        public override async Task <bool> UpdateFromOnlineSeriesSeasonAsync(SeasonInfo season, string language, bool cacheOnly)
        {
            try
            {
                TvMazeSeries seriesDetail = null;
                TvMazeSeason seasonDetail = null;
                if (season.SeriesTvMazeId > 0)
                {
                    seriesDetail = await _tvMazeHandler.GetSeriesAsync(season.SeriesTvMazeId, cacheOnly).ConfigureAwait(false);
                }
                if (seriesDetail == null)
                {
                    return(false);
                }
                if (season.SeriesTvMazeId > 0 && season.SeasonNumber.HasValue)
                {
                    List <TvMazeSeason> seasons = await _tvMazeHandler.GetSeriesSeasonsAsync(season.SeriesTvMazeId, cacheOnly).ConfigureAwait(false);

                    if (seasons != null)
                    {
                        seasonDetail = seasons.Where(s => s.SeasonNumber == season.SeasonNumber).FirstOrDefault();
                    }
                }
                if (seasonDetail == null)
                {
                    return(false);
                }

                season.TvMazeId = seasonDetail.Id;

                season.SeriesTvMazeId = seriesDetail.Id;
                season.SeriesImdbId   = seriesDetail.Externals.ImDbId;
                season.SeriesTvdbId   = seriesDetail.Externals.TvDbId ?? 0;
                season.SeriesTvRageId = seriesDetail.Externals.TvRageId ?? 0;

                season.SeriesName    = new SimpleTitle(seriesDetail.Name, true);
                season.FirstAired    = seasonDetail.PremiereDate;
                season.SeasonNumber  = seasonDetail.SeasonNumber;
                season.TotalEpisodes = seasonDetail.EpisodeCount ?? 0;
                if (!season.DataProviders.Contains(_name))
                {
                    season.DataProviders.Add(_name);
                }

                return(true);
            }
            catch (Exception ex)
            {
                ServiceRegistration.Get <ILogger>().Debug("TvMazeWrapper: Exception while processing season {0}", ex, season.ToString());
                return(false);
            }
        }
Beispiel #22
0
        public override bool UpdateSeason(SeasonInfo seasonInfo, bool importOnly)
        {
            // Don't allow OMDB during first import cycle because it is english only
            // If it was allowed it would prevent the update of metadata with preffered language
            // during refresh cycle that also allows searches which might be needed to find metadata
            // in the preferred language
            if (importOnly && !Primary)
            {
                return(false);
            }

            return(base.UpdateSeason(seasonInfo, importOnly));
        }
Beispiel #23
0
        // GET: SeasonInfo/Delete/5
        public ActionResult Delete(int?id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }
            SeasonInfo seasonInfo = db.SeasonInfo.Find(id);

            if (seasonInfo == null)
            {
                return(HttpNotFound());
            }
            return(View(seasonInfo));
        }
Beispiel #24
0
        public void TestGetSeasonMetadata()
        {
            SeasonInfo seasonInfo = new SeasonInfo
            {
                Name = "老友记 第二季",
                SeriesProviderIds = new Dictionary <string, string> {
                    { OddbPlugin.ProviderId, "1393859" }
                }
            };

            var metadataResult = _doubanProvider.GetMetadata(seasonInfo, CancellationToken.None).Result;

            Assert.True(metadataResult.HasMetadata);
        }
Beispiel #25
0
        // GET: SeasonInfo/Edit/5
        public ActionResult Edit(int?id)
        {
            if (id == null)
            {
                return(new HttpStatusCodeResult(HttpStatusCode.BadRequest));
            }
            SeasonInfo seasonInfo = db.SeasonInfo.Find(id);

            if (seasonInfo == null)
            {
                return(HttpNotFound());
            }

            ViewBag.BuyerInfoId = new SelectList(db.BuyerInfo.OrderBy(x => x.Name), "Id", "Name", seasonInfo.BuyerInfoId);
            return(View(seasonInfo));
        }
        public static SeasonInfo AsSeason(this BaseInfo info)
        {
            SeasonInfo season = info as SeasonInfo;

            if (season != null)
            {
                return(season);
            }
            EpisodeInfo episode = info as EpisodeInfo;

            if (episode != null)
            {
                return(episode.CloneBasicInstance <SeasonInfo>());
            }
            return(null);
        }
Beispiel #27
0
        public List <FolderInfo> GetFiles()
        {
            List <FolderInfo> folders = new List <FolderInfo>();

            PathConverter pathConverter = PathConverter.GetInstance();
            List <string> folderPaths   = pathConverter.GetFilesPhysicalPathInVirtual(this.virtualAddress);

            folderPaths = pathConverter.SortByNumeric(folderPaths);

            String physicalPath = pathConverter.ConvertToPhysicalPath(this.virtualAddress);

            SeasonInfo parser = new SeasonInfo(physicalPath);

            foreach (var path in folderPaths)
            {
                if (settings.FileFilter.Test(path))
                {
                    FolderInfo folderInfo = new FolderInfo(path, true);

                    foreach (var episode in parser.Episodes)
                    {
                        if (Path.GetFileNameWithoutExtension(folderInfo.FolderName) == episode.Filename)
                        {
                            string imgVirtPath = String.Format("{0}/metadata/{1}", this.virtualAddress, episode.EpisodeImage.ImagePhysicalPath);

                            String imgPhysPath = pathConverter.ConvertToPhysicalPath(imgVirtPath);

                            if (!File.Exists(imgPhysPath))
                            {
                                imgVirtPath = "Resources/unknown.jpg";
                            }

                            folderInfo.FolderImage.ImagePhysicalPath = imgVirtPath;
                            folderInfo.FolderDiplayName  = String.Format("{0}) {1}", episode.EpisodeNumber, episode.EpisodeName);
                            folderInfo.Overview          = episode.EpisodeOverview;
                            folderInfo.SubtitleLanguages = episode.SubtitleLanguages;
                            break;
                        }
                    }

                    folders.Add(folderInfo);
                }
            }

            return(folders);
        }
Beispiel #28
0
        public static SeasonStorage Run(
            [QueueTrigger(QueueNames.AvailableSeasons, Connection = "AzureWebJobsStorage")]
            SeasonInfo seasonInfo,
            ILogger log)
        {
            var result = new SeasonStorage
            {
                PartitionKey = "Season",
                RowKey       = $"{seasonInfo.Year.ToString()}-{seasonInfo.Season}",
                Season       = seasonInfo.Season,
                Year         = seasonInfo.Year
            }.AddEtag();

            log.LogInformation($"Updating available seasons with {seasonInfo.Season} on {seasonInfo.Year}");

            return(result);
        }
Beispiel #29
0
 public ActionResult Edit([Bind(Include = "Id,BuyerInfoId,Name,Description")] SeasonInfo seasonInfo)
 {
     if (ModelState.IsValid)
     {
         if (db.SeasonInfo.Where(x => x.BuyerInfoId == seasonInfo.BuyerInfoId && x.Name.ToLower() == seasonInfo.Name.ToLower() && x.Id != seasonInfo.Id).Count() > 0)
         {
             Danger("Name exists! Try different", false);
         }
         else
         {
             seasonInfo.OpBy            = 1;
             seasonInfo.OpOn            = DateTime.Now;
             db.Entry(seasonInfo).State = EntityState.Modified;
             db.SaveChanges();
             Success("Saved successfully!", true);
             return(RedirectToAction("Index"));
         }
     }
     ViewBag.BuyerInfoId = new SelectList(db.BuyerInfo.OrderBy(x => x.Name), "Id", "Name", seasonInfo.BuyerInfoId);
     return(View(seasonInfo));
 }
        public async Task <MetadataResult <Season> > GetMetadata(SeasonInfo info,
                                                                 CancellationToken cancellationToken)
        {
            _logger.LogInformation($"[DOUBAN FRODO INFO] Getting metadata for \"{info.Name}\"");
            var result = new MetadataResult <Season>();

            info.SeriesProviderIds.TryGetValue(ProviderID, out string seriesId);
            var sid = info.GetProviderId(ProviderID);

            if (string.IsNullOrEmpty(sid))
            {
                var searchResults = await SearchFrodoByName(info.Name, "tv",
                                                            cancellationToken).ConfigureAwait(false);

                sid = searchResults.FirstOrDefault()?.Id;
            }

            if (string.IsNullOrWhiteSpace(sid))
            {
                _logger.LogError($"[DOUBAN FRODO ERROR] No sid found for \"{info.Name}\"");
                return(new MetadataResult <Season>());
            }

            var subject = await GetFrodoSubject(sid, "tv", cancellationToken).
                          ConfigureAwait(false);

            string pattern_name = @".* (?i)Season(?-i) (\d+)$";
            Match  match        = Regex.Match(subject.Original_Title, pattern_name);

            if (match.Success)
            {
                result.Item = new Season
                {
                    IndexNumber    = int.Parse(match.Groups[1].Value),
                    ProductionYear = int.Parse(subject.Year)
                };
                result.HasMetadata = true;
            }
            return(result);
        }