Exemple #1
0
 /// <summary>
 /// Tries to write metadata into <see cref="GenreAspect.ATTR_GENRE"/>
 /// </summary>
 /// <param name="extractedAspectData">Dictionary of <see cref="MediaItemAspect"/>s to write into</param>
 /// <returns><c>true</c> if any information was written; otherwise <c>false</c></returns>
 private bool TryWriteAlbumAspectGenres(IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
 {
     if (_stubs[0].Genres != null && _stubs[0].Genres.Any())
     {
         List <GenreInfo> genres = _stubs[0].Genres.Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo {
             Name = s.Trim()
         }).ToList();
         IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
         foreach (var genre in genres)
         {
             if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId))
             {
                 genre.Id = genreId;
             }
         }
         foreach (GenreInfo genre in genres)
         {
             MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata);
             genreAspect.SetAttribute(GenreAspect.ATTR_ID, genre.Id);
             genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre.Name);
         }
         return(true);
     }
     return(false);
 }
Exemple #2
0
        public EpisodeInfo GetSeriesFromTags(IDictionary metadata)
        {
            EpisodeInfo episodeInfo = new EpisodeInfo();
            string      tmpString;

            if (TryGet(metadata, TAG_TITLE, out tmpString))
            {
                episodeInfo.SeriesName.Text = tmpString;
            }

            if (TryGet(metadata, TAG_EPISODENAME, out tmpString))
            {
                episodeInfo.EpisodeName.Text = tmpString;
            }

            if (TryGet(metadata, TAG_GENRE, out tmpString))
            {
                episodeInfo.Genres = new List <GenreInfo>(tmpString.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenreInfo {
                    Name = s.Trim()
                }));
                IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                foreach (var genre in episodeInfo.Genres)
                {
                    if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId))
                    {
                        genre.Id = genreId;
                    }
                }
            }

            episodeInfo.HasChanged = true;
            return(episodeInfo);
        }
        public virtual async Task <bool> UpdateCollectionAsync(MovieCollectionInfo movieCollectionInfo, bool updateMovieList)
        {
            try
            {
                // Try online lookup
                if (!await InitAsync().ConfigureAwait(false))
                {
                    return(false);
                }

                TLang language = FindBestMatchingLanguage(movieCollectionInfo.Languages);
                bool  updated  = false;
                MovieCollectionInfo movieCollectionMatch = movieCollectionInfo.Clone();
                movieCollectionMatch.Movies.Clear();
                //Try updating from cache
                if (!await _wrapper.UpdateFromOnlineMovieCollectionAsync(movieCollectionMatch, language, true).ConfigureAwait(false))
                {
                    Logger.Debug(_id + ": Search for collection {0} online", movieCollectionInfo.ToString());

                    //Try to update movie collection information from online source
                    if (await _wrapper.UpdateFromOnlineMovieCollectionAsync(movieCollectionMatch, language, false).ConfigureAwait(false))
                    {
                        updated = true;
                    }
                }
                else
                {
                    Logger.Debug(_id + ": Found collection {0} in cache", movieCollectionInfo.ToString());
                    updated = true;
                }

                if (updated)
                {
                    movieCollectionInfo.MergeWith(movieCollectionMatch, true, updateMovieList);

                    if (updateMovieList)
                    {
                        foreach (MovieInfo movie in movieCollectionMatch.Movies)
                        {
                            IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                            foreach (var genre in movie.Genres)
                            {
                                if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId))
                                {
                                    genre.Id = genreId;
                                }
                            }
                        }
                    }
                }

                return(updated);
            }
            catch (Exception ex)
            {
                Logger.Debug(_id + ": Exception while processing collection {0}", ex, movieCollectionInfo.ToString());
                return(false);
            }
        }
