public virtual bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode)
        {
            IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor;

            if (fsra == null)
            {
                return(false);
            }
            if (!fsra.IsFile)
            {
                return(false);
            }
            string fileName = fsra.ResourceName;

            if (!HasAudioExtension(fileName))
            {
                return(false);
            }

            bool refresh = false;

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

            try
            {
                TrackInfo trackInfo = new TrackInfo();
                if (refresh)
                {
                    trackInfo.FromMetadata(extractedAspectData);
                }
                if (!trackInfo.IsBaseInfoPresent)
                {
                    File tag = null;
                    try
                    {
                        ByteVector.UseBrokenLatin1Behavior = true; // Otherwise we have problems retrieving non-latin1 chars
                        tag = File.Create(new ResourceProviderFileAbstraction(fsra));
                    }
                    catch (CorruptFileException)
                    {
                        // Only log at the info level here - And simply return false. This makes the importer know that we
                        // couldn't perform our task here.
                        ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Audio file '{0}' seems to be broken", fsra.CanonicalLocalResourcePath);
                        return(false);
                    }

                    using (tag)
                    {
                        // Some file extensions like .mp4 can contain audio and video. Do not handle files with video content here.
                        if (tag.Properties.VideoHeight > 0 && tag.Properties.VideoWidth > 0)
                        {
                            return(false);
                        }

                        fileName = ProviderPathHelper.GetFileNameWithoutExtension(fileName) ?? string.Empty;
                        string title;
                        string sortTitle;
                        string artist;
                        uint?  trackNo;
                        GuessMetadataFromFileName(fileName, out title, out artist, out trackNo);
                        if (!string.IsNullOrEmpty(title))
                        {
                            title = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(title.ToLowerInvariant());
                        }
                        if (!string.IsNullOrEmpty(artist))
                        {
                            artist = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(artist.ToLowerInvariant());
                        }

                        if (!string.IsNullOrEmpty(tag.Tag.Title))
                        {
                            title = tag.Tag.Title.Trim();
                        }

                        sortTitle = BaseInfo.GetSortTitle(title);
                        if (!string.IsNullOrEmpty(tag.Tag.TitleSort))
                        {
                            sortTitle = tag.Tag.TitleSort.Trim();
                        }

                        IEnumerable <string> artists;
                        if (tag.Tag.Performers.Length > 0)
                        {
                            artists = tag.Tag.Performers;
                            if ((tag.TagTypes & TagTypes.Id3v2) != 0)
                            {
                                artists = PatchID3v23Enumeration(artists);
                            }
                        }
                        else
                        {
                            artists = artist == null ? null : new string[] { artist.Trim() }
                        };
                        if (tag.Tag.Track != 0)
                        {
                            trackNo = tag.Tag.Track;
                        }

                        if (importOnly)
                        {
                            MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata);
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true);
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size);
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, fsra.CanonicalLocalResourcePath.Serialize());
                            // FIXME Albert: tag.MimeType returns taglib/mp3 for an MP3 file. This is not what we want and collides with the
                            // mimetype handling in the BASS player, which expects audio/xxx.
                            if (!string.IsNullOrWhiteSpace(tag.MimeType))
                            {
                                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, tag.MimeType.Replace("taglib/", "audio/"));
                            }

                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, sortTitle);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(tag.Tag.Comment));
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, fsra.LastChanged);
                        }

                        trackInfo.TrackName     = title;
                        trackInfo.TrackNameSort = sortTitle;
                        if (tag.Properties.Codecs.Count() > 0)
                        {
                            trackInfo.Encoding = tag.Properties.Codecs.First().Description;
                        }
                        if (tag.Properties.Duration.TotalSeconds != 0)
                        {
                            trackInfo.Duration = (long)tag.Properties.Duration.TotalSeconds;
                        }
                        if (tag.Properties.AudioBitrate != 0)
                        {
                            trackInfo.BitRate = (int)tag.Properties.AudioBitrate;
                        }
                        if (tag.Properties.AudioChannels != 0)
                        {
                            trackInfo.Channels = (int)tag.Properties.AudioChannels;
                        }
                        if (tag.Properties.AudioSampleRate != 0)
                        {
                            trackInfo.SampleRate = (int)tag.Properties.AudioSampleRate;
                        }

                        TagLib.Id3v2.Tag id3Tag = (TagLib.Id3v2.Tag)tag.GetTag(TagTypes.Id3v2, false);
                        if (id3Tag != null && !id3Tag.IsEmpty)
                        {
                            trackInfo.Compilation = id3Tag.IsCompilation;
                        }

                        trackInfo.Album = !string.IsNullOrEmpty(tag.Tag.Album) ? tag.Tag.Album.Trim() : null;
                        if (!string.IsNullOrEmpty(tag.Tag.AlbumSort))
                        {
                            IAudioRelationshipExtractor.StoreAlbum(extractedAspectData, tag.Tag.Album, tag.Tag.AlbumSort.Trim());
                        }

                        if (trackNo.HasValue)
                        {
                            trackInfo.TrackNum = (int)trackNo.Value;
                        }
                        if (tag.Tag.TrackCount != 0)
                        {
                            trackInfo.TotalTracks = (int)tag.Tag.TrackCount;
                        }
                        if (tag.Tag.Disc != 0)
                        {
                            trackInfo.DiscNum = (int)tag.Tag.Disc;
                        }
                        if (tag.Tag.DiscCount != 0)
                        {
                            trackInfo.TotalDiscs = (int)tag.Tag.DiscCount;
                        }
                        if (!string.IsNullOrEmpty(tag.Tag.Lyrics))
                        {
                            trackInfo.TrackLyrics = tag.Tag.Lyrics;
                        }

                        if (tag.Tag.TrackCount != 0)
                        {
                            trackInfo.TotalTracks = (int)tag.Tag.TrackCount;
                        }

                        if (!string.IsNullOrEmpty(tag.Tag.MusicBrainzTrackId))
                        {
                            trackInfo.MusicBrainzId = tag.Tag.MusicBrainzTrackId;
                        }
                        if (!string.IsNullOrEmpty(tag.Tag.MusicBrainzReleaseId))
                        {
                            trackInfo.AlbumMusicBrainzId = tag.Tag.MusicBrainzReleaseId;
                        }
                        if (!string.IsNullOrEmpty(tag.Tag.MusicBrainzDiscId))
                        {
                            trackInfo.AlbumMusicBrainzDiscId = tag.Tag.MusicBrainzDiscId;
                        }
                        if (!string.IsNullOrEmpty(tag.Tag.AmazonId))
                        {
                            trackInfo.AlbumAmazonId = tag.Tag.AmazonId;
                        }
                        if (!string.IsNullOrEmpty(tag.Tag.MusicIpId))
                        {
                            trackInfo.MusicIpId = tag.Tag.MusicIpId;
                        }

                        trackInfo.Artists = new List <PersonInfo>();
                        if (artists != null)
                        {
                            foreach (string artistName in ApplyAdditionalSeparator(artists))
                            {
                                trackInfo.Artists.Add(new PersonInfo()
                                {
                                    Name            = artistName.Trim(),
                                    Occupation      = PersonAspect.OCCUPATION_ARTIST,
                                    ParentMediaName = trackInfo.Album,
                                    MediaName       = trackInfo.TrackName
                                });
                            }
                        }

                        //Save id if possible
                        if (trackInfo.Artists.Count == 1 && !string.IsNullOrEmpty(tag.Tag.MusicBrainzArtistId))
                        {
                            trackInfo.Artists[0].MusicBrainzId = tag.Tag.MusicBrainzArtistId;
                        }

                        IEnumerable <string> albumArtists = tag.Tag.AlbumArtists;
                        if ((tag.TagTypes & TagTypes.Id3v2) != 0)
                        {
                            albumArtists = PatchID3v23Enumeration(albumArtists);
                        }
                        trackInfo.AlbumArtists = new List <PersonInfo>();
                        if (albumArtists != null)
                        {
                            foreach (string artistName in ApplyAdditionalSeparator(albumArtists))
                            {
                                trackInfo.AlbumArtists.Add(new PersonInfo()
                                {
                                    Name            = artistName.Trim(),
                                    Occupation      = PersonAspect.OCCUPATION_ARTIST,
                                    ParentMediaName = trackInfo.Album,
                                    MediaName       = trackInfo.TrackName
                                });
                            }
                        }

                        //Save id if possible
                        if (trackInfo.AlbumArtists.Count == 1 && !string.IsNullOrEmpty(tag.Tag.MusicBrainzReleaseArtistId))
                        {
                            trackInfo.AlbumArtists[0].MusicBrainzId = tag.Tag.MusicBrainzReleaseArtistId;
                        }

                        IEnumerable <string> composers = tag.Tag.Composers;
                        if ((tag.TagTypes & TagTypes.Id3v2) != 0)
                        {
                            composers = PatchID3v23Enumeration(composers);
                        }
                        trackInfo.Composers = new List <PersonInfo>();
                        if (composers != null)
                        {
                            foreach (string composerName in ApplyAdditionalSeparator(composers))
                            {
                                trackInfo.Composers.Add(new PersonInfo()
                                {
                                    Name            = composerName.Trim(),
                                    Occupation      = PersonAspect.OCCUPATION_COMPOSER,
                                    ParentMediaName = trackInfo.Album,
                                    MediaName       = trackInfo.TrackName
                                });
                            }
                        }

                        if (tag.Tag.Genres.Length > 0)
                        {
                            IEnumerable <string> genres = tag.Tag.Genres;
                            if ((tag.TagTypes & TagTypes.Id3v2) != 0)
                            {
                                genres = PatchID3v23Enumeration(genres);
                            }
                            trackInfo.Genres = ApplyAdditionalSeparator(genres).Select(s => new GenreInfo {
                                Name = s.Trim()
                            }).ToList();
                            OnlineMatcherService.Instance.AssignMissingMusicGenreIds(trackInfo.Genres);
                        }

                        int year = (int)tag.Tag.Year;
                        if (year >= 30 && year <= 99)
                        {
                            year += 1900;
                        }
                        if (year >= 1930 && year <= 2030)
                        {
                            trackInfo.ReleaseDate = new DateTime(year, 1, 1);
                        }

                        if (!trackInfo.HasThumbnail)
                        {
                            // The following code gets cover art images from file (embedded) or from windows explorer cache (supports folder.jpg).
                            IPicture[] pics = tag.Tag.Pictures;
                            if (pics.Length > 0)
                            {
                                try
                                {
                                    using (MemoryStream stream = new MemoryStream(pics[0].Data.Data))
                                    {
                                        trackInfo.Thumbnail  = stream.ToArray();
                                        trackInfo.HasChanged = true;
                                    }
                                }
                                // Decoding of invalid image data can fail, but main MediaItem is correct.
                                catch { }
                            }
                            else
                            {
                                // In quick mode only allow thumbs taken from cache.
                                bool cachedOnly = importOnly || forceQuickMode;

                                // Thumbnail extraction
                                fileName = mediaItemAccessor.ResourcePathName;
                                IThumbnailGenerator generator = ServiceRegistration.Get <IThumbnailGenerator>();
                                byte[]    thumbData;
                                ImageType imageType;
                                if (generator.GetThumbnail(fileName, cachedOnly, out thumbData, out imageType))
                                {
                                    trackInfo.Thumbnail  = thumbData;
                                    trackInfo.HasChanged = true;
                                }
                            }
                        }
                    }

                    if (string.IsNullOrEmpty(trackInfo.Album) || trackInfo.Artists.Count == 0)
                    {
                        MusicNameMatcher.MatchTrack(fileName, trackInfo);
                    }
                }

                //Determine compilation
                if (importOnly && !trackInfo.Compilation)
                {
                    if (trackInfo.AlbumArtists.Count > 0 &&
                        (trackInfo.AlbumArtists[0].Name.IndexOf("Various", StringComparison.InvariantCultureIgnoreCase) >= 0 ||
                         trackInfo.AlbumArtists[0].Name.Equals("VA", StringComparison.InvariantCultureIgnoreCase)))
                    {
                        trackInfo.Compilation = true;
                    }
                    else
                    {
                        //Look for itunes compilation folder
                        var mediaItemPath = mediaItemAccessor.CanonicalLocalResourcePath;
                        var albumMediaItemDirectoryPath  = ResourcePathHelper.Combine(mediaItemPath, "../");
                        var artistMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../");

                        if (IsDiscFolder(trackInfo.Album, albumMediaItemDirectoryPath.FileName))
                        {
                            //Probably a CD folder so try next parent
                            artistMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../../");
                        }
                        if (artistMediaItemDirectoryPath.FileName.IndexOf("Compilation", StringComparison.InvariantCultureIgnoreCase) >= 0)
                        {
                            trackInfo.Compilation = true;
                        }
                    }
                }

                if (!refresh)
                {
                    //Check artists
                    trackInfo.Artists      = GetCorrectedArtistsList(trackInfo, trackInfo.Artists);
                    trackInfo.AlbumArtists = GetCorrectedArtistsList(trackInfo, trackInfo.AlbumArtists);
                }

                trackInfo.AssignNameId();

                if (!forceQuickMode)
                {
                    AudioCDMatcher.GetDiscMatchAndUpdate(mediaItemAccessor.ResourcePathName, trackInfo);

                    if (SkipOnlineSearches && !SkipFanArtDownload)
                    {
                        TrackInfo tempInfo = trackInfo.Clone();
                        OnlineMatcherService.Instance.FindAndUpdateTrack(tempInfo, importOnly);
                        trackInfo.CopyIdsFrom(tempInfo);
                        trackInfo.HasChanged = tempInfo.HasChanged;
                    }
                    else if (!SkipOnlineSearches)
                    {
                        OnlineMatcherService.Instance.FindAndUpdateTrack(trackInfo, importOnly);
                    }
                }

                if (refresh)
                {
                    if ((IncludeArtistDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ARTIST) && trackInfo.Artists.Count > 0) ||
                        (IncludeArtistDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ALBUMARTIST) && trackInfo.AlbumArtists.Count > 0) ||
                        (IncludeComposerDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_COMPOSER) && trackInfo.Composers.Count > 0))
                    {
                        trackInfo.HasChanged = true;
                    }
                }

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

                trackInfo.SetMetadata(extractedAspectData);

                if (importOnly)
                {
                    //Store metadata for the Relationship Extractors
                    if (IncludeArtistDetails)
                    {
                        IAudioRelationshipExtractor.StorePersons(extractedAspectData, trackInfo.Artists, false);
                        IAudioRelationshipExtractor.StorePersons(extractedAspectData, trackInfo.AlbumArtists, true);
                    }
                    if (IncludeComposerDetails)
                    {
                        IAudioRelationshipExtractor.StorePersons(extractedAspectData, trackInfo.Composers, false);
                    }
                }

                return(trackInfo.IsBaseInfoPresent);
            }
            catch (UnsupportedFormatException)
            {
                ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Unsupported audio file '{0}'", fsra.CanonicalLocalResourcePath);
                return(false);
            }
            catch (Exception e)
            {
                // Only log at the info level here - And simply return false. This makes the importer know that we
                // couldn't perform our task here
                ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message);
            }
            return(false);
        }
        public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects)
        {
            extractedLinkedAspects = null;

            MovieInfo movieInfo = new MovieInfo();

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

            MovieCollectionInfo cachedCollection;
            Guid collectionId;
            MovieCollectionInfo collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>();

            if (TryGetInfoFromCache(collectionInfo, out cachedCollection, out collectionId))
            {
                collectionInfo = cachedCollection;
            }
            else if (!MovieMetadataExtractor.SkipOnlineSearches && collectionInfo.HasExternalId)
            {
                OnlineMatcherService.Instance.UpdateCollection(collectionInfo, false, false);
            }

            bool hasId = false;

            if (!MovieMetadataExtractor.SkipOnlineSearches)
            {
                hasId = collectionInfo.HasExternalId || !string.IsNullOrEmpty(movieInfo.CollectionNameId);
            }
            else
            {
                hasId = !string.IsNullOrEmpty(movieInfo.CollectionNameId);
            }

            if (!BaseInfo.HasRelationship(aspects, LinkedRole) && hasId)
            {
                collectionInfo.HasChanged = true; //Force save if no relationship exists
            }
            if (!collectionInfo.HasChanged && !importOnly)
            {
                return(false);
            }

            extractedLinkedAspects = new List <RelationshipItem>();

            IDictionary <Guid, IList <MediaItemAspect> > collectionAspects = new Dictionary <Guid, IList <MediaItemAspect> >();

            if (collectionId != Guid.Empty)
            {
                collectionInfo.SetMetadata(collectionAspects);

                bool movieVirtual = true;
                if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out movieVirtual))
                {
                    MediaItemAspect.SetAttribute(collectionAspects, MediaAspect.ATTR_ISVIRTUAL, movieVirtual);
                }

                if (collectionAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID))
                {
                    extractedLinkedAspects.Add(new RelationshipItem(collectionAspects, collectionId));
                }
            }
            else
            {
                //Create custom collection
                if (!string.IsNullOrEmpty(movieInfo.CollectionNameId) && !collectionInfo.HasExternalId)
                {
                    collectionInfo            = movieInfo.CloneBasicInstance <MovieCollectionInfo>();
                    collectionInfo.HasChanged = true;
                }
                collectionInfo.SetMetadata(collectionAspects);

                bool movieVirtual = true;
                if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out movieVirtual))
                {
                    MediaItemAspect.SetAttribute(collectionAspects, MediaAspect.ATTR_ISVIRTUAL, movieVirtual);
                }

                if (collectionAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID))
                {
                    extractedLinkedAspects.Add(new RelationshipItem(collectionAspects, Guid.Empty));
                }
            }

            return(extractedLinkedAspects.Count > 0);
        }
        public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects)
        {
            extractedLinkedAspects = null;

            EpisodeInfo episodeInfo = new EpisodeInfo();

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

            if (CheckCacheContains(episodeInfo))
            {
                return(false);
            }

            SeriesInfo cachedSeries;
            Guid       seriesId;
            SeriesInfo seriesInfo = episodeInfo.CloneBasicInstance <SeriesInfo>();

            if (TryGetInfoFromCache(seriesInfo, out cachedSeries, out seriesId))
            {
                seriesInfo = cachedSeries;
            }
            else if (!SeriesMetadataExtractor.SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.UpdateSeries(seriesInfo, false, importOnly);
            }

            if (!BaseInfo.HasRelationship(aspects, LinkedRole))
            {
                seriesInfo.HasChanged = true; //Force save if no relationship exists
            }
            if (!seriesInfo.HasChanged && !importOnly)
            {
                return(false);
            }

            AddToCheckCache(episodeInfo);

            extractedLinkedAspects = new List <RelationshipItem>();
            IDictionary <Guid, IList <MediaItemAspect> > seriesAspects = 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 (seriesId != Guid.Empty)
            {
                extractedLinkedAspects.Add(new RelationshipItem(seriesAspects, seriesId));
            }
            else
            {
                extractedLinkedAspects.Add(new RelationshipItem(seriesAspects, Guid.Empty));
            }
            return(true);
        }
        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);
        }
예제 #5
0
        private bool ExtractMovieData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly)
        {
            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() };
            string   title       = null;
            string   sortTitle   = null;

            // 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(MovieAspect.ASPECT_ID))
            {
                refresh = true;
            }

            MovieInfo movieInfo = new MovieInfo();

            if (refresh)
            {
                movieInfo.FromMetadata(extractedAspectData);
            }

            if (movieInfo.MovieName.IsEmpty)
            {
                //Try to get title
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) &&
                    !string.IsNullOrEmpty(title) && !lfsra.ResourceName.StartsWith(title, StringComparison.InvariantCultureIgnoreCase))
                {
                    movieInfo.MovieName = title;
                    /* Clear the names from unwanted strings */
                    MovieNameMatcher.CleanupTitle(movieInfo);
                }
            }
            if (movieInfo.MovieNameSort.IsEmpty)
            {
                //Try to get sort title
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, out sortTitle) && !string.IsNullOrEmpty(sortTitle))
                {
                    movieInfo.MovieNameSort = sortTitle;
                }
            }

            if (movieInfo.MovieDbId == 0)
            {
                try
                {
                    // Try to use an existing TMDB id for exact mapping
                    string tmdbId;
                    if (MatroskaMatcher.TryMatchTmdbId(lfsra, out tmdbId))
                    {
                        movieInfo.MovieDbId = Convert.ToInt32(tmdbId);
                    }
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading TMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            if (string.IsNullOrEmpty(movieInfo.ImdbId))
            {
                try
                {
                    // Try to use an existing IMDB id for exact mapping
                    string imdbId = null;
                    if (pathsToTest.Any(path => MatroskaMatcher.TryMatchImdbId(lfsra, out imdbId)))
                    {
                        movieInfo.ImdbId = imdbId;
                    }
                    else if (pathsToTest.Any(path => ImdbIdMatcher.TryMatchImdbId(path, out imdbId)))
                    {
                        movieInfo.ImdbId = imdbId;
                    }
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading IMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            if (!movieInfo.IsBaseInfoPresent)
            {
                // Also test the full path year. This is useful if the path contains the real name and year.
                foreach (string path in pathsToTest)
                {
                    if (MovieNameMatcher.MatchTitleYear(path, movieInfo))
                    {
                        break;
                    }
                }
                //Fall back to MediaAspect.ATTR_TITLE
                if (movieInfo.MovieName.IsEmpty && !string.IsNullOrEmpty(title))
                {
                    movieInfo.MovieName = title;
                }

                /* Clear the names from unwanted strings */
                MovieNameMatcher.CleanupTitle(movieInfo);
            }

            if (!movieInfo.ReleaseDate.HasValue && !movieInfo.HasExternalId)
            {
                // When searching movie title, the year can be relevant for multiple titles with same name but different years
                DateTime recordingDate;
                if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, out recordingDate))
                {
                    movieInfo.ReleaseDate = recordingDate;
                }
            }

            // Allow the online lookup to choose best matching language for metadata
            if (movieInfo.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) && !movieInfo.Languages.Contains(language))
                        {
                            movieInfo.Languages.Add(language);
                        }
                    }
                }
            }

            if (importOnly)
            {
                try
                {
                    MatroskaMatcher.ExtractFromTags(lfsra, movieInfo);
                    MP4Matcher.ExtractFromTags(lfsra, movieInfo);
                }
                catch (Exception ex)
                {
                    ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath);
                }
            }

            if (SkipOnlineSearches && !SkipFanArtDownload)
            {
                MovieInfo tempInfo = movieInfo.Clone();
                OnlineMatcherService.Instance.FindAndUpdateMovie(tempInfo, importOnly);
                movieInfo.CopyIdsFrom(tempInfo);
                movieInfo.HasChanged = tempInfo.HasChanged;
            }
            else if (!SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.FindAndUpdateMovie(movieInfo, importOnly);
            }

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

            if (importOnly)
            {
                //Create custom collection (overrides online collection)
                MovieCollectionInfo collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>();
                string collectionName;
                if (string.IsNullOrEmpty(collectionInfo.NameId) && CollectionFolderHasFanArt(lfsra, out collectionName))
                {
                    collectionInfo = new MovieCollectionInfo();
                    collectionInfo.CollectionName = collectionName;
                    if (!collectionInfo.CollectionName.IsEmpty)
                    {
                        movieInfo.CollectionName = collectionInfo.CollectionName;
                        movieInfo.CopyIdsFrom(collectionInfo); //Reset ID's
                        movieInfo.HasChanged = true;
                    }
                }
            }
            movieInfo.AssignNameId();

            if (refresh)
            {
                if ((IncludeActorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ACTOR) && movieInfo.Actors.Count > 0) ||
                    (IncludeCharacterDetails && !BaseInfo.HasRelationship(extractedAspectData, CharacterAspect.ROLE_CHARACTER) && movieInfo.Characters.Count > 0) ||
                    (IncludeDirectorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_DIRECTOR) && movieInfo.Directors.Count > 0) ||
                    (IncludeWriterDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_WRITER) && movieInfo.Writers.Count > 0) ||
                    (IncludeProductionCompanyDetails && !BaseInfo.HasRelationship(extractedAspectData, CompanyAspect.ROLE_COMPANY) && movieInfo.ProductionCompanies.Count > 0))
                {
                    movieInfo.HasChanged = true;
                }
            }

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

            movieInfo.SetMetadata(extractedAspectData);

            return(movieInfo.IsBaseInfoPresent);
        }
        public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects)
        {
            extractedLinkedAspects = null;

            TrackInfo trackInfo = new TrackInfo();

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

            if (CheckCacheContains(trackInfo))
            {
                return(false);
            }

            IList <Guid> linkedIds;
            Guid         albumId = BaseInfo.TryGetLinkedIds(aspects, LinkedRole, out linkedIds) ? linkedIds[0] : Guid.Empty;

            AlbumInfo cachedAlbum;
            Guid      cachedId;
            AlbumInfo albumInfo = trackInfo.CloneBasicInstance <AlbumInfo>();

            UpdateAlbum(aspects, albumInfo);
            UpdatePersons(aspects, albumInfo.Artists, true);
            if (TryGetInfoFromCache(albumInfo, out cachedAlbum, out cachedId))
            {
                albumInfo = cachedAlbum;
                if (albumId == Guid.Empty)
                {
                    albumId = cachedId;
                }
            }
            else if (!AudioMetadataExtractor.SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.UpdateAlbum(albumInfo, false, importOnly);
            }

            if (!BaseInfo.HasRelationship(aspects, LinkedRole))
            {
                albumInfo.HasChanged = true; //Force save if no relationship exists
            }
            if (!albumInfo.HasChanged && !importOnly)
            {
                return(false);
            }

            AddToCheckCache(trackInfo);

            extractedLinkedAspects = new List <RelationshipItem>();
            IDictionary <Guid, IList <MediaItemAspect> > albumAspects = new Dictionary <Guid, IList <MediaItemAspect> >();

            albumInfo.SetMetadata(albumAspects);

            bool trackVirtual = true;

            if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out trackVirtual))
            {
                MediaItemAspect.SetAttribute(albumAspects, MediaAspect.ATTR_ISVIRTUAL, trackVirtual);
            }

            byte[] data;
            if (MediaItemAspect.TryGetAttribute(aspects, ThumbnailLargeAspect.ATTR_THUMBNAIL, out data))
            {
                //Use image from track as image
                MediaItemAspect.SetAttribute(albumAspects, ThumbnailLargeAspect.ATTR_THUMBNAIL, data);
            }

            if (importOnly)
            {
                StorePersons(albumAspects, albumInfo.Artists, true);
            }

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

            if (albumId != Guid.Empty)
            {
                extractedLinkedAspects.Add(new RelationshipItem(albumAspects, albumId, albumInfo.HasChanged));
            }
            else
            {
                extractedLinkedAspects.Add(new RelationshipItem(albumAspects, Guid.Empty));
            }
            return(true);
        }