protected bool ExtractSeriesData(string localFsResourcePath, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
            {
                return(false);
            }

            SeriesInfo seriesInfo;

            // Try to get extended information out of matroska files)
            MatroskaMatcher matroskaMatcher = new MatroskaMatcher();

            matroskaMatcher.MatchSeries(localFsResourcePath, out seriesInfo, ref extractedAspectData);

            // If no information from mkv were found, try name matching
            if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
            {
                // Try to match series from folder and file namings
                SeriesMatcher seriesMatcher = new SeriesMatcher();
                seriesMatcher.MatchSeries(localFsResourcePath, out seriesInfo);
            }

            // Lookup online information (incl. fanart)
            if (seriesInfo != null && seriesInfo.IsCompleteMatch)
            {
                SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo);
                seriesInfo.SetMetadata(extractedAspectData);
            }
            return(seriesInfo != null && seriesInfo.IsCompleteMatch);
        }
        protected bool ExtractSeriesData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
            {
                return(false);
            }

            SeriesInfo seriesInfo;

            // Try to get extended information out of matroska files)
            MatroskaMatcher matroskaMatcher = new MatroskaMatcher();

            if (matroskaMatcher.MatchSeries(lfsra, out seriesInfo, ref extractedAspectData))
            {
                ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Found SeriesInformation by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, IsCompleteMatch {3}",
                                                          seriesInfo.Series, seriesInfo.ImdbId, seriesInfo.TvdbId, seriesInfo.IsCompleteMatch);
            }

            // If no information from mkv were found, try name matching
            if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
            {
                // Try to match series from folder and file namings
                SeriesMatcher seriesMatcher = new SeriesMatcher();
                seriesMatcher.MatchSeries(lfsra, out seriesInfo);
            }

            // Lookup online information (incl. fanart)
            if (seriesInfo != null && seriesInfo.IsCompleteMatch)
            {
                SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo);
                seriesInfo.SetMetadata(extractedAspectData);
            }
            return(seriesInfo != null && seriesInfo.IsCompleteMatch);
        }
예제 #3
0
        protected bool ExtractSeriesData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
            {
                return(false);
            }

            SeriesInfo seriesInfo = null;

            // First check if we already have a complete match from a previous MDE
            string            title;
            int               tvDbId;
            int               seasonNumber;
            IEnumerable <int> episodeNumbers;

            if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) &&
                MediaItemAspect.TryGetAttribute(extractedAspectData, SeriesAspect.ATTR_TVDB_ID, out tvDbId) &&
                MediaItemAspect.TryGetAttribute(extractedAspectData, SeriesAspect.ATTR_SEASON, out seasonNumber) &&
                (episodeNumbers = extractedAspectData[SeriesAspect.ASPECT_ID].GetCollectionAttribute <int>(SeriesAspect.ATTR_EPISODE)) != null)
            {
                seriesInfo = new SeriesInfo
                {
                    Series       = title,
                    TvdbId       = tvDbId,
                    SeasonNumber = seasonNumber,
                };
                episodeNumbers.ToList().ForEach(n => seriesInfo.EpisodeNumbers.Add(n));
            }

            // If there was no complete match, yet, try to get extended information out of matroska files)
            if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
            {
                MatroskaMatcher matroskaMatcher = new MatroskaMatcher();
                if (matroskaMatcher.MatchSeries(lfsra, out seriesInfo, ref extractedAspectData))
                {
                    ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Found SeriesInformation by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, IsCompleteMatch {3}",
                                                              seriesInfo.Series, seriesInfo.ImdbId, seriesInfo.TvdbId, seriesInfo.IsCompleteMatch);
                }
            }

            // If no information was found before, try name matching
            if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
            {
                // Try to match series from folder and file namings
                SeriesMatcher seriesMatcher = new SeriesMatcher();
                seriesMatcher.MatchSeries(lfsra, out seriesInfo);
            }

            // Lookup online information (incl. fanart)
            if (seriesInfo != null && seriesInfo.IsCompleteMatch)
            {
                SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo);
                if (!_onlyFanArt)
                {
                    seriesInfo.SetMetadata(extractedAspectData);
                }
            }
            return(seriesInfo != null && seriesInfo.IsCompleteMatch && !_onlyFanArt);
        }