Exemple #4
0
        public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects)
        {
            TrackInfo trackInfo = new TrackInfo();

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

            AlbumInfo reimport = null;

            if (aspects.ContainsKey(ReimportAspect.ASPECT_ID))
            {
                reimport = trackInfo.CloneBasicInstance <AlbumInfo>();
            }

            IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData = extractedLinkedAspects.Count > 0 ?
                                                                               extractedLinkedAspects[0] : new Dictionary <Guid, IList <MediaItemAspect> >();

            if (!await TryExtractAlbumMetadataAsync(mediaItemAccessor, extractedAspectData, reimport).ConfigureAwait(false))
            {
                return(false);
            }

            AlbumInfo albumInfo = new AlbumInfo();

            if (!albumInfo.FromMetadata(extractedAspectData))
            {
                return(false);
            }

            IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();

            foreach (var genre in albumInfo.Genres)
            {
                if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId))
                {
                    genre.Id = genreId;
                }
            }
            albumInfo.SetMetadata(extractedAspectData);

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

            bool trackVirtual;

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

            extractedLinkedAspects.Clear();
            extractedLinkedAspects.Add(extractedAspectData);
            return(true);
        }
        public EpisodeInfo GetSeriesFromTags(Argus.Recording recording)
        {
            EpisodeInfo episodeInfo = new EpisodeInfo {
                SeriesName = recording.Title
            };

            if (recording.SeriesNumber.HasValue)
            {
                episodeInfo.SeasonNumber = recording.SeriesNumber.Value;
            }

            if (recording.EpisodeNumber.HasValue)
            {
                episodeInfo.EpisodeNumbers.Add(recording.EpisodeNumber.Value);
            }

            if (!episodeInfo.IsBaseInfoPresent)
            {
                // Check for formatted display value, i.e.:
                // <EpisodeNumberDisplay>1.4</EpisodeNumberDisplay>
                if (!string.IsNullOrWhiteSpace(recording.EpisodeNumberDisplay))
                {
                    var parts = recording.EpisodeNumberDisplay.Split('.');
                    if (parts.Length == 2)
                    {
                        int val;
                        if (int.TryParse(parts[0], out val))
                        {
                            episodeInfo.SeasonNumber = val;
                        }
                        if (int.TryParse(parts[1], out val))
                        {
                            episodeInfo.EpisodeNumbers.Add(val);
                        }
                    }
                }
            }

            if (!string.IsNullOrEmpty(recording.Category?.Trim()))
            {
                episodeInfo.Genres.Add(new GenreInfo {
                    Name = recording.Category.Trim()
                });
                IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                foreach (var genre in episodeInfo.Genres)
                {
                    if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId))
                    {
                        genre.Id = genreId;
                    }
                }
            }

            episodeInfo.HasChanged = true;
            return(episodeInfo);
        }
        public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects)
        {
            EpisodeInfo episodeInfo = new EpisodeInfo();

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

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

            if (!SeriesMetadataExtractor.SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.UpdateSeriesAsync(seriesInfo, false, _category).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);
        }
Exemple #7
0
        public EpisodeInfo GetSeriesFromTags(Tags extractedTags)
        {
            EpisodeInfo episodeInfo = new EpisodeInfo();
            string      tmpString;
            int         tmpInt;

            if (TryGet(extractedTags, TAG_TITLE, out tmpString))
            {
                episodeInfo.SeriesName = tmpString;
            }

            if (TryGet(extractedTags, TAG_EPISODENAME, out tmpString))
            {
                episodeInfo.EpisodeName = tmpString;
            }

            if (TryGet(extractedTags, TAG_SERIESNUM, out tmpString) && int.TryParse(tmpString, out tmpInt))
            {
                episodeInfo.SeasonNumber = tmpInt;
            }

            if (TryGet(extractedTags, TAG_EPISODENUM, out tmpString))
            {
                int episodeNum;
                if (int.TryParse(tmpString, out episodeNum))
                {
                    episodeInfo.EpisodeNumbers.Add(episodeNum);
                }
            }

            if (TryGet(extractedTags, TAG_GENRE, out tmpString) && !string.IsNullOrEmpty(tmpString?.Trim()))
            {
                episodeInfo.Genres = new List <GenreInfo>(new GenreInfo[] { new GenreInfo {
                                                                                Name = tmpString.Trim()
                                                                            } });
                IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                foreach (var genre in episodeInfo.Genres)
                {
                    if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId))
                    {
                        genre.Id = genreId;
                    }
                }
            }

            episodeInfo.HasChanged = true;
            return(episodeInfo);
        }
        public virtual Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            try
            {
                IResourceAccessor metaFileAccessor;
                if (!CanExtract(mediaItemAccessor, extractedAspectData, out metaFileAccessor))
                {
                    return(Task.FromResult(false));
                }

                Argus.Recording recording;
                using (metaFileAccessor)
                {
                    using (Stream metaStream = ((IFileSystemResourceAccessor)metaFileAccessor).OpenRead())
                        recording = (Argus.Recording)GetTagsXmlSerializer().Deserialize(metaStream);
                }

                // Force MimeType
                IList <MultipleMediaItemAspect> providerAspects;
                MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerAspects);
                foreach (MultipleMediaItemAspect aspect in providerAspects)
                {
                    aspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/arg");
                }

                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false);

                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, recording.Title);
                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(recording.Title));

                if (!string.IsNullOrEmpty(recording.Category?.Trim()))
                {
                    List <GenreInfo> genreList = new List <GenreInfo>(new GenreInfo[] { new GenreInfo {
                                                                                            Name = recording.Category.Trim()
                                                                                        } });
                    IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                    foreach (var genre in genreList)
                    {
                        if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId))
                        {
                            genre.Id = genreId;
                        }
                    }
                    if (genreList.Count > 0)
                    {
                        MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata);
                        genreAspect.SetAttribute(GenreAspect.ATTR_ID, genreList[0].Id);
                        genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genreList[0].Name);
                    }
                }

                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, recording.Description);

                Match yearMatch = _yearMatcher.Match(recording.Description);
                int   guessedYear;
                if (int.TryParse(yearMatch.Value, out guessedYear))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(guessedYear, 1, 1));
                }

                MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, recording.ChannelDisplayName);

                MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, recording.ProgramStartTime);

                MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, recording.ProgramStopTime);
                RecordingUtils.CheckAndPrepareAspectRefresh(extractedAspectData);

                if (!string.IsNullOrWhiteSpace(recording.Director))
                {
                    MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_DIRECTORS, recording.Director.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
                }

                if (!string.IsNullOrWhiteSpace(recording.Actors))
                {
                    MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_ACTORS, recording.Actors.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
                }

                return(Task.FromResult(true));
            }
            catch (Exception e)
            {
                // Only log at the info level here - And simply return false. This lets the caller know that we
                // couldn't perform our task here.
                ServiceRegistration.Get <ILogger>().Info("ArgusRecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message);
            }
            return(Task.FromResult(false));
        }
Exemple #9
0
        protected virtual Task <bool> ExtractMetadataAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            if (!CanExtract(lfsra, extractedAspectData))
            {
                return(Task.FromResult(false));
            }

            using (var rec = new MCRecMetadataEditor(lfsra.LocalFileSystemPath))
            {
                // Handle series information
                IDictionary tags = rec.GetAttributes();

                // Force MimeType
                IList <MultipleMediaItemAspect> providerAspects;
                MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerAspects);
                foreach (MultipleMediaItemAspect aspect in providerAspects)
                {
                    aspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/wtv");
                }

                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false);

                string value;
                if (TryGet(tags, TAG_TITLE, out value) && !string.IsNullOrEmpty(value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, value);
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(value));
                }

                if (TryGet(tags, TAG_GENRE, out value))
                {
                    List <GenreInfo> genreList = new List <GenreInfo>(value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenreInfo {
                        Name = s.Trim()
                    }));
                    IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                    foreach (var genre in genreList)
                    {
                        if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId))
                        {
                            genre.Id = genreId;
                        }
                    }
                    foreach (GenreInfo genre in genreList)
                    {
                        MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata);
                        genreAspect.SetAttribute(GenreAspect.ATTR_ID, genre.Id);
                        genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre.Name);
                    }
                }

                if (TryGet(tags, TAG_PLOT, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, value);
                }

                if (TryGet(tags, TAG_ORIGINAL_TIME, out value))
                {
                    DateTime origTime;
                    if (DateTime.TryParse(value, out origTime))
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, origTime);
                    }
                }

                if (TryGet(tags, TAG_CHANNEL, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, value);
                }

                long lValue;
                if (TryGet(tags, TAG_STARTTIME, out lValue))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, FromMCEFileTime(lValue));
                }
                if (TryGet(tags, TAG_ENDTIME, out lValue))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, FromMCEFileTime(lValue));
                }
            }
            return(Task.FromResult(true));
        }
        protected override void InitGenreMap()
        {
            if (_tvGenresInited)
            {
                return;
            }

            _tvGenresInited = true;

            string          genre;
            bool            enabled;
            IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();

            if (converter == null)
            {
                return;
            }

            // Get the id of the mp genre identified as the movie genre.
            int genreMapMovieGenreId;

            if (!int.TryParse(_tvBusiness.GetSetting("genreMapMovieGenreId").Value, out genreMapMovieGenreId))
            {
                genreMapMovieGenreId = -1;
            }

            // Each genre map value is a '{' delimited list of "program" genre names (those that may be compared with the genre from the program listings).
            // It is an error if a single "program" genre is mapped to more than one guide genre; behavior is undefined for this condition.
            int genreIndex = 0;

            while (true)
            {
                // The genremap key is an integer value that is added to a base value in order to locate the correct localized genre name string.
                genre = _tvBusiness.GetSetting("genreMapName" + genreIndex).Value;
                if (string.IsNullOrEmpty(genre))
                {
                    break;
                }

                // Get the status of the mp genre.
                if (!bool.TryParse(_tvBusiness.GetSetting("genreMapNameEnabled" + genreIndex).Value, out enabled))
                {
                    enabled = true;
                }
                EpgGenre?epgGenre = null;
                if (enabled && genreIndex == genreMapMovieGenreId)
                {
                    epgGenre = EpgGenre.Movie;
                }
                else if (enabled && !string.IsNullOrEmpty(genre))
                {
                    if (converter.GetGenreId(genre, GenreCategory.Epg, null, out int genreId))
                    {
                        epgGenre = (EpgGenre)genreId;
                    }
                }
                if (epgGenre.HasValue)
                {
                    string genreMapEntry = _tvBusiness.GetSetting("genreMapEntry" + genreIndex).Value;
                    if (!string.IsNullOrEmpty(genreMapEntry))
                    {
                        _tvGenres.TryAdd(epgGenre.Value, genreMapEntry.Split(new char[] { '{' }, StringSplitOptions.RemoveEmptyEntries));
                    }
                }
                genreIndex++;
            }
        }
Exemple #11
0
        /// <summary>
        /// Asynchronously tries to extract metadata for the given <param name="mediaItemAccessor"></param>
        /// </summary>
        /// <param name="mediaItemAccessor">Points to the resource for which we try to extract metadata</param>
        /// <param name="extractedAspectData">Dictionary of <see cref="MediaItemAspect"/>s with the extracted metadata</param>
        /// <param name="forceQuickMode">If <c>true</c>, nothing is downloaded from the internet</param>
        /// <returns><c>true</c> if metadata was found and stored into <param name="extractedAspectData"></param>, else <c>false</c></returns>
        private async Task <bool> TryExtractAudioMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            // Get a unique number for this call to TryExtractMetadataAsync. We use this to make reading the debug log easier.
            // This MetadataExtractor is called in parallel for multiple MediaItems so that the respective debug log entries
            // for one call are not contained one after another in debug log. We therefore prepend this number before every log entry.
            var  miNumber = Interlocked.Increment(ref _lastMediaItemNumber);
            bool isStub   = extractedAspectData.ContainsKey(StubAspect.ASPECT_ID);

            if (!isStub)
            {
                _debugLogger.Info("[#{0}]: Ignoring non-stub track", miNumber);
                return(false);
            }
            try
            {
                _debugLogger.Info("[#{0}]: Start extracting metadata for resource '{1}' (forceQuickMode: {2})", miNumber, mediaItemAccessor, forceQuickMode);

                // We only extract metadata with this MetadataExtractor, if another MetadataExtractor that was applied before
                // has identified this MediaItem as a video and therefore added a VideoAspect.
                if (!extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract metadata; this resource is not audio", miNumber);
                    return(false);
                }

                // This MetadataExtractor only works for MediaItems accessible by an IFileSystemResourceAccessor.
                // Otherwise it is not possible to find a nfo-file in the MediaItem's directory.
                if (!(mediaItemAccessor is IFileSystemResourceAccessor))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract metadata; mediaItemAccessor is not an IFileSystemResourceAccessor", miNumber);
                    return(false);
                }

                // First we try to find an IFileSystemResourceAccessor pointing to the album nfo-file.
                IFileSystemResourceAccessor albumNfoFsra;
                if (TryGetAlbumNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out albumNfoFsra))
                {
                    // If we found one, we (asynchronously) extract the metadata into a stub object and, if metadata was found,
                    // we store it into the MediaItemAspects.
                    var albumNfoReader = new NfoAlbumReader(_debugLogger, miNumber, forceQuickMode, isStub, _httpClient, _settings);
                    using (albumNfoFsra)
                    {
                        if (await albumNfoReader.TryReadMetadataAsync(albumNfoFsra).ConfigureAwait(false))
                        {
                            //Check reimport
                            if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID))
                            {
                                AlbumInfo reimport = new AlbumInfo();
                                reimport.FromMetadata(extractedAspectData);
                                if (!VerifyAlbumReimport(albumNfoReader, reimport))
                                {
                                    ServiceRegistration.Get <ILogger>().Info("NfoMovieMetadataExtractor: Nfo album metadata from resource '{0}' ignored because it does not match reimport {1}", mediaItemAccessor, reimport);
                                    return(false);
                                }
                            }

                            Stubs.AlbumStub album = albumNfoReader.GetAlbumStubs().FirstOrDefault();
                            if (album != null)
                            {
                                int trackNo = 0;
                                if (album.Tracks != null && album.Tracks.Count > 0 && MediaItemAspect.TryGetAttribute(extractedAspectData, AudioAspect.ATTR_TRACK, out trackNo))
                                {
                                    var track = album.Tracks.FirstOrDefault(t => t.TrackNumber.HasValue && trackNo == t.TrackNumber.Value);
                                    if (track != null)
                                    {
                                        TrackInfo trackInfo = new TrackInfo();
                                        string    title;
                                        string    sortTitle;

                                        title     = track.Title.Trim();
                                        sortTitle = BaseInfo.GetSortTitle(title);

                                        IEnumerable <string> artists;
                                        if (track.Artists.Count > 0)
                                        {
                                            artists = track.Artists;
                                        }

                                        IList <MultipleMediaItemAspect> providerResourceAspects;
                                        if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerResourceAspects))
                                        {
                                            MultipleMediaItemAspect providerResourceAspect = providerResourceAspects.First(pa => pa.GetAttributeValue <int>(ProviderResourceAspect.ATTR_TYPE) == ProviderResourceAspect.TYPE_STUB);
                                            string mime = null;
                                            if (track.FileInfo != null && track.FileInfo.Count > 0)
                                            {
                                                mime = MimeTypeDetector.GetMimeTypeFromExtension("file" + track.FileInfo.First().Container);
                                            }
                                            if (mime != null)
                                            {
                                                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mime);
                                            }
                                        }

                                        trackInfo.TrackName               = title;
                                        trackInfo.TrackNameSort           = sortTitle;
                                        trackInfo.Duration                = track.Duration.HasValue ? Convert.ToInt64(track.Duration.Value.TotalSeconds) : 0;
                                        trackInfo.Album                   = !string.IsNullOrEmpty(album.Title) ? album.Title.Trim() : null;
                                        trackInfo.TrackNum                = track.TrackNumber.HasValue ? track.TrackNumber.Value : 0;
                                        trackInfo.TotalTracks             = album.Tracks.Count;
                                        trackInfo.MusicBrainzId           = track.MusicBrainzId;
                                        trackInfo.IsrcId                  = track.Isrc;
                                        trackInfo.AudioDbId               = track.AudioDbId.HasValue ? track.AudioDbId.Value : 0;
                                        trackInfo.AlbumMusicBrainzId      = album.MusicBrainzAlbumId;
                                        trackInfo.AlbumMusicBrainzGroupId = album.MusicBrainzReleaseGroupId;
                                        trackInfo.ReleaseDate             = album.ReleaseDate;
                                        if (track.FileInfo != null && track.FileInfo.Count > 0 && track.FileInfo.First().AudioStreams != null && track.FileInfo.First().AudioStreams.Count > 0)
                                        {
                                            var audio = track.FileInfo.First().AudioStreams.First();
                                            trackInfo.Encoding = audio.Codec;
                                            trackInfo.BitRate  = audio.Bitrate != null?Convert.ToInt32(audio.Bitrate / 1000) : 0;

                                            trackInfo.Channels = audio.Channels != null ? audio.Channels.Value : 0;
                                        }
                                        trackInfo.Artists = new List <PersonInfo>();
                                        if (track.Artists != null && track.Artists.Count > 0)
                                        {
                                            foreach (string artistName in track.Artists)
                                            {
                                                trackInfo.Artists.Add(new PersonInfo()
                                                {
                                                    Name            = artistName.Trim(),
                                                    Occupation      = PersonAspect.OCCUPATION_ARTIST,
                                                    ParentMediaName = trackInfo.Album,
                                                    MediaName       = trackInfo.TrackName
                                                });
                                            }
                                        }
                                        trackInfo.AlbumArtists = new List <PersonInfo>();
                                        if (album.Artists != null && album.Artists.Count > 0)
                                        {
                                            foreach (string artistName in album.Artists)
                                            {
                                                trackInfo.AlbumArtists.Add(new PersonInfo()
                                                {
                                                    Name            = artistName.Trim(),
                                                    Occupation      = PersonAspect.OCCUPATION_ARTIST,
                                                    ParentMediaName = trackInfo.Album,
                                                    MediaName       = trackInfo.TrackName
                                                });
                                            }
                                        }
                                        if (album.Genres != null && album.Genres.Count > 0)
                                        {
                                            trackInfo.Genres = album.Genres.Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo {
                                                Name = s.Trim()
                                            }).ToList();
                                            IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                                            foreach (var genre in trackInfo.Genres)
                                            {
                                                if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId))
                                                {
                                                    genre.Id = genreId;
                                                }
                                            }
                                        }

                                        if (album.Thumb != null && album.Thumb.Length > 0)
                                        {
                                            try
                                            {
                                                using (MemoryStream stream = new MemoryStream(album.Thumb))
                                                {
                                                    trackInfo.Thumbnail  = stream.ToArray();
                                                    trackInfo.HasChanged = true;
                                                }
                                            }
                                            // Decoding of invalid image data can fail, but main MediaItem is correct.
                                            catch { }
                                        }

                                        //Determine 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 artistMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../");
                                            if (artistMediaItemDirectoryPath.FileName.IndexOf("Compilation", StringComparison.InvariantCultureIgnoreCase) >= 0)
                                            {
                                                trackInfo.Compilation = true;
                                            }
                                        }
                                        trackInfo.SetMetadata(extractedAspectData);
                                    }
                                }
                            }
                        }
                        else
                        {
                            _debugLogger.Warn("[#{0}]: No valid metadata found in album nfo-file", miNumber);
                        }
                    }
                }

                _debugLogger.Info("[#{0}]: Successfully finished extracting metadata", miNumber);
                ServiceRegistration.Get <ILogger>().Debug("NfoAudioMetadataExtractor: Assigned nfo audio metadata for resource '{0}'", mediaItemAccessor);
                return(true);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Warn("NfoAudioMetadataExtractor: Exception while extracting metadata for resource '{0}'; enable debug logging for more details.", mediaItemAccessor);
                _debugLogger.Error("[#{0}]: Exception while extracting metadata", e, miNumber);
                return(false);
            }
        }
Exemple #12
0
        public virtual Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            try
            {
                IResourceAccessor metaFileAccessor;
                if (!CanExtract(mediaItemAccessor, extractedAspectData, out metaFileAccessor))
                {
                    return(Task.FromResult(false));
                }

                Tags tags;
                using (metaFileAccessor)
                {
                    using (Stream metaStream = ((IFileSystemResourceAccessor)metaFileAccessor).OpenRead())
                        tags = (Tags)GetTagsXmlSerializer().Deserialize(metaStream);
                }

                string value;
                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false);

                if (TryGet(tags, TAG_TITLE, out value) && !string.IsNullOrEmpty(value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, value);
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(value));
                }

                if (TryGet(tags, TAG_GENRE, out value) && !string.IsNullOrEmpty(value?.Trim()))
                {
                    List <GenreInfo> genreList = new List <GenreInfo>(new GenreInfo[] { new GenreInfo {
                                                                                            Name = value.Trim()
                                                                                        } });
                    IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                    foreach (var genre in genreList)
                    {
                        if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId))
                        {
                            genre.Id = genreId;
                        }
                    }
                    MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata);
                    genreAspect.SetAttribute(GenreAspect.ATTR_ID, genreList[0].Id);
                    genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genreList[0].Name);
                }

                if (TryGet(tags, TAG_PLOT, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, value);
                    Match yearMatch = _yearMatcher.Match(value);
                    int   guessedYear;
                    if (int.TryParse(yearMatch.Value, out guessedYear))
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(guessedYear, 1, 1));
                    }
                }

                if (TryGet(tags, TAG_CHANNEL, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, value);
                }

                // Recording date formatted: 2011-11-04 20:55
                DateTime tmpValue;
                DateTime?recordingStart = null;
                DateTime?recordingEnd   = null;
                DateTime?programStart   = null;
                DateTime?programEnd     = null;

                // First try to read program start and end times, they will be preferred.
                if (TryGet(tags, TAG_PROGRAMSTARTTIME, out value) && DateTime.TryParse(value, out tmpValue))
                {
                    programStart = tmpValue;
                }

                if (TryGet(tags, TAG_PROGRAMENDTIME, out value) && DateTime.TryParse(value, out tmpValue))
                {
                    programEnd = tmpValue;
                }

                if (TryGet(tags, TAG_STARTTIME, out value) && DateTime.TryParse(value, out tmpValue))
                {
                    recordingStart = tmpValue;
                }

                if (TryGet(tags, TAG_ENDTIME, out value) && DateTime.TryParse(value, out tmpValue))
                {
                    recordingEnd = tmpValue;
                }

                // Correct start time if recording started before the program (skip pre-recording offset)
                if (programStart.HasValue && recordingStart.HasValue && programStart > recordingStart)
                {
                    recordingStart = programStart;
                }

                // Correct end time if recording ended after the program (skip the post-recording offset)
                if (programEnd.HasValue && recordingEnd.HasValue && programEnd < recordingEnd)
                {
                    recordingEnd = programEnd;
                }

                if (recordingStart.HasValue)
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, recordingStart.Value);
                }
                if (recordingEnd.HasValue)
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, recordingEnd.Value);
                }

                return(Task.FromResult(true));
            }
            catch (Exception e)
            {
                // Only log at the info level here - And simply return false. This lets the caller know that we
                // couldn't perform our task here.
                ServiceRegistration.Get <ILogger>().Info("Tve3RecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message);
            }
            return(Task.FromResult(false));
        }
Exemple #13
0
        private async Task <bool> ExtractMovieData(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);
            }

            // Calling EnsureLocalFileSystemAccess not necessary; only string operation
            string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() };
            string   title       = null;
            string   sortTitle   = null;
            bool     isReimport  = extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID);

            MovieInfo movieInfo = new MovieInfo();

            if (extractedAspectData.ContainsKey(MovieAspect.ASPECT_ID))
            {
                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))
                {
                    //The title may still contain tags and other noise, try and parse it for a title and year.
                    MovieNameMatcher.MatchTitleYear(title, 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 (!isReimport) //Ignore tags or file based information for reimport because they might be the cause of the wrong import
            {
                if (movieInfo.MovieDbId == 0)
                {
                    try
                    {
                        // Try to use an existing TMDB id for exact mapping
                        string tmdbId = await MatroskaMatcher.TryMatchTmdbIdAsync(lfsra).ConfigureAwait(false);

                        if (!string.IsNullOrEmpty(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 = await MatroskaMatcher.TryMatchImdbIdAsync(lfsra).ConfigureAwait(false);

                        if (!string.IsNullOrEmpty(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 || !movieInfo.ReleaseDate.HasValue)
                {
                    // 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;
                    }
                }

                try
                {
                    await MatroskaMatcher.ExtractFromTagsAsync(lfsra, movieInfo).ConfigureAwait(false);

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

            // 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 (SkipOnlineSearches && !SkipFanArtDownload)
            {
                MovieInfo tempInfo = movieInfo.Clone();
                if (await OnlineMatcherService.Instance.FindAndUpdateMovieAsync(tempInfo).ConfigureAwait(false))
                {
                    movieInfo.CopyIdsFrom(tempInfo);
                    movieInfo.HasChanged = tempInfo.HasChanged;
                }
            }
            else if (!SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.FindAndUpdateMovieAsync(movieInfo).ConfigureAwait(false);
            }

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

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

            //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;
                }
            }

            if (movieInfo.MovieNameSort.IsEmpty)
            {
                if (!movieInfo.CollectionName.IsEmpty && movieInfo.ReleaseDate.HasValue)
                {
                    movieInfo.MovieNameSort = $"{movieInfo.CollectionName.Text} {movieInfo.ReleaseDate.Value.Year}-{movieInfo.ReleaseDate.Value.Month.ToString("00")}";
                }
                else if (!movieInfo.MovieName.IsEmpty)
                {
                    movieInfo.MovieNameSort = BaseInfo.GetSortTitle(movieInfo.MovieName.Text);
                }
                else
                {
                    movieInfo.MovieNameSort = BaseInfo.GetSortTitle(title);
                }
            }
            movieInfo.SetMetadata(extractedAspectData);

            return(movieInfo.IsBaseInfoPresent);
        }
        public override async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            // If the base AudioMDE already extracted metadata, don't try here again to avoid conflicts.
            if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID))
            {
                return(false);
            }

            ILocalFsResourceAccessor fsra = mediaItemAccessor as ILocalFsResourceAccessor;

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

            if (!HasAudioExtension(fileName))
            {
                return(false);
            }
            if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID)) //Ignore for reimports because the tags might be the cause of the wrong match
            {
                return(false);
            }

            try
            {
                TAG_INFO tags;
                using (fsra.EnsureLocalFileSystemAccess())
                    tags = BassTags.BASS_TAG_GetFromFile(fsra.LocalFileSystemPath);
                if (tags == null)
                {
                    return(false);
                }

                fileName = ProviderPathHelper.GetFileNameWithoutExtension(fileName) ?? string.Empty;
                string title;
                string artist;
                uint?  trackNo;
                GuessMetadataFromFileName(fileName, out title, out artist, out trackNo);
                if (!string.IsNullOrWhiteSpace(tags.title))
                {
                    title = tags.title;
                }
                IEnumerable <string> artists;
                if (!string.IsNullOrWhiteSpace(tags.artist))
                {
                    artists = SplitTagEnum(tags.artist);
                    artists = PatchID3v23Enumeration(artists);
                }
                else
                {
                    artists = artist == null ? null : new string[] { artist }
                };
                if (!string.IsNullOrWhiteSpace(tags.track) && tags.track != "0")
                {
                    int iTrackNo;
                    if (int.TryParse(tags.track, out iTrackNo))
                    {
                        trackNo = (uint?)iTrackNo;
                    }
                    else
                    {
                        trackNo = null;
                    }
                }

                TrackInfo trackInfo = new TrackInfo();
                if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID))
                {
                    trackInfo.FromMetadata(extractedAspectData);
                }
                else
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);
                    IList <MultipleMediaItemAspect> providerResourceAspect;
                    if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerResourceAspect))
                    {
                        providerResourceAspect[0].SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size);
                        // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                        providerResourceAspect[0].SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "audio/" + Path.GetExtension(fsra.LocalFileSystemPath).Substring(1));
                    }
                    MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_BITRATE, tags.bitrate);
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(tags.comment));
                    MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_DURATION, (long)tags.duration);
                }

                if (!trackInfo.IsBaseInfoPresent)
                {
                    trackInfo.TrackName = title;
                    trackInfo.Album     = StringUtils.TrimToNull(tags.album);
                    if (trackNo.HasValue)
                    {
                        trackInfo.TrackNum = (int)trackNo.Value;
                    }

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

                    IEnumerable <string> albumArtists = SplitTagEnum(tags.albumartist);
                    albumArtists           = PatchID3v23Enumeration(albumArtists);
                    trackInfo.AlbumArtists = new List <PersonInfo>();
                    foreach (string artistName in ApplyAdditionalSeparator(albumArtists))
                    {
                        trackInfo.AlbumArtists.Add(new PersonInfo()
                        {
                            Name            = artistName,
                            Occupation      = PersonAspect.OCCUPATION_ARTIST,
                            ParentMediaName = trackInfo.Album,
                            MediaName       = trackInfo.TrackName
                        });
                    }

                    IEnumerable <string> composers = SplitTagEnum(tags.composer);
                    composers           = PatchID3v23Enumeration(composers);
                    trackInfo.Composers = new List <PersonInfo>();
                    foreach (string composerName in ApplyAdditionalSeparator(composers))
                    {
                        trackInfo.Composers.Add(new PersonInfo()
                        {
                            Name            = composerName,
                            Occupation      = PersonAspect.OCCUPATION_COMPOSER,
                            ParentMediaName = trackInfo.Album,
                            MediaName       = trackInfo.TrackName
                        });
                    }

                    IEnumerable <string> genres = SplitTagEnum(tags.genre);
                    genres           = PatchID3v23Enumeration(genres);
                    trackInfo.Genres = ApplyAdditionalSeparator(genres).Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo {
                        Name = s.Trim()
                    }).ToList();
                    IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                    foreach (var genre in trackInfo.Genres)
                    {
                        if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId))
                        {
                            genre.Id = genreId;
                        }
                    }

                    int year;
                    if (int.TryParse(tags.year, out 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).
                        if (tags.PictureCount > 0)
                        {
                            try
                            {
                                using (Image cover = tags.PictureGetImage(0))
                                    using (MemoryStream result = new MemoryStream())
                                    {
                                        cover.Save(result, ImageFormat.Jpeg);
                                        trackInfo.Thumbnail  = result.ToArray();
                                        trackInfo.HasChanged = true;
                                    }
                            }
                            // Decoding of invalid image data can fail, but main MediaItem is correct.
                            catch { }
                        }
                    }
                }

                if (!SkipOnlineSearches && !forceQuickMode)
                {
                    await OnlineMatcherService.Instance.FindAndUpdateTrackAsync(trackInfo).ConfigureAwait(false);
                }

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

                trackInfo.SetMetadata(extractedAspectData);

                return(trackInfo.IsBaseInfoPresent);
            }
            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("BassAudioMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message);
            }
            return(false);
        }
        public override async Task <ICollection <FilterValue> > GetAvailableValuesAsync(IEnumerable <Guid> necessaryMIATypeIds, IFilter selectAttributeFilter, IFilter filter)
        {
            IContentDirectory cd = ServiceRegistration.Get <IServerConnectionManager>().ContentDirectory;

            if (cd == null)
            {
                throw new NotConnectedException("The MediaLibrary is not connected");
            }

            bool            showVirtual = VirtualMediaHelper.ShowVirtualMedia(necessaryMIATypeIds);
            ViewSettings    settings    = ServiceRegistration.Get <ISettingsManager>().Load <ViewSettings>();
            IGenreConverter converter   = ServiceRegistration.Get <IGenreConverter>();
            Dictionary <int, FilterValue> genredFilters   = new Dictionary <int, FilterValue>();
            List <FilterValue>            ungenredFilters = new List <FilterValue>();

            if (_necessaryMIATypeIds != null)
            {
                necessaryMIATypeIds = _necessaryMIATypeIds;
            }
            HomogenousMap valueGroups = null;
            HomogenousMap valueKeys   = null;

            if (_keyAttributeType != null)
            {
                Tuple <HomogenousMap, HomogenousMap> values = await cd.GetKeyValueGroupsAsync(_keyAttributeType, _valueAttributeType, selectAttributeFilter, ProjectionFunction.None, necessaryMIATypeIds, filter, true, showVirtual);

                valueGroups = values.Item1;
                valueKeys   = values.Item2;
            }
            else
            {
                valueGroups = await cd.GetValueGroupsAsync(_valueAttributeType, selectAttributeFilter, ProjectionFunction.None, necessaryMIATypeIds, filter, true, showVirtual);
            }
            IList <FilterValue> result = new List <FilterValue>(valueGroups.Count);
            int numEmptyEntries        = 0;

            foreach (KeyValuePair <object, object> group in valueGroups)
            {
                string name = GetDisplayName(group.Key);
                if (name == string.Empty)
                {
                    numEmptyEntries += (int)group.Value;
                }
                else if (!string.IsNullOrEmpty(_genreCategory) && settings.UseLocalizedGenres)
                {
                    int?genreId = valueKeys[group.Key] as int?;
                    if (!genreId.HasValue)
                    {
                        ungenredFilters.Add(new FilterValue(valueKeys[group.Key], name, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key), null, (int)group.Value, this));
                    }
                    else if (!genredFilters.ContainsKey(genreId.Value))
                    {
                        if (converter.GetGenreName(genreId.Value, _genreCategory, null, out string genreName))
                        {
                            name = genreName;
                        }
                        genredFilters.Add(genreId.Value, new FilterValue(genreId.Value, name, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key), null, (int)group.Value, this));
                    }
                    else
                    {
                        genredFilters[genreId.Value] = new FilterValue(genreId.Value, genredFilters[genreId.Value].Title,
                                                                       BooleanCombinationFilter.CombineFilters(BooleanOperator.Or, genredFilters[genreId.Value].Filter, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key)), null,
                                                                       genredFilters[genreId.Value].NumItems.Value + (int)group.Value, this);
                    }
                }
                else
                {
                    result.Add(new FilterValue(valueKeys[group.Key], name, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key), null, (int)group.Value, this));
                }
            }

            foreach (var gf in genredFilters.Values)
            {
                result.Add(gf);
            }

            foreach (var ugf in ungenredFilters)
            {
                result.Add(ugf);
            }

            if (numEmptyEntries > 0)
            {
                result.Insert(0, new FilterValue(Consts.RES_VALUE_EMPTY_TITLE, new EmptyFilter(_valueAttributeType), null, numEmptyEntries, this));
            }

            return(result);
        }