public static void StoreSeries(IDictionary <Guid, IList <MediaItemAspect> > aspects, SeriesStub series)
        {
            SingleMediaItemAspect seriesAspect = MediaItemAspect.GetOrCreateAspect(aspects, TempSeriesAspect.Metadata);

            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_TVDBID, series.Id.HasValue ? series.Id.Value : 0);
            string title = !string.IsNullOrEmpty(series.Title) ? series.Title : series.ShowTitle;

            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_NAME, title);
            if (!string.IsNullOrEmpty(series.SortTitle))
            {
                seriesAspect.SetAttribute(TempSeriesAspect.ATTR_SORT_NAME, series.SortTitle);
            }
            else
            {
                seriesAspect.SetAttribute(TempSeriesAspect.ATTR_SORT_NAME, BaseInfo.GetSortTitle(title));
            }
            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_CERTIFICATION, series.Mpaa != null && series.Mpaa.Count > 0 ? series.Mpaa.First() : null);
            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_ENDED, !string.IsNullOrEmpty(series.Status) ? series.Status.Contains("End") : false);
            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_PLOT, !string.IsNullOrEmpty(series.Plot) ? series.Plot : series.Outline);
            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_PREMIERED, series.Premiered.HasValue ? series.Premiered.Value : series.Year.HasValue ? series.Year.Value : default(DateTime?));
            seriesAspect.SetCollectionAttribute(TempSeriesAspect.ATTR_GENRES, series.Genres);
            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_RATING, series.Rating.HasValue ? Convert.ToDouble(series.Rating.Value) : 0.0);
            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_VOTES, series.Votes.HasValue ? series.Votes.Value : series.Rating.HasValue ? 1 : 0);
            seriesAspect.SetAttribute(TempSeriesAspect.ATTR_STATION, series.Studio);
        }
        private static LiveTvMediaItem.LiveTvMediaItem CreateCommonMediaItem(int slotIndex, string path, bool isTv, string customMimeType = null)
        {
            ISystemResolver systemResolver = ServiceRegistration.Get <ISystemResolver>();
            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();

            SlimTvResourceAccessor resourceAccessor = new SlimTvResourceAccessor(slotIndex, path);

            MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(aspects, ProviderResourceAspect.Metadata);

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_PRIMARY);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, systemResolver.LocalSystemId);

            String raPath = resourceAccessor.CanonicalLocalResourcePath.Serialize();

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, raPath);

            string title;
            string mimeType;

            if (isTv)
            {
                // VideoAspect needs to be included to associate VideoPlayer later!
                MediaItemAspect.GetOrCreateAspect(aspects, VideoAspect.Metadata);
                title    = "Live TV";
                mimeType = LiveTvMediaItem.LiveTvMediaItem.MIME_TYPE_TV;
            }
            else
            {
                // AudioAspect needs to be included to associate an AudioPlayer later!
                MediaItemAspect.GetOrCreateAspect(aspects, AudioAspect.Metadata);
                title    = "Live Radio";
                mimeType = LiveTvMediaItem.LiveTvMediaItem.MIME_TYPE_RADIO;
            }

            // Allow overriding from argument
            if (!string.IsNullOrEmpty(customMimeType))
            {
                mimeType = customMimeType;
            }

            MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_TITLE, title);
            MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(title));
            MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mimeType); // Custom mimetype for LiveTv or Radio
            LiveTvMediaItem.LiveTvMediaItem tvStream = new LiveTvMediaItem.LiveTvMediaItem(new Guid(), aspects);
            return(tvStream);
        }
        /// <summary>
        /// Asynchronously tries to extract episode 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> TryExtractEpsiodeMetadataAsync(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);

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

                // 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 or parent directory.
                if (!(mediaItemAccessor is IFileSystemResourceAccessor))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract metadata; mediaItemAccessor is not an IFileSystemResourceAccessor", miNumber);
                    return(false);
                }

                // 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(VideoAspect.ASPECT_ID))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract metadata; this resource is not a video", miNumber);
                    return(false);
                }

                // Here we try to find an IFileSystemResourceAccessor pointing to the episode nfo-file.
                // If we don't find one, we cannot extract any metadata.
                IFileSystemResourceAccessor episodeNfoFsra;
                NfoSeriesEpisodeReader      episodeNfoReader = null;
                bool episodeDetailsFound = false;
                if (TryGetEpisodeNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out episodeNfoFsra))
                {
                    episodeDetailsFound = true;
                    // Now we (asynchronously) extract the metadata into a stub object.
                    // If no metadata was found, nothing can be stored in the MediaItemAspects.
                    episodeNfoReader = new NfoSeriesEpisodeReader(_debugLogger, miNumber, forceQuickMode, isStub, _httpClient, _settings);
                    using (episodeNfoFsra)
                    {
                        if (!await episodeNfoReader.TryReadMetadataAsync(episodeNfoFsra).ConfigureAwait(false))
                        {
                            _debugLogger.Warn("[#{0}]: No valid metadata found in episode nfo-file", miNumber);
                            return(false);
                        }
                    }
                }

                // Then we try to find an IFileSystemResourceAccessor pointing to the series nfo-file.
                IFileSystemResourceAccessor seriesNfoFsra;
                if (TryGetSeriesNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out seriesNfoFsra))
                {
                    // If we found one, we (asynchronously) extract the metadata into a stub object and, if metadata was found,
                    // we store it into the episodeNfoReader so that the latter can store metadata from series and episode level into the MediaItemAspects.
                    var seriesNfoReader = new NfoSeriesReader(_debugLogger, miNumber, forceQuickMode, !episodeDetailsFound, isStub, _httpClient, _settings);
                    using (seriesNfoFsra)
                    {
                        if (await seriesNfoReader.TryReadMetadataAsync(seriesNfoFsra).ConfigureAwait(false))
                        {
                            //Check reimport
                            if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID))
                            {
                                SeriesInfo reimport = new SeriesInfo();
                                reimport.FromMetadata(extractedAspectData);
                                if (!VerifySeriesReimport(seriesNfoReader, reimport))
                                {
                                    ServiceRegistration.Get <ILogger>().Info("NfoSeriesMetadataExtractor: Nfo series metadata from resource '{0}' ignored because it does not match reimport {1}", mediaItemAccessor, reimport);
                                    return(false);
                                }
                            }

                            Stubs.SeriesStub series = seriesNfoReader.GetSeriesStubs().FirstOrDefault();

                            // Check if episode should be found
                            if (isStub || !episodeDetailsFound)
                            {
                                if (series != null && series.Episodes?.Count > 0)
                                {
                                    List <Stubs.SeriesEpisodeStub> episodeStubs = null;
                                    if (extractedAspectData.ContainsKey(EpisodeAspect.ASPECT_ID))
                                    {
                                        int?        seasonNo = 0;
                                        IEnumerable episodes;
                                        if (MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_SEASON, out seasonNo) && MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_EPISODE, out episodes))
                                        {
                                            List <int> episodeNos = new List <int>();
                                            CollectionUtils.AddAll(episodeNos, episodes.Cast <int>());

                                            if (seasonNo.HasValue && episodeNos.Count > 0)
                                            {
                                                episodeStubs = series.Episodes.Where(e => e.Season == seasonNo.Value && episodeNos.Intersect(e.Episodes).Any()).ToList();
                                            }
                                        }
                                    }
                                    else
                                    {
                                        string title = null;
                                        if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title))
                                        {
                                            Regex regex = new Regex(@"(?<series>[^\\]+).S(?<seasonnum>\d+)[\s|\.|\-|_]{0,1}E((?<episodenum>\d+)_?)+(?<episode>.*)");
                                            Match match = regex.Match(title);

                                            if (match.Success && match.Groups["seasonnum"].Length > 0 && match.Groups["episodenum"].Length > 0)
                                            {
                                                episodeStubs = series.Episodes.Where(e => e.Season == Convert.ToInt32(match.Groups["seasonnum"].Value) && e.Episodes.Contains(Convert.ToInt32(match.Groups["episodenum"].Value))).ToList();
                                            }
                                        }
                                    }
                                    if (episodeStubs != null && episodeStubs.Count > 0)
                                    {
                                        Stubs.SeriesEpisodeStub mergedEpisode = null;
                                        if (isStub)
                                        {
                                            if (episodeStubs.Count == 1)
                                            {
                                                mergedEpisode = episodeStubs.First();
                                            }
                                            else
                                            {
                                                Stubs.SeriesEpisodeStub episode = episodeStubs.First();
                                                mergedEpisode                      = new Stubs.SeriesEpisodeStub();
                                                mergedEpisode.Actors               = episode.Actors;
                                                mergedEpisode.Aired                = episode.Aired;
                                                mergedEpisode.Credits              = episode.Credits;
                                                mergedEpisode.Director             = episode.Director;
                                                mergedEpisode.DisplayEpisode       = episode.DisplayEpisode;
                                                mergedEpisode.DisplaySeason        = episode.DisplaySeason;
                                                mergedEpisode.EpBookmark           = episode.EpBookmark;
                                                mergedEpisode.FileInfo             = episode.FileInfo;
                                                mergedEpisode.LastPlayed           = episode.LastPlayed;
                                                mergedEpisode.Mpaa                 = episode.Mpaa;
                                                mergedEpisode.PlayCount            = episode.PlayCount;
                                                mergedEpisode.Premiered            = episode.Premiered;
                                                mergedEpisode.ProductionCodeNumber = episode.ProductionCodeNumber;
                                                mergedEpisode.ResumePosition       = episode.ResumePosition;
                                                mergedEpisode.Season               = episode.Season;
                                                mergedEpisode.Sets                 = episode.Sets;
                                                mergedEpisode.ShowTitle            = episode.ShowTitle;
                                                mergedEpisode.Status               = episode.Status;
                                                mergedEpisode.Studio               = episode.Studio;
                                                mergedEpisode.Tagline              = episode.Tagline;
                                                mergedEpisode.Thumb                = episode.Thumb;
                                                mergedEpisode.Top250               = episode.Top250;
                                                mergedEpisode.Trailer              = episode.Trailer;
                                                mergedEpisode.Watched              = episode.Watched;
                                                mergedEpisode.Year                 = episode.Year;
                                                mergedEpisode.Id                   = episode.Id;
                                                mergedEpisode.UniqueId             = episode.UniqueId;

                                                //Merge episodes
                                                mergedEpisode.Title   = string.Join("; ", episodeStubs.OrderBy(e => e.Episodes.First()).Select(e => e.Title).ToArray());
                                                mergedEpisode.Rating  = episodeStubs.Where(e => e.Rating.HasValue).Sum(e => e.Rating.Value) / episodeStubs.Where(e => e.Rating.HasValue).Count(); // Average rating
                                                mergedEpisode.Votes   = episodeStubs.Where(e => e.Votes.HasValue).Sum(e => e.Votes.Value) / episodeStubs.Where(e => e.Votes.HasValue).Count();
                                                mergedEpisode.Runtime = TimeSpan.FromSeconds(episodeStubs.Where(e => e.Runtime.HasValue).Sum(e => e.Runtime.Value.TotalSeconds));
                                                mergedEpisode.Plot    = string.Join("\r\n\r\n", episodeStubs.OrderBy(e => e.Episodes.First()).
                                                                                    Select(e => string.Format("{0,02}) {1}", e.Episodes.First(), e.Plot)).ToArray());
                                                mergedEpisode.Outline = string.Join("\r\n\r\n", episodeStubs.OrderBy(e => e.Episodes.First()).
                                                                                    Select(e => string.Format("{0,02}) {1}", e.Episodes.First(), e.Outline)).ToArray());
                                                mergedEpisode.Episodes    = new HashSet <int>(episodeStubs.SelectMany(x => x.Episodes).ToList());
                                                mergedEpisode.DvdEpisodes = new HashSet <decimal>(episodeStubs.SelectMany(x => x.DvdEpisodes).ToList());
                                            }

                                            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 (mergedEpisode.FileInfo != null && mergedEpisode.FileInfo.Count > 0)
                                                {
                                                    mime = MimeTypeDetector.GetMimeTypeFromExtension("file" + mergedEpisode.FileInfo.First().Container);
                                                }
                                                if (mime != null)
                                                {
                                                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mime);
                                                }
                                            }

                                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, string.Format("{0} S{1:00}{2} {3}", series.ShowTitle, mergedEpisode.Season, mergedEpisode.Episodes.Select(e => "E" + e.ToString("00")), mergedEpisode.Title));
                                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(mergedEpisode.Title));
                                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, mergedEpisode.Premiered.HasValue ? mergedEpisode.Premiered.Value : mergedEpisode.Year.HasValue ? mergedEpisode.Year.Value : (DateTime?)null);

                                            if (mergedEpisode.FileInfo != null && mergedEpisode.FileInfo.Count > 0)
                                            {
                                                extractedAspectData.Remove(VideoStreamAspect.ASPECT_ID);
                                                extractedAspectData.Remove(VideoAudioStreamAspect.ASPECT_ID);
                                                extractedAspectData.Remove(SubtitleAspect.ASPECT_ID);
                                                StubParser.ParseFileInfo(extractedAspectData, mergedEpisode.FileInfo, mergedEpisode.Title);
                                            }
                                        }

                                        episodeNfoReader = new NfoSeriesEpisodeReader(_debugLogger, miNumber, forceQuickMode, isStub, _httpClient, _settings);
                                        episodeNfoReader.SetEpisodeStubs(new List <Stubs.SeriesEpisodeStub> {
                                            mergedEpisode
                                        });
                                    }
                                }
                            }
                            if (series != null)
                            {
                                if (episodeNfoReader != null)
                                {
                                    episodeNfoReader.SetSeriesStubs(new List <Stubs.SeriesStub> {
                                        series
                                    });

                                    // Then we store the found metadata in the MediaItemAspects. If we only found metadata that is
                                    // not (yet) supported by our MediaItemAspects, this MetadataExtractor returns false.
                                    if (!episodeNfoReader.TryWriteMetadata(extractedAspectData))
                                    {
                                        _debugLogger.Warn("[#{0}]: No metadata was written into MediaItemsAspects", miNumber);
                                        return(false);
                                    }
                                }
                                else
                                {
                                    EpisodeInfo episode = new EpisodeInfo();
                                    if (series.Id.HasValue)
                                    {
                                        episode.SeriesTvdbId = series.Id.Value;
                                    }
                                    if (series.Premiered.HasValue)
                                    {
                                        episode.SeriesFirstAired = series.Premiered.Value;
                                    }
                                    episode.SeriesName = series.ShowTitle;
                                    episode.SetMetadata(extractedAspectData);
                                }
                            }
                        }
                        else
                        {
                            _debugLogger.Warn("[#{0}]: No valid metadata found in series nfo-file", miNumber);
                        }
                    }
                }
                else if (episodeNfoReader != null)
                {
                    //Check reimport
                    if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID))
                    {
                        EpisodeInfo reimport = new EpisodeInfo();
                        reimport.FromMetadata(extractedAspectData);
                        if (!VerifyEpisodeReimport(episodeNfoReader, reimport))
                        {
                            ServiceRegistration.Get <ILogger>().Info("NfoSeriesMetadataExtractor: Nfo episode metadata from resource '{0}' ignored because it does not match reimport {1}", mediaItemAccessor, reimport);
                            return(false);
                        }
                    }

                    // Then we store the found metadata in the MediaItemAspects. If we only found metadata that is
                    // not (yet) supported by our MediaItemAspects, this MetadataExtractor returns false.
                    if (!episodeNfoReader.TryWriteMetadata(extractedAspectData))
                    {
                        _debugLogger.Warn("[#{0}]: No metadata was written into MediaItemsAspects", miNumber);
                        return(false);
                    }
                }

                _debugLogger.Info("[#{0}]: Successfully finished extracting metadata", miNumber);
                ServiceRegistration.Get <ILogger>().Debug("NfoSeriesMetadataExtractor: Assigned nfo episode metadata for resource '{0}'", mediaItemAccessor);
                return(true);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Warn("NfoSeriesMetadataExtractor: 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);
            }
        }
Example #4
0
        public Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID))
            {
                return(Task.FromResult(false));
            }
            if (!CanExtract(mediaItemAccessor, extractedAspectData))
            {
                return(Task.FromResult(false));
            }

            try
            {
                IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor;
                using (MediaInfoWrapper mediaInfo = ReadMediaInfo(fsra))
                {
                    // Before we start evaluating the file, check if it is not a video file
                    if (mediaInfo.IsValid && (mediaInfo.GetVideoCount() != 0 || mediaInfo.GetAudioCount() == 0))
                    {
                        return(Task.FromResult(false));
                    }

                    string fileName = ProviderPathHelper.GetFileNameWithoutExtension(fsra.Path) ?? string.Empty;
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, fileName);
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(fileName));

                    MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata);
                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size);
                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/radio");

                    var audioBitrate = mediaInfo.GetAudioBitrate(0);
                    if (audioBitrate.HasValue)
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_BITRATE, (int)(audioBitrate.Value / 1000)); // We store kbit/s;
                    }
                    var audioChannels = mediaInfo.GetAudioChannels(0);
                    if (audioChannels.HasValue)
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_CHANNELS, audioChannels.Value);
                    }
                    var audioSampleRate = mediaInfo.GetAudioSampleRate(0);
                    if (audioSampleRate.HasValue)
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_SAMPLERATE, audioSampleRate.Value);
                    }

                    MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_ENCODING, mediaInfo.GetAudioCodec(0));
                    // MediaInfo returns milliseconds, we need seconds
                    long?time = mediaInfo.GetPlaytime(0);
                    if (time.HasValue && time > 1000)
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_DURATION, time.Value / 1000);
                    }
                }
                return(Task.FromResult(true));
            }
            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("RadioRecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message);
                return(Task.FromResult(false));
            }
        }
        private async Task <bool> TryExtractStubItemsAsync(IResourceAccessor mediaItemAccessor, ICollection <IDictionary <Guid, IList <MediaItemAspect> > > extractedStubAspectData)
        {
            // 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 stubs for resource '{1}'", miNumber, mediaItemAccessor);

                if (!IsStubResource(mediaItemAccessor))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract stubs; file does not have a supported extension", miNumber);
                    return(false);
                }

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

                var fsra            = mediaItemAccessor as IFileSystemResourceAccessor;
                var albumStubReader = new StubAlbumReader(_debugLogger, miNumber, true, _settings);
                if (fsra != null && await albumStubReader.TryReadMetadataAsync(fsra).ConfigureAwait(false))
                {
                    AlbumStub album = albumStubReader.GetAlbumStubs().FirstOrDefault();
                    if (album != null && album.Tracks != null && album.Tracks > 0)
                    {
                        for (int trackNo = 1; trackNo <= album.Tracks.Value; trackNo++)
                        {
                            Dictionary <Guid, IList <MediaItemAspect> > extractedAspectData = new Dictionary <Guid, IList <MediaItemAspect> >();
                            string title = string.Format("{0}: {1}", album.Title, "Track " + trackNo);

                            MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata);
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_STUB);
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, fsra.CanonicalLocalResourcePath.Serialize());
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "audio/L16");

                            SingleMediaItemAspect audioAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, AudioAspect.Metadata);
                            audioAspect.SetAttribute(AudioAspect.ATTR_ISCD, true);
                            audioAspect.SetAttribute(AudioAspect.ATTR_TRACK, trackNo);
                            audioAspect.SetAttribute(AudioAspect.ATTR_TRACKNAME, title);
                            audioAspect.SetAttribute(AudioAspect.ATTR_ENCODING, "PCM");
                            if (album.Cd.HasValue)
                            {
                                audioAspect.SetAttribute(AudioAspect.ATTR_DISCID, album.Cd.Value);
                            }
                            audioAspect.SetAttribute(AudioAspect.ATTR_BITRATE, 1411); // 44.1 kHz * 16 bit * 2 channel
                            audioAspect.SetAttribute(AudioAspect.ATTR_CHANNELS, 2);
                            audioAspect.SetAttribute(AudioAspect.ATTR_NUMTRACKS, album.Tracks.Value);
                            audioAspect.SetAttribute(AudioAspect.ATTR_ALBUM, album.Title);
                            if (album.Artists.Count > 0)
                            {
                                audioAspect.SetCollectionAttribute(AudioAspect.ATTR_ALBUMARTISTS, album.Artists);
                            }

                            SingleMediaItemAspect stubAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, StubAspect.Metadata);
                            stubAspect.SetAttribute(StubAspect.ATTR_DISC_NAME, album.DiscName);
                            stubAspect.SetAttribute(StubAspect.ATTR_MESSAGE, album.Message);

                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(title));
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISSTUB, true);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, fsra.LastChanged);

                            extractedStubAspectData.Add(extractedAspectData);
                        }
                    }
                }
                else
                {
                    _debugLogger.Warn("[#{0}]: No valid metadata found in album stub file", miNumber);
                }


                _debugLogger.Info("[#{0}]: Successfully finished extracting stubs", miNumber);
                return(true);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Warn("StubAudioMetadataExtractor: Exception while extracting stubs for resource '{0}'; enable debug logging for more details.", mediaItemAccessor);
                _debugLogger.Error("[#{0}]: Exception while extracting stubs", e, miNumber);
                return(false);
            }
        }
        /// <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> TryExtractMovieMetadataAsync(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);

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

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

                // 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(VideoAspect.ASPECT_ID))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract metadata; this resource is not a video", miNumber);
                    return(false);
                }

                // Here we try to find an IFileSystemResourceAccessor pointing to the nfo-file.
                // If we don't find one, we cannot extract any metadata.
                IFileSystemResourceAccessor nfoFsra;
                if (!TryGetNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out nfoFsra))
                {
                    return(false);
                }

                // Now we (asynchronously) extract the metadata into a stub object.
                // If there is an error parsing the nfo-file with XmlNfoReader, we at least try to parse for a valid IMDB-ID.
                // If no metadata was found, nothing can be stored in the MediaItemAspects.
                NfoMovieReader nfoReader = new NfoMovieReader(_debugLogger, miNumber, false, forceQuickMode, isStub, _httpClient, _settings);
                using (nfoFsra)
                {
                    if (!await nfoReader.TryReadMetadataAsync(nfoFsra).ConfigureAwait(false) &&
                        !await nfoReader.TryParseForImdbId(nfoFsra).ConfigureAwait(false))
                    {
                        _debugLogger.Warn("[#{0}]: No valid metadata found", miNumber);
                        return(false);
                    }
                    else if (isStub)
                    {
                        Stubs.MovieStub movie = nfoReader.GetMovieStubs().FirstOrDefault();
                        if (movie != null)
                        {
                            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 (movie.FileInfo != null && movie.FileInfo.Count > 0)
                                {
                                    mime = MimeTypeDetector.GetMimeTypeFromExtension("file" + movie.FileInfo.First().Container);
                                }
                                if (mime != null)
                                {
                                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mime);
                                }
                            }

                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, movie.Title);
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, movie.SortTitle != null ? movie.SortTitle : BaseInfo.GetSortTitle(movie.Title));
                            MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, movie.Premiered.HasValue ? movie.Premiered.Value : movie.Year.HasValue ? movie.Year.Value : (DateTime?)null);

                            if (movie.FileInfo != null && movie.FileInfo.Count > 0)
                            {
                                extractedAspectData.Remove(VideoStreamAspect.ASPECT_ID);
                                extractedAspectData.Remove(VideoAudioStreamAspect.ASPECT_ID);
                                extractedAspectData.Remove(SubtitleAspect.ASPECT_ID);
                                StubParser.ParseFileInfo(extractedAspectData, movie.FileInfo, movie.Title, movie.Fps);
                            }
                        }
                    }
                }

                //Check reimport
                if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID))
                {
                    MovieInfo reimport = new MovieInfo();
                    reimport.FromMetadata(extractedAspectData);
                    if (!VerifyMovieReimport(nfoReader, reimport))
                    {
                        ServiceRegistration.Get <ILogger>().Info("NfoMovieMetadataExtractor: Nfo movie metadata from resource '{0}' ignored because it does not match reimport {1}", mediaItemAccessor, reimport);
                        return(false);
                    }
                }

                // Then we store the found metadata in the MediaItemAspects. If we only found metadata that is
                // not (yet) supported by our MediaItemAspects, this MetadataExtractor returns false.
                if (!nfoReader.TryWriteMetadata(extractedAspectData))
                {
                    _debugLogger.Warn("[#{0}]: No metadata was written into MediaItemsAspects", miNumber);
                    return(false);
                }

                _debugLogger.Info("[#{0}]: Successfully finished extracting metadata", miNumber);
                ServiceRegistration.Get <ILogger>().Debug("NfoMovieMetadataExtractor: Assigned nfo movie metadata for resource '{0}'", mediaItemAccessor);
                return(true);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Warn("NfoMovieMetadataExtractor: 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);
            }
        }
Example #7
0
        /// <summary>
        /// Copies the contained movie information into MediaItemAspect.
        /// </summary>
        /// <param name="aspectData">Dictionary with extracted aspects.</param>
        public bool SetMetadata(IDictionary <Guid, IList <MediaItemAspect> > aspectData, ILocalFsResourceAccessor lfsra)
        {
            MediaItemAspect.GetOrCreateAspect(aspectData, GameAspect.Metadata);
            MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(aspectData, ProviderResourceAspect.Metadata);

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_PRIMARY);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, GameCategory.CategoryNameToMimeType(Platform));
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, lfsra.Size);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, lfsra.CanonicalLocalResourcePath.Serialize());
            MediaItemAspect.SetAttribute(aspectData, MediaAspect.ATTR_ISVIRTUAL, false);

            if (!string.IsNullOrEmpty(GameName))
            {
                MediaItemAspect.SetAttribute(aspectData, MediaAspect.ATTR_TITLE, GameName);
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_GAME_NAME, GameName);
                MediaItemAspect.SetAttribute(aspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(GameName));
            }
            if (ReleaseDate.HasValue)
            {
                MediaItemAspect.SetAttribute(aspectData, MediaAspect.ATTR_RECORDINGTIME, ReleaseDate);
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_YEAR, ReleaseDate.Value.Year);
            }

            if (!string.IsNullOrEmpty(Platform))
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_PLATFORM, Platform);
            }
            if (!string.IsNullOrEmpty(PlatformId))
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_PLATFORM_ID, PlatformId);
            }
            if (MatcherId != Guid.Empty)
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_MATCHER_ID, MatcherId);
            }
            if (!string.IsNullOrEmpty(OnlineId))
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_ONLINE_ID, OnlineId);
            }
            if (GamesDbId > 0)
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_TGDB_ID, GamesDbId);
            }
            if (!string.IsNullOrEmpty(Description))
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_DESCRIPTION, Description);
            }
            if (!string.IsNullOrEmpty(Certification))
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_CERTIFICATION, Certification);
            }
            if (!string.IsNullOrEmpty(Developer))
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_DEVELOPER, Developer);
            }
            if (Rating > 0d)
            {
                MediaItemAspect.SetAttribute(aspectData, GameAspect.ATTR_RATING, Rating);
            }
            if (Genres.Count > 0)
            {
                MediaItemAspect.SetCollectionAttribute(aspectData, GameAspect.ATTR_GENRES, Genres);
            }
            return(true);
        }
Example #8
0
        private async Task <bool> TryExtractStubItemsAsync(IResourceAccessor mediaItemAccessor, ICollection <IDictionary <Guid, IList <MediaItemAspect> > > extractedStubAspectData)
        {
            // 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 stubs for resource '{1}'", miNumber, mediaItemAccessor);

                if (!IsStubResource(mediaItemAccessor))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract stubs; file does not have a supported extension", miNumber);
                    return(false);
                }

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

                var fsra             = mediaItemAccessor as IFileSystemResourceAccessor;
                var seriesStubReader = new StubSeriesReader(_debugLogger, miNumber, true, _settings);
                if (fsra != null && await seriesStubReader.TryReadMetadataAsync(fsra).ConfigureAwait(false))
                {
                    SeriesStub series = seriesStubReader.GetSeriesStubs().FirstOrDefault();
                    if (series != null && series.Episodes != null && series.Episodes.Count > 0)
                    {
                        Dictionary <Guid, IList <MediaItemAspect> > extractedAspectData = new Dictionary <Guid, IList <MediaItemAspect> >();
                        string title = string.Format("{0} S{1:00}{2}", series.Title, series.Season.Value, string.Join("", series.Episodes.Select(e => "E" + e.ToString("00"))));

                        MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata);
                        providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
                        providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_STUB);
                        providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, fsra.CanonicalLocalResourcePath.Serialize());
                        if (IsVhs(mediaItemAccessor))
                        {
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/unknown");

                            MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_SD);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(4.0 / 3.0));
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 25);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 720);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 576);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1);

                            MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 2);
                        }
                        else if (IsTv(mediaItemAccessor))
                        {
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/unknown");

                            MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_HD);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0));
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 25F);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 1920);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 1080);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1);

                            MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 2);
                        }
                        else if (IsDvd(mediaItemAccessor))
                        {
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/mp2t");

                            MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_SD);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0));
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 25F);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 720);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 576);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEOENCODING, "MPEG-2 Video");
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1);

                            MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOENCODING, "AC3");
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 6);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOSAMPLERATE, 48000L);
                        }
                        else if (IsBluray(mediaItemAccessor))
                        {
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/mp4");

                            MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_HD);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0));
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 24F);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 1920);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 1080);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEOENCODING, "AVC");
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1);

                            MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOENCODING, "AC3");
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 6);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOSAMPLERATE, 48000L);
                        }
                        else if (IsHdDvd(mediaItemAccessor))
                        {
                            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/wvc1");

                            MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_TYPE, VideoStreamAspect.TYPE_HD);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_ASPECTRATIO, Convert.ToSingle(16.0 / 9.0));
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, 24F);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 1920);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 1080);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEOENCODING, "VC1");
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_AUDIOSTREAMCOUNT, 1);
                            videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_VIDEO_PART, 1);

                            MultipleMediaItemAspect audioAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoAudioStreamAspect.Metadata);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_RESOURCE_INDEX, 0);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_STREAM_INDEX, 1);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOENCODING, "AC3");
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOCHANNELS, 6);
                            audioAspect.SetAttribute(VideoAudioStreamAspect.ATTR_AUDIOSAMPLERATE, 48000L);
                        }

                        SingleMediaItemAspect videoAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, VideoAspect.Metadata);
                        videoAspect.SetAttribute(VideoAspect.ATTR_ISDVD, true);

                        SingleMediaItemAspect movieAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, EpisodeAspect.Metadata);
                        movieAspect.SetCollectionAttribute(EpisodeAspect.ATTR_EPISODE, series.Episodes);
                        movieAspect.SetAttribute(EpisodeAspect.ATTR_SEASON, series.Season.Value);
                        movieAspect.SetAttribute(EpisodeAspect.ATTR_EPISODE_NAME, string.Format("{0} {1}", "Episode", string.Join(", ", series.Episodes)));
                        movieAspect.SetAttribute(EpisodeAspect.ATTR_SERIES_NAME, series.Title);
                        movieAspect.SetAttribute(EpisodeAspect.ATTR_SERIES_SEASON, string.Format("{0} S{1:00}", series.Title, series.Season.Value));

                        SingleMediaItemAspect stubAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, StubAspect.Metadata);
                        stubAspect.SetAttribute(StubAspect.ATTR_DISC_NAME, series.DiscName);
                        stubAspect.SetAttribute(StubAspect.ATTR_MESSAGE, series.Message);

                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, series.Title);
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(series.Title));
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISSTUB, true);
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, fsra.LastChanged);

                        extractedStubAspectData.Add(extractedAspectData);
                    }
                }
                else
                {
                    _debugLogger.Warn("[#{0}]: No valid metadata found in movie stub file", miNumber);
                }


                _debugLogger.Info("[#{0}]: Successfully finished extracting stubs", miNumber);
                return(true);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Warn("StubMovieMetadataExtractor: Exception while extracting stubs for resource '{0}'; enable debug logging for more details.", mediaItemAccessor);
                _debugLogger.Error("[#{0}]: Exception while extracting stubs", e, miNumber);
                return(false);
            }
        }
        public virtual bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode)
        {
            try
            {
                IResourceAccessor metaFileAccessor;
                if (!CanExtract(mediaItemAccessor, extractedAspectData, out metaFileAccessor))
                {
                    return(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))
                {
                    List <GenreInfo> genreList = new List <GenreInfo>(new GenreInfo[] { new GenreInfo {
                                                                                            Name = value
                                                                                        } });
                    OnlineMatcherService.Instance.AssignMissingMovieGenreIds(genreList);
                    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 recordingStart;
                DateTime recordingEnd;
                if (TryGet(tags, TAG_STARTTIME, out value) && DateTime.TryParse(value, out recordingStart))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, recordingStart);
                }

                if (TryGet(tags, TAG_ENDTIME, out value) && DateTime.TryParse(value, out recordingEnd))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, recordingEnd);
                }

                return(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(false);
        }
Example #10
0
 /// <summary>
 /// Tries to write metadata into <see cref="MediaAspect.ATTR_SORT_TITLE"/>
 /// </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 TryWriteMediaAspectSortTitle(IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
 {
     if (_stubs[0].Title != null)
     {
         MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, BaseInfo.GetSortTitle(_stubs[0].Title));
         return(true);
     }
     return(false);
 }
Example #11
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));
        }
        public static LiveTvMediaItem.LiveTvMediaItem CreateRecordingMediaItem(int slotIndex, string path, IProgram program, IChannel channel)
        {
            if (!String.IsNullOrEmpty(path))
            {
                var tvStream = CreateCommonMediaItem(slotIndex, path, true);

                MediaItemAspect.SetAttribute(tvStream.Aspects, MediaAspect.ATTR_TITLE, program.Title); // Override with real program name
                MediaItemAspect.SetAttribute(tvStream.Aspects, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(program.Title));
                MediaItemAspect.SetAttribute(tvStream.Aspects, MediaAspect.ATTR_ISVIRTUAL, false);
                tvStream.AdditionalProperties[LiveTvMediaItem.LiveTvMediaItem.SLOT_INDEX]      = slotIndex;
                tvStream.AdditionalProperties[LiveTvMediaItem.LiveTvMediaItem.CHANNEL]         = channel;
                tvStream.AdditionalProperties[LiveTvMediaItem.LiveTvMediaItem.CURRENT_PROGRAM] = program;
                return(tvStream);
            }
            return(null);
        }
Example #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 virtual bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode)
        {
            IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor;

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

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

            bool refresh = false;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                trackInfo.AssignNameId();

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

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

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

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

                trackInfo.SetMetadata(extractedAspectData);

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

                return(trackInfo.IsBaseInfoPresent);
            }
            catch (UnsupportedFormatException)
            {
                ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Unsupported audio file '{0}'", fsra.CanonicalLocalResourcePath);
                return(false);
            }
            catch (Exception e)
            {
                // Only log at the info level here - And simply return false. This makes the importer know that we
                // couldn't perform our task here
                ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message);
            }
            return(false);
        }
        protected async Task <bool> ExtractSeriesDataAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData)
        {
            // VideoAspect must be present to be sure it is actually a video resource.
            if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID))
            {
                return(false);
            }

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

            EpisodeInfo episodeInfo = new EpisodeInfo();

            episodeInfo.FromMetadata(extractedAspectData);

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

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

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

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

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

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

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

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

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

            return(episodeInfo.IsBaseInfoPresent);
        }
Example #16
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));
        }
        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));
        }
Example #18
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);
            }
        }