예제 #4
0
        public void SeriesMatchesAreCorrect()
        {
            Init();

            List <string> shouldMatchPaths = new List <string>
            {
                @"\\SAGESERVER\TV Series\Almost Human\Almost.Human.S01E01.720p.HDTV.X264-DIMENSION.mkv",
                @"F:\TVSeries\The Bill Engvall Show\Season 1\The Bill Engvall Show - 101 - Good People.avi",
                @"G:\Serien\The Big Bang Theory\Staffel 1 [DEU-ENG][720p]\S01E01 Penny und die Physiker.mkv",
                @"G:\Serien\The Big Bang Theory\Staffel 1 [DEU-ENG][720p]\1x01 Penny und die Physiker.mkv",
                @"D:\Capture\EUReKA - Die geheime Stadt\EUReKA - Die geheime Stadt - (Lyncht Fargo) S4 E3 - 2011-05-02 - 21_15.ts",
                @"M:\Serien\Stargate Universe Season 1\Stargate Universe S01.E01_02 - Die Destiny.avi",
                @"M:\Serien\Caprica\Caprica 1x01 - Pilot (1).mkv",
                @"M:\\Serien\\Fringe Season 3\\Fringe S03E04 - Träumen Gestaltwandler von elektrischen Schafen.avi",
                @"\The.Vampire.Diaries.S03-E13.720p.WEB-DL.mkv",
                @"\Nikita.S02_E02.720p.WEB-DL.mkv",
                @"\Switched.at.Birth.S01E10.The Homecoming.720p.WEB-DL.DD5.1.H.264-NT.mkv",
                @"\The.Lying.Game.111.WEB.DL.mkv",
                @"\Unforgettable.S01E02.720p.WEB-DL.mkv",
                @"\Grimm.S01E06.720p.HDTV.X264-DIMENSION.mkv",
                @"\The.Secret.Circle.S01E01.720p.WEB-DL.mkv",
                @"\Lost.Girl.S02E06.720p.WEB-DL.mkv",
                @"\Fringe.S04E08.720p.WEB-DL.mkv",
                @"\the_fades.1x01.720p_hdtv_x264-fov.mkv",
                @"\Once.Upon.A.Time.S01E13.720p.WEB-DL.mkv",
                @"\The.Lying.Game.114.WEB.DL.mkv",
                @"X:\USB_Backup\Filme\CodecTest\Dr. House - S01E01 Irgendwas.mkv",
                @"Z:\Serien\Hawaii Five-O\Staffel 02\11 - In Der Falle.mkv",
                @"Z:\Serien\Hawaii Five-O\Season 02\11 - In Der Falle.mkv",
                @"Z:\Serien\Hawaii Five-O\02. Staffel\12 - In Der Falle.mkv",
                @"Z:\Serien\Hawaii Five-O\Temporada 02\11 - In Der Falle.mkv",
                @"Z:\Serien\Hawaii Five-O\02\11 - In Der Falle.mkv",
                //@"Z:\Serien\Hawaii Five-O\2\11 In Der Falle.mkv",
                @"Z:\Serien\The Mentalist\4\11 - Die Geister, Die Wir Riefen.mkv",
                //@"Z:\Serien\The Mentalist\4\11 Die Geister, Die Wir Riefen.mkv",
                @"\\BNAS\TV\All\Comedy\Farther Ted\Series 1\01 - Good Luke Father Ted.mkv",
                @"\\BNAS\TV\All\Comedy\Black Adder\Black Adder 1\01 - The Foretelling.avi",
            };

            SeriesMatcher matcher = new SeriesMatcher();

            foreach (string folderOrFileName in shouldMatchPaths)
            {
                SeriesInfo seriesInfo;
                bool       match = matcher.MatchSeries(folderOrFileName, out seriesInfo);
                Assert.IsTrue(match, string.Format("Failed to match path '{0}'", folderOrFileName));
            }
        }
예제 #5
0
        public void SeriesFalseMatches()
        {
            List <string> shouldNotMatchPaths = new List <string>
            {
                @"\\Fool's Gold (2008).mkv",
                @"Into the Blue (2005)",
                @"\\Iron Man 2 Disc 1.mkv",
                @"\\Salt - Blu-ray™.mkv",
                @"\\Transformers Revenge of the Fallen Disc 1.mkv",
                // FIXME: this one is currently wrongly matched
                // @"Polizeiruf 110\Polizeiruf 110 - SE - Liebeswahn (Das Erste HD - 2014-01-12 20_15).ts",
            };
            SeriesMatcher matcher = new SeriesMatcher();

            foreach (string folderOrFileName in shouldNotMatchPaths)
            {
                SeriesInfo seriesInfo;
                bool       match = matcher.MatchSeries(folderOrFileName, out seriesInfo);
                Assert.IsFalse(match, string.Format("Wrong match for '{0}' should not be matched!", folderOrFileName));
            }
        }
        protected bool ExtractSeriesData(string localFsResourcePath, IDictionary <Guid, MediaItemAspect> extractedAspectData)
        {
            SeriesInfo seriesInfo = null;

            string extensionUpper = StringUtils.TrimToEmpty(Path.GetExtension(localFsResourcePath)).ToUpper();

            // Try to get extended information out of matroska files)
            if (extensionUpper == ".MKV" || extensionUpper == ".MK3D")
            {
                MatroskaInfoReader mkvReader = new MatroskaInfoReader(localFsResourcePath);
                // Add keys to be extracted to tags dictionary, matching results will returned as value
                Dictionary <string, IList <string> > tagsToExtract = MatroskaConsts.DefaultTags;
                mkvReader.ReadTags(tagsToExtract);

                string         title = string.Empty;
                IList <string> tags  = tagsToExtract[MatroskaConsts.TAG_SIMPLE_TITLE];
                if (tags != null)
                {
                    title = tags.FirstOrDefault();
                }

                if (!string.IsNullOrEmpty(title))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);
                }

                string yearCandidate = null;
                tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_YEAR] ?? tagsToExtract[MatroskaConsts.TAG_SEASON_YEAR];
                if (tags != null)
                {
                    yearCandidate = (tags.FirstOrDefault() ?? string.Empty).Substring(0, 4);
                }

                int year;
                if (int.TryParse(yearCandidate, out year))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1));
                }

                tags = tagsToExtract[MatroskaConsts.TAG_EPISODE_SUMMARY];
                string plot = tags != null?tags.FirstOrDefault() : string.Empty;

                if (!string.IsNullOrEmpty(plot))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, plot);
                }

                // Series and episode handling. Prefer information from tags.
                seriesInfo = GetSeriesFromTags(tagsToExtract);
            }

            // If now information from mkv were found, try name matching
            if (seriesInfo == null || !seriesInfo.IsCompleteMatch)
            {
                // Try to match series from folder and file namings
                SeriesMatcher seriesMatcher = new SeriesMatcher();
                seriesMatcher.MatchSeries(localFsResourcePath, out seriesInfo);
            }

            // Lookup online information (incl. fanart)
            if (seriesInfo != null && seriesInfo.IsCompleteMatch)
            {
                SeriesTvDbMatcher.Instance.FindAndUpdateSeries(seriesInfo);
                seriesInfo.SetMetadata(extractedAspectData);
            }
            return(seriesInfo != null && seriesInfo.IsCompleteMatch);
        }
        public async Task <IList <MediaItemSearchResult> > SearchForMatchesAsync(IDictionary <Guid, IList <MediaItemAspect> > searchAspectData, ICollection <string> searchCategories)
        {
            try
            {
                if (!(searchCategories?.Contains(MEDIA_CATEGORY_NAME_SERIES) ?? true))
                {
                    return(null);
                }

                string searchData     = null;
                var    reimportAspect = MediaItemAspect.GetAspect(searchAspectData, ReimportAspect.Metadata);
                if (reimportAspect != null)
                {
                    searchData = reimportAspect.GetAttributeValue <string>(ReimportAspect.ATTR_SEARCH);
                }

                ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Search aspects to use: '{0}'", string.Join(",", searchAspectData.Keys));

                //Prepare search info
                EpisodeInfo episodeSearchinfo = null;
                SeriesInfo  seriesSearchinfo  = null;
                List <MediaItemSearchResult> searchResults = new List <MediaItemSearchResult>();
                if (!string.IsNullOrEmpty(searchData))
                {
                    if (searchAspectData.ContainsKey(VideoAspect.ASPECT_ID))
                    {
                        EpisodeInfo episode = new EpisodeInfo();
                        episode.FromMetadata(searchAspectData);

                        episodeSearchinfo = new EpisodeInfo();
                        episodeSearchinfo.SeasonNumber   = episode.SeasonNumber;
                        episodeSearchinfo.EpisodeNumbers = episode.EpisodeNumbers.ToList();
                        if (searchData.StartsWith("tt", StringComparison.InvariantCultureIgnoreCase) && !searchData.Contains(" ") && int.TryParse(searchData.Substring(2), out int id))
                        {
                            episodeSearchinfo.SeriesImdbId = searchData;
                        }
                        else if (!searchData.Contains(" ") && int.TryParse(searchData, out int tvDbSeriesId))
                        {
                            episodeSearchinfo.SeriesTvdbId = tvDbSeriesId;
                        }
                        else //Fallabck to name search
                        {
                            searchData = searchData.Trim();
                            SeriesMatcher seriesMatcher = new SeriesMatcher();
                            //Add extension to simulate a file name which the matcher expects
                            EpisodeInfo tempEpisodeInfo = new EpisodeInfo();
                            tempEpisodeInfo.SeasonNumber = episode.SeasonNumber;
                            if (seriesMatcher.MatchSeries(searchData + ".ext", tempEpisodeInfo))
                            {
                                episodeSearchinfo.SeriesName     = tempEpisodeInfo.SeriesName;
                                episodeSearchinfo.SeasonNumber   = tempEpisodeInfo.SeasonNumber;
                                episodeSearchinfo.EpisodeNumbers = tempEpisodeInfo.EpisodeNumbers.ToList();
                            }
                        }

                        ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Searching for episode matches on search: '{0}'", searchData);
                    }
                    else if (searchAspectData.ContainsKey(SeriesAspect.ASPECT_ID))
                    {
                        seriesSearchinfo = new SeriesInfo();
                        if (searchData.StartsWith("tt", StringComparison.InvariantCultureIgnoreCase) && !searchData.Contains(" ") && int.TryParse(searchData.Substring(2), out int id))
                        {
                            seriesSearchinfo.ImdbId = searchData;
                        }
                        else if (!searchData.Contains(" ") && int.TryParse(searchData, out int tvDbSeriesId))
                        {
                            seriesSearchinfo.TvdbId = tvDbSeriesId;
                        }
                        else //Fallabck to name search
                        {
                            searchData = searchData.Trim();
                            EpisodeInfo tempEpisodeInfo = new EpisodeInfo();
                            tempEpisodeInfo.SeasonNumber = 1;
                            SeriesMatcher seriesMatcher = new SeriesMatcher();
                            //Add extension to simulate a file name which the matcher expects
                            seriesMatcher.MatchSeries(searchData + " S01E01.ext", tempEpisodeInfo);
                            if (tempEpisodeInfo.SeriesFirstAired.HasValue)
                            {
                                seriesSearchinfo.SeriesName = tempEpisodeInfo.SeriesName;
                                seriesSearchinfo.FirstAired = tempEpisodeInfo.SeriesFirstAired;
                            }
                            else
                            {
                                seriesSearchinfo.SeriesName = searchData;
                            }

                            ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Searching for series matches on search: '{0}'", searchData);
                        }
                    }
                }
                else
                {
                    if (searchAspectData.ContainsKey(VideoAspect.ASPECT_ID))
                    {
                        episodeSearchinfo = new EpisodeInfo();
                        episodeSearchinfo.FromMetadata(searchAspectData);

                        ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Searching for episode matches on aspects");
                    }
                    else if (searchAspectData.ContainsKey(SeriesAspect.ASPECT_ID))
                    {
                        seriesSearchinfo = new SeriesInfo();
                        seriesSearchinfo.FromMetadata(searchAspectData);

                        ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Searching for series matches on aspects");
                    }
                }

                //Perform online search
                if (episodeSearchinfo != null)
                {
                    List <int> epNos   = new List <int>(episodeSearchinfo.EpisodeNumbers.OrderBy(e => e));
                    var        matches = await OnlineMatcherService.Instance.FindMatchingEpisodesAsync(episodeSearchinfo).ConfigureAwait(false);

                    ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Episode search returned {0} matches", matches.Count());
                    if (epNos.Count > 1)
                    {
                        //Check if double episode is in the search results
                        if (!matches.Any(e => e.EpisodeNumbers.SequenceEqual(epNos)))
                        {
                            //Add a double episode if it's not
                            var potentialEpisodes = new Dictionary <int, List <EpisodeInfo> >();
                            foreach (var episodeNo in epNos)
                            {
                                potentialEpisodes[episodeNo] = matches.Where(e => e.FirstEpisodeNumber == episodeNo && e.EpisodeNumbers.Count == 1).ToList();
                            }
                            //Merge fitting episodes
                            var mergedEpisodes = new List <EpisodeInfo>();
                            foreach (var episodeNo in epNos)
                            {
                                if (episodeNo == episodeSearchinfo.FirstEpisodeNumber)
                                {
                                    foreach (var episode in potentialEpisodes[episodeNo])
                                    {
                                        mergedEpisodes.Add(episode.Clone());
                                    }
                                }
                                else
                                {
                                    foreach (var mergedEpisode in mergedEpisodes)
                                    {
                                        var nextEpisode = potentialEpisodes[episodeNo].FirstOrDefault(e => e.SeriesTvdbId > 0 && e.SeriesTvdbId == mergedEpisode.SeriesTvdbId &&
                                                                                                      e.SeasonNumber == mergedEpisode.SeasonNumber);
                                        if (nextEpisode == null)
                                        {
                                            nextEpisode = potentialEpisodes[episodeNo].FirstOrDefault(e => !string.IsNullOrEmpty(e.SeriesImdbId) && e.SeriesImdbId.Equals(mergedEpisode.SeriesImdbId, StringComparison.InvariantCultureIgnoreCase) &&
                                                                                                      e.SeasonNumber == mergedEpisode.SeasonNumber);
                                        }
                                        if (nextEpisode == null)
                                        {
                                            nextEpisode = potentialEpisodes[episodeNo].FirstOrDefault(e => e.SeriesMovieDbId > 0 && e.SeriesMovieDbId == mergedEpisode.SeriesMovieDbId &&
                                                                                                      e.SeasonNumber == mergedEpisode.SeasonNumber);
                                        }
                                        if (nextEpisode != null)
                                        {
                                            MergeEpisodeDetails(mergedEpisode, nextEpisode);
                                        }
                                    }
                                }
                            }
                            //Add valid merged episodes to search result
                            var list = matches.ToList();
                            var validMergedEpisodes = mergedEpisodes.Where(e => e.EpisodeNumbers.SequenceEqual(epNos));
                            list.AddRange(validMergedEpisodes);
                            matches = list.AsEnumerable();

                            if (validMergedEpisodes.Count() > 0)
                            {
                                ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Added {0} multi-episodes to matches", validMergedEpisodes.Count());
                            }
                        }
                    }
                    foreach (var match in matches)
                    {
                        var result = new MediaItemSearchResult
                        {
                            Name = $"{match.SeriesName}{(match.SeriesFirstAired == null || match.SeriesName.Text.EndsWith($"({match.SeriesFirstAired.Value.Year})") ? "" : $" ({match.SeriesFirstAired.Value.Year})")}" +
                                   $" S{(match.SeasonNumber.HasValue ? match.SeasonNumber.Value.ToString("00") : "??")}{(match.EpisodeNumbers.Count > 0 ? string.Join("", match.EpisodeNumbers.Select(e => "E" + e.ToString("00"))) : "E??")}" +
                                   $"{(match.EpisodeName.IsEmpty ? "" : $": {match.EpisodeName.Text}")}",
                            Description = match.Summary.IsEmpty ? "" : match.Summary.Text,
                        };

                        //Add external Ids
                        if (match.TvdbId > 0)
                        {
                            result.ExternalIds.Add("thetvdb.com", match.TvdbId.ToString());
                        }
                        if (!string.IsNullOrEmpty(match.ImdbId))
                        {
                            result.ExternalIds.Add("imdb.com", match.ImdbId);
                        }
                        if (match.MovieDbId > 0)
                        {
                            result.ExternalIds.Add("themoviedb.org", match.MovieDbId.ToString());
                        }

                        //Assign aspects and remove unwanted aspects
                        match.SetMetadata(result.AspectData, true);
                        CleanReimportAspects(result.AspectData);

                        searchResults.Add(result);
                    }
                    return(searchResults);
                }
                else if (seriesSearchinfo != null)
                {
                    var matches = await OnlineMatcherService.Instance.FindMatchingSeriesAsync(seriesSearchinfo).ConfigureAwait(false);

                    ServiceRegistration.Get <ILogger>().Debug("SeriesMetadataExtractor: Series search returned {0} matches", matches.Count());
                    foreach (var match in matches)
                    {
                        var result = new MediaItemSearchResult
                        {
                            Name        = $"{match.SeriesName}{(match.FirstAired == null || match.SeriesName.Text.EndsWith($"({match.FirstAired.Value.Year})") ? "" : $" ({match.FirstAired.Value.Year})")}",
                            Description = match.Description.IsEmpty ? "" : match.Description.Text,
                        };

                        //Add external Ids
                        if (match.TvdbId > 0)
                        {
                            result.ExternalIds.Add("thetvdb.com", match.TvdbId.ToString());
                        }
                        if (!string.IsNullOrEmpty(match.ImdbId))
                        {
                            result.ExternalIds.Add("imdb.com", match.ImdbId);
                        }
                        if (match.MovieDbId > 0)
                        {
                            result.ExternalIds.Add("themoviedb.org", match.MovieDbId.ToString());
                        }

                        //Assign aspects and remove unwanted aspects
                        match.SetMetadata(result.AspectData, true);
                        CleanReimportAspects(result.AspectData);

                        searchResults.Add(result);
                    }
                    return(searchResults);
                }
        protected async Task <bool> ExtractSeriesDataAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID))
            {
                return(false);
            }

            bool isReimport = extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID);

            EpisodeInfo episodeInfo = new EpisodeInfo();

            episodeInfo.FromMetadata(extractedAspectData);

            if (!isReimport) //Ignore file based information for reimports because they might be the cause of the wrong match
            {
                // If there was no complete match, yet, try to get extended information out of matroska files)
                if (!episodeInfo.IsBaseInfoPresent || !episodeInfo.HasExternalId)
                {
                    try
                    {
                        MatroskaMatcher matroskaMatcher = new MatroskaMatcher();
                        if (await matroskaMatcher.MatchSeriesAsync(lfsra, episodeInfo).ConfigureAwait(false))
                        {
                            ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Found EpisodeInfo by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, TMDB {3}, AreReqiredFieldsFilled {4}",
                                                                      episodeInfo.SeriesName, episodeInfo.SeriesImdbId, episodeInfo.SeriesTvdbId, episodeInfo.SeriesMovieDbId, episodeInfo.IsBaseInfoPresent);
                        }
                    }
                    catch (Exception ex)
                    {
                        ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Exception reading matroska tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                    }
                }

                // If no information was found before, try name matching
                if (!episodeInfo.IsBaseInfoPresent)
                {
                    // Try to match series from folder and file naming
                    SeriesMatcher seriesMatcher = new SeriesMatcher();
                    seriesMatcher.MatchSeries(lfsra, episodeInfo);
                }

                //Prepare online search improvements
                if (episodeInfo.SeriesFirstAired == null)
                {
                    EpisodeInfo   tempEpisodeInfo = new EpisodeInfo();
                    SeriesMatcher seriesMatcher   = new SeriesMatcher();
                    seriesMatcher.MatchSeries(lfsra, tempEpisodeInfo);
                    if (tempEpisodeInfo.SeriesFirstAired.HasValue)
                    {
                        episodeInfo.SeriesFirstAired = tempEpisodeInfo.SeriesFirstAired;
                    }
                }
                if (string.IsNullOrEmpty(episodeInfo.SeriesAlternateName))
                {
                    var mediaItemPath = lfsra.CanonicalLocalResourcePath;
                    var seriesMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../");
                    episodeInfo.SeriesAlternateName = seriesMediaItemDirectoryPath.FileName;
                }
            }

            if (episodeInfo.Languages.Count == 0)
            {
                IList <MultipleMediaItemAspect> audioAspects;
                if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects))
                {
                    foreach (MultipleMediaItemAspect aspect in audioAspects)
                    {
                        string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE);
                        if (!string.IsNullOrEmpty(language) && !episodeInfo.Languages.Contains(language))
                        {
                            episodeInfo.Languages.Add(language);
                        }
                    }
                }
            }

            if (SkipOnlineSearches && !SkipFanArtDownload)
            {
                EpisodeInfo tempInfo = episodeInfo.Clone();
                await OnlineMatcherService.Instance.FindAndUpdateEpisodeAsync(tempInfo).ConfigureAwait(false);

                episodeInfo.CopyIdsFrom(tempInfo);
                episodeInfo.HasChanged = tempInfo.HasChanged;
            }
            else if (!SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.FindAndUpdateEpisodeAsync(episodeInfo).ConfigureAwait(false);
            }

            if (episodeInfo.EpisodeName.IsEmpty)
            {
                if (episodeInfo.EpisodeNumbers.Any())
                {
                    episodeInfo.EpisodeName = $"E{episodeInfo.EpisodeNumbers.First().ToString("000")}";
                }
            }

            //Send it to the videos section
            if (!SkipOnlineSearches && !episodeInfo.HasExternalId)
            {
                return(false);
            }

            if (episodeInfo.EpisodeNameSort.IsEmpty)
            {
                if (!episodeInfo.SeriesName.IsEmpty && episodeInfo.SeasonNumber.HasValue && episodeInfo.DvdEpisodeNumbers.Any())
                {
                    episodeInfo.EpisodeNameSort = $"{episodeInfo.SeriesName.Text} S{episodeInfo.SeasonNumber.Value.ToString("00")}E{episodeInfo.DvdEpisodeNumbers.First().ToString("000.000")}";
                }
                if (!episodeInfo.SeriesName.IsEmpty && episodeInfo.SeasonNumber.HasValue && episodeInfo.EpisodeNumbers.Any())
                {
                    episodeInfo.EpisodeNameSort = $"{episodeInfo.SeriesName.Text} S{episodeInfo.SeasonNumber.Value.ToString("00")}E{episodeInfo.EpisodeNumbers.First().ToString("000")}";
                }
                else if (!episodeInfo.EpisodeName.IsEmpty)
                {
                    episodeInfo.EpisodeNameSort = BaseInfo.GetSortTitle(episodeInfo.EpisodeName.Text);
                }
            }
            episodeInfo.SetMetadata(extractedAspectData);

            return(episodeInfo.IsBaseInfoPresent);
        }
        protected bool ExtractSeriesData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID))
            {
                return(false);
            }

            if (extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID) && !importOnly)
            {
                return(false); //Subtitles can only be imported not refreshed
            }
            bool refresh = false;

            if (extractedAspectData.ContainsKey(EpisodeAspect.ASPECT_ID))
            {
                refresh = true;
            }

            EpisodeInfo episodeInfo = new EpisodeInfo();

            if (refresh)
            {
                episodeInfo.FromMetadata(extractedAspectData);
            }
            ISeriesRelationshipExtractor.UpdateEpisodeSeries(extractedAspectData, episodeInfo);
            if (!episodeInfo.IsBaseInfoPresent)
            {
                string title = null;
                int    seasonNumber;
                SingleMediaItemAspect episodeAspect;
                MediaItemAspect.TryGetAspect(extractedAspectData, EpisodeAspect.Metadata, out episodeAspect);
                IEnumerable <int> episodeNumbers;
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_SERIES_NAME, out title) &&
                    MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_SEASON, out seasonNumber) &&
                    (episodeNumbers = episodeAspect.GetCollectionAttribute <int>(EpisodeAspect.ATTR_EPISODE)) != null)
                {
                    episodeInfo.SeriesName   = title;
                    episodeInfo.SeasonNumber = seasonNumber;
                    episodeInfo.EpisodeNumbers.Clear();
                    episodeNumbers.ToList().ForEach(n => episodeInfo.EpisodeNumbers.Add(n));
                }
            }

            // If there was no complete match, yet, try to get extended information out of matroska files)
            if (!episodeInfo.IsBaseInfoPresent || !episodeInfo.HasExternalId)
            {
                try
                {
                    MatroskaMatcher matroskaMatcher = new MatroskaMatcher();
                    if (matroskaMatcher.MatchSeries(lfsra, episodeInfo))
                    {
                        ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Found EpisodeInfo by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, TMDB {3}, AreReqiredFieldsFilled {4}",
                                                                  episodeInfo.SeriesName, episodeInfo.SeriesImdbId, episodeInfo.SeriesTvdbId, episodeInfo.SeriesMovieDbId, episodeInfo.IsBaseInfoPresent);
                    }
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Exception reading matroska tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            // If no information was found before, try name matching
            if (!episodeInfo.IsBaseInfoPresent)
            {
                // Try to match series from folder and file naming
                SeriesMatcher seriesMatcher = new SeriesMatcher();
                seriesMatcher.MatchSeries(lfsra, episodeInfo);
            }

            //Prepare online search improvements
            if (episodeInfo.SeriesFirstAired == null)
            {
                EpisodeInfo   tempEpisodeInfo = new EpisodeInfo();
                SeriesMatcher seriesMatcher   = new SeriesMatcher();
                seriesMatcher.MatchSeries(lfsra, tempEpisodeInfo);
                if (tempEpisodeInfo.SeriesFirstAired.HasValue)
                {
                    episodeInfo.SeriesFirstAired = tempEpisodeInfo.SeriesFirstAired;
                }
            }
            if (string.IsNullOrEmpty(episodeInfo.SeriesAlternateName))
            {
                var mediaItemPath = lfsra.CanonicalLocalResourcePath;
                var seriesMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../");
                episodeInfo.SeriesAlternateName = seriesMediaItemDirectoryPath.FileName;
            }

            if (episodeInfo.Languages.Count == 0)
            {
                IList <MultipleMediaItemAspect> audioAspects;
                if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects))
                {
                    foreach (MultipleMediaItemAspect aspect in audioAspects)
                    {
                        string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE);
                        if (!string.IsNullOrEmpty(language) && !episodeInfo.Languages.Contains(language))
                        {
                            episodeInfo.Languages.Add(language);
                        }
                    }
                }
            }

            episodeInfo.AssignNameId();

            if (SkipOnlineSearches && !SkipFanArtDownload)
            {
                EpisodeInfo tempInfo = episodeInfo.Clone();
                OnlineMatcherService.Instance.FindAndUpdateEpisode(tempInfo, importOnly);
                episodeInfo.CopyIdsFrom(tempInfo);
                episodeInfo.HasChanged = tempInfo.HasChanged;
            }
            else if (!SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.FindAndUpdateEpisode(episodeInfo, importOnly);
            }

            //Send it to the videos section
            if (!SkipOnlineSearches && !episodeInfo.HasExternalId)
            {
                return(false);
            }

            if (refresh)
            {
                if ((IncludeActorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ACTOR) && episodeInfo.Actors.Count > 0) ||
                    (IncludeCharacterDetails && !BaseInfo.HasRelationship(extractedAspectData, CharacterAspect.ROLE_CHARACTER) && episodeInfo.Characters.Count > 0) ||
                    (IncludeDirectorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_DIRECTOR) && episodeInfo.Directors.Count > 0) ||
                    (IncludeWriterDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_WRITER) && episodeInfo.Writers.Count > 0) ||
                    (!BaseInfo.HasRelationship(extractedAspectData, SeriesAspect.ROLE_SERIES) && !episodeInfo.SeriesName.IsEmpty) ||
                    (!BaseInfo.HasRelationship(extractedAspectData, SeasonAspect.ROLE_SEASON) && episodeInfo.SeasonNumber.HasValue))
                {
                    episodeInfo.HasChanged = true;
                }
            }

            if (!episodeInfo.HasChanged && !importOnly)
            {
                return(false);
            }

            episodeInfo.SetMetadata(extractedAspectData);

            return(episodeInfo.IsBaseInfoPresent);
        }