예제 #1
0
        /// <summary>
        /// Asynchronously tries to extract series 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 MediaItemAspect to update with metadata</param>
        /// <param name="reimport">During reimport only allow if nfo is for same media as this</param>
        /// <returns><c>true</c> if metadata was found and stored into the <paramref name="extractedAspectData"/>, else <c>false</c></returns>
        protected async Task <bool> TryExtractAlbumMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, AlbumInfo reimport)
        {
            NfoAlbumReader albumNfoReader = await TryGetNfoAlbumReaderAsync(mediaItemAccessor).ConfigureAwait(false);

            if (albumNfoReader != null)
            {
                if (reimport != null && !VerifyAlbumReimport(albumNfoReader, reimport))
                {
                    return(false);
                }

                return(albumNfoReader.TryWriteMetadata(extractedAspectData));
            }
            return(false);
        }
예제 #2
0
        /// <summary>
        /// Asynchronously tries to get an <see cref="NfoAlbumReader"/> 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>
        protected async Task <NfoAlbumReader> TryGetNfoAlbumReaderAsync(IResourceAccessor mediaItemAccessor)
        {
            // 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);

            try
            {
                _debugLogger.Info("[#{0}]: Start extracting metadata for resource '{1}'", miNumber, mediaItemAccessor);

                // 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(null);
                }

                // 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
                    var albumNfoReader = new NfoAlbumReader(_debugLogger, miNumber, false, false, _httpClient, _settings);
                    using (albumNfoFsra)
                    {
                        if (await albumNfoReader.TryReadMetadataAsync(albumNfoFsra).ConfigureAwait(false))
                        {
                            return(albumNfoReader);
                        }
                        else
                        {
                            _debugLogger.Warn("[#{0}]: No valid metadata found in album nfo-file", miNumber);
                        }
                    }
                }
            }
            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(null);
        }
예제 #3
0
        /// <summary>
        /// Verifies if the album being reimported matches the album in the nfo file
        /// </summary>
        /// <param name="reader">Reader used read the album information from the nfo file</param>
        /// <param name="reimport">The album being reimported</param>
        /// <returns>Result of the verification</returns>
        protected bool VerifyAlbumReimport(NfoAlbumReader reader, AlbumInfo reimport)
        {
            if (reimport == null)
            {
                return(true);
            }

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

            if (reader.TryWriteMetadata(aspectData))
            {
                AlbumInfo info = new AlbumInfo();
                info.FromMetadata(aspectData);
                if (reimport.Equals(info))
                {
                    return(true);
                }
            }
            return(false);
        }
        /// <summary>
        /// Asynchronously tries to extract series actors 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="extractedAspects">List of MediaItemAspect dictionaries to update with metadata</param>
        /// <returns><c>true</c> if metadata was found and stored into the <paramref name="extractedAspects"/>, else <c>false</c></returns>
        protected async Task <bool> TryExtractAlbumArtistMetadataAsync(IResourceAccessor mediaItemAccessor, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedAspects)
        {
            NfoArtistReader artistReader = await TryGetNfoArtistReaderAsync(mediaItemAccessor).ConfigureAwait(false);

            if (artistReader != null)
            {
                IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                if (artistReader.TryWriteMetadata(aspects))
                {
                    extractedAspects.Add(aspects);
                    return(true);
                }
            }

            NfoAlbumReader albumNfoReader = await TryGetNfoAlbumReaderAsync(mediaItemAccessor).ConfigureAwait(false);

            if (albumNfoReader != null)
            {
                return(albumNfoReader.TryWriteArtistMetadata(extractedAspects));
            }
            return(false);
        }
예제 #5
0
        public override async Task CollectFanArtAsync(Guid mediaItemId, IDictionary <Guid, IList <MediaItemAspect> > aspects)
        {
            IResourceLocator mediaItemLocator = null;

            if (!BaseInfo.IsVirtualResource(aspects))
            {
                mediaItemLocator = GetResourceLocator(aspects);
            }

            if (!aspects.ContainsKey(AudioAspect.ASPECT_ID) || mediaItemLocator == null)
            {
                return;
            }

            IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>();

            using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor())
            {
                //Album fanart
                if (RelationshipExtractorUtils.TryGetLinkedId(AudioAlbumAspect.ROLE_ALBUM, aspects, out Guid albumMediaItemId) && AddToCache(albumMediaItemId))
                {
                    var existingCovers = fanArtCache.GetFanArtFiles(albumMediaItemId, FanArtTypes.Cover);
                    if (!existingCovers.Any()) //Only get album cover if needed for better performance
                    {
                        NfoAlbumReader albumNfoReader = await AUDIO_EXTRACTOR.TryGetNfoAlbumReaderAsync(mediaItemAccessor, true).ConfigureAwait(false);

                        if (albumNfoReader != null)
                        {
                            var stubs    = albumNfoReader.GetAlbumStubs();
                            var mainStub = stubs?.FirstOrDefault();
                            if (mainStub?.Thumb != null)
                            {
                                await fanArtCache.TrySaveFanArt(albumMediaItemId, mainStub.Title, FanArtTypes.Cover, p => TrySaveFileImage(mainStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false);
                            }
                        }
                    }
                }

                //Artist fanart
                IList <Tuple <Guid, string> > artists = GetArtists(aspects);
                if (artists?.Count > 0)
                {
                    foreach (var artist in artists)
                    {
                        var existingThumbs = fanArtCache.GetFanArtFiles(artist.Item1, FanArtTypes.Thumbnail);
                        if (!existingThumbs.Any() && AddToCache(artist.Item1)) //Only get artist thumbnail if needed for better performance
                        {
                            NfoArtistReader artistReader = await AUDIO_EXTRACTOR.TryGetNfoArtistReaderAsync(mediaItemAccessor, artist.Item2, true).ConfigureAwait(false);

                            if (artistReader != null)
                            {
                                var stubs    = artistReader.GetArtistStubs();
                                var mainStub = stubs?.FirstOrDefault();
                                if (string.Equals(mainStub?.Name, artist.Item2, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    if (mainStub?.Thumb != null)
                                    {
                                        await fanArtCache.TrySaveFanArt(artist.Item1, artist.Item2, FanArtTypes.Thumbnail, p => TrySaveFileImage(mainStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #6
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);
            }
        }