/// <summary> /// Tries to write metadata into <see cref="GenreAspect.ATTR_GENRE"/> /// </summary> /// <param name="extractedAspectData">Dictionary of <see cref="MediaItemAspect"/>s to write into</param> /// <returns><c>true</c> if any information was written; otherwise <c>false</c></returns> private bool TryWriteAlbumAspectGenres(IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData) { if (_stubs[0].Genres != null && _stubs[0].Genres.Any()) { List <GenreInfo> genres = _stubs[0].Genres.Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo { Name = s.Trim() }).ToList(); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId)) { genre.Id = genreId; } } foreach (GenreInfo genre in genres) { MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata); genreAspect.SetAttribute(GenreAspect.ATTR_ID, genre.Id); genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre.Name); } return(true); } return(false); }
public EpisodeInfo GetSeriesFromTags(IDictionary metadata) { EpisodeInfo episodeInfo = new EpisodeInfo(); string tmpString; if (TryGet(metadata, TAG_TITLE, out tmpString)) { episodeInfo.SeriesName.Text = tmpString; } if (TryGet(metadata, TAG_EPISODENAME, out tmpString)) { episodeInfo.EpisodeName.Text = tmpString; } if (TryGet(metadata, TAG_GENRE, out tmpString)) { episodeInfo.Genres = new List <GenreInfo>(tmpString.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenreInfo { Name = s.Trim() })); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in episodeInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId)) { genre.Id = genreId; } } } episodeInfo.HasChanged = true; return(episodeInfo); }
public virtual async Task <bool> UpdateCollectionAsync(MovieCollectionInfo movieCollectionInfo, bool updateMovieList) { try { // Try online lookup if (!await InitAsync().ConfigureAwait(false)) { return(false); } TLang language = FindBestMatchingLanguage(movieCollectionInfo.Languages); bool updated = false; MovieCollectionInfo movieCollectionMatch = movieCollectionInfo.Clone(); movieCollectionMatch.Movies.Clear(); //Try updating from cache if (!await _wrapper.UpdateFromOnlineMovieCollectionAsync(movieCollectionMatch, language, true).ConfigureAwait(false)) { Logger.Debug(_id + ": Search for collection {0} online", movieCollectionInfo.ToString()); //Try to update movie collection information from online source if (await _wrapper.UpdateFromOnlineMovieCollectionAsync(movieCollectionMatch, language, false).ConfigureAwait(false)) { updated = true; } } else { Logger.Debug(_id + ": Found collection {0} in cache", movieCollectionInfo.ToString()); updated = true; } if (updated) { movieCollectionInfo.MergeWith(movieCollectionMatch, true, updateMovieList); if (updateMovieList) { foreach (MovieInfo movie in movieCollectionMatch.Movies) { IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in movie.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId)) { genre.Id = genreId; } } } } } return(updated); } catch (Exception ex) { Logger.Debug(_id + ": Exception while processing collection {0}", ex, movieCollectionInfo.ToString()); return(false); } }
public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects) { TrackInfo trackInfo = new TrackInfo(); if (!trackInfo.FromMetadata(aspects)) { return(false); } AlbumInfo reimport = null; if (aspects.ContainsKey(ReimportAspect.ASPECT_ID)) { reimport = trackInfo.CloneBasicInstance <AlbumInfo>(); } IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData = extractedLinkedAspects.Count > 0 ? extractedLinkedAspects[0] : new Dictionary <Guid, IList <MediaItemAspect> >(); if (!await TryExtractAlbumMetadataAsync(mediaItemAccessor, extractedAspectData, reimport).ConfigureAwait(false)) { return(false); } AlbumInfo albumInfo = new AlbumInfo(); if (!albumInfo.FromMetadata(extractedAspectData)) { return(false); } IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in albumInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId)) { genre.Id = genreId; } } albumInfo.SetMetadata(extractedAspectData); if (!extractedAspectData.ContainsKey(ExternalIdentifierAspect.ASPECT_ID)) { return(false); } bool trackVirtual; if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out trackVirtual)) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, trackVirtual); } extractedLinkedAspects.Clear(); extractedLinkedAspects.Add(extractedAspectData); return(true); }
public EpisodeInfo GetSeriesFromTags(Argus.Recording recording) { EpisodeInfo episodeInfo = new EpisodeInfo { SeriesName = recording.Title }; if (recording.SeriesNumber.HasValue) { episodeInfo.SeasonNumber = recording.SeriesNumber.Value; } if (recording.EpisodeNumber.HasValue) { episodeInfo.EpisodeNumbers.Add(recording.EpisodeNumber.Value); } if (!episodeInfo.IsBaseInfoPresent) { // Check for formatted display value, i.e.: // <EpisodeNumberDisplay>1.4</EpisodeNumberDisplay> if (!string.IsNullOrWhiteSpace(recording.EpisodeNumberDisplay)) { var parts = recording.EpisodeNumberDisplay.Split('.'); if (parts.Length == 2) { int val; if (int.TryParse(parts[0], out val)) { episodeInfo.SeasonNumber = val; } if (int.TryParse(parts[1], out val)) { episodeInfo.EpisodeNumbers.Add(val); } } } } if (!string.IsNullOrEmpty(recording.Category?.Trim())) { episodeInfo.Genres.Add(new GenreInfo { Name = recording.Category.Trim() }); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in episodeInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId)) { genre.Id = genreId; } } } episodeInfo.HasChanged = true; return(episodeInfo); }
public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects) { EpisodeInfo episodeInfo = new EpisodeInfo(); if (!episodeInfo.FromMetadata(aspects)) { return(false); } SeriesInfo seriesInfo = RelationshipExtractorUtils.TryCreateInfoFromLinkedAspects(extractedLinkedAspects, out List <SeriesInfo> series) ? series[0] : episodeInfo.CloneBasicInstance <SeriesInfo>(); if (!SeriesMetadataExtractor.SkipOnlineSearches) { await OnlineMatcherService.Instance.UpdateSeriesAsync(seriesInfo, false, _category).ConfigureAwait(false); } if (seriesInfo.Genres.Count > 0) { IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in seriesInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId)) { genre.Id = genreId; seriesInfo.HasChanged = true; } } } IDictionary <Guid, IList <MediaItemAspect> > seriesAspects = seriesInfo.LinkedAspects != null ? seriesInfo.LinkedAspects : new Dictionary <Guid, IList <MediaItemAspect> >(); seriesInfo.SetMetadata(seriesAspects); if (aspects.ContainsKey(EpisodeAspect.ASPECT_ID)) { bool episodeVirtual = true; if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out episodeVirtual)) { MediaItemAspect.SetAttribute(seriesAspects, MediaAspect.ATTR_ISVIRTUAL, episodeVirtual); } } if (!seriesAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID)) { return(false); } if (seriesInfo.LinkedAspects == null) { extractedLinkedAspects.Add(seriesAspects); } return(true); }
public EpisodeInfo GetSeriesFromTags(Tags extractedTags) { EpisodeInfo episodeInfo = new EpisodeInfo(); string tmpString; int tmpInt; if (TryGet(extractedTags, TAG_TITLE, out tmpString)) { episodeInfo.SeriesName = tmpString; } if (TryGet(extractedTags, TAG_EPISODENAME, out tmpString)) { episodeInfo.EpisodeName = tmpString; } if (TryGet(extractedTags, TAG_SERIESNUM, out tmpString) && int.TryParse(tmpString, out tmpInt)) { episodeInfo.SeasonNumber = tmpInt; } if (TryGet(extractedTags, TAG_EPISODENUM, out tmpString)) { int episodeNum; if (int.TryParse(tmpString, out episodeNum)) { episodeInfo.EpisodeNumbers.Add(episodeNum); } } if (TryGet(extractedTags, TAG_GENRE, out tmpString) && !string.IsNullOrEmpty(tmpString?.Trim())) { episodeInfo.Genres = new List <GenreInfo>(new GenreInfo[] { new GenreInfo { Name = tmpString.Trim() } }); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in episodeInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Series, null, out int genreId)) { genre.Id = genreId; } } } episodeInfo.HasChanged = true; return(episodeInfo); }
public virtual Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { try { IResourceAccessor metaFileAccessor; if (!CanExtract(mediaItemAccessor, extractedAspectData, out metaFileAccessor)) { return(Task.FromResult(false)); } Argus.Recording recording; using (metaFileAccessor) { using (Stream metaStream = ((IFileSystemResourceAccessor)metaFileAccessor).OpenRead()) recording = (Argus.Recording)GetTagsXmlSerializer().Deserialize(metaStream); } // Force MimeType IList <MultipleMediaItemAspect> providerAspects; MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerAspects); foreach (MultipleMediaItemAspect aspect in providerAspects) { aspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/arg"); } MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false); MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, recording.Title); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(recording.Title)); if (!string.IsNullOrEmpty(recording.Category?.Trim())) { List <GenreInfo> genreList = new List <GenreInfo>(new GenreInfo[] { new GenreInfo { Name = recording.Category.Trim() } }); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in genreList) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId)) { genre.Id = genreId; } } if (genreList.Count > 0) { MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata); genreAspect.SetAttribute(GenreAspect.ATTR_ID, genreList[0].Id); genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genreList[0].Name); } } MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, recording.Description); Match yearMatch = _yearMatcher.Match(recording.Description); int guessedYear; if (int.TryParse(yearMatch.Value, out guessedYear)) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(guessedYear, 1, 1)); } MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, recording.ChannelDisplayName); MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, recording.ProgramStartTime); MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, recording.ProgramStopTime); RecordingUtils.CheckAndPrepareAspectRefresh(extractedAspectData); if (!string.IsNullOrWhiteSpace(recording.Director)) { MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_DIRECTORS, recording.Director.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); } if (!string.IsNullOrWhiteSpace(recording.Actors)) { MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_ACTORS, recording.Actors.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); } return(Task.FromResult(true)); } catch (Exception e) { // Only log at the info level here - And simply return false. This lets the caller know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Info("ArgusRecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message); } return(Task.FromResult(false)); }
protected virtual Task <bool> ExtractMetadataAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { if (!CanExtract(lfsra, extractedAspectData)) { return(Task.FromResult(false)); } using (var rec = new MCRecMetadataEditor(lfsra.LocalFileSystemPath)) { // Handle series information IDictionary tags = rec.GetAttributes(); // Force MimeType IList <MultipleMediaItemAspect> providerAspects; MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerAspects); foreach (MultipleMediaItemAspect aspect in providerAspects) { aspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/wtv"); } MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false); MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false); string value; if (TryGet(tags, TAG_TITLE, out value) && !string.IsNullOrEmpty(value)) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, value); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(value)); } if (TryGet(tags, TAG_GENRE, out value)) { List <GenreInfo> genreList = new List <GenreInfo>(value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenreInfo { Name = s.Trim() })); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in genreList) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId)) { genre.Id = genreId; } } foreach (GenreInfo genre in genreList) { MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata); genreAspect.SetAttribute(GenreAspect.ATTR_ID, genre.Id); genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre.Name); } } if (TryGet(tags, TAG_PLOT, out value)) { MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, value); } if (TryGet(tags, TAG_ORIGINAL_TIME, out value)) { DateTime origTime; if (DateTime.TryParse(value, out origTime)) { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, origTime); } } if (TryGet(tags, TAG_CHANNEL, out value)) { MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, value); } long lValue; if (TryGet(tags, TAG_STARTTIME, out lValue)) { MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, FromMCEFileTime(lValue)); } if (TryGet(tags, TAG_ENDTIME, out lValue)) { MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, FromMCEFileTime(lValue)); } } return(Task.FromResult(true)); }
protected override void InitGenreMap() { if (_tvGenresInited) { return; } _tvGenresInited = true; string genre; bool enabled; IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); if (converter == null) { return; } // Get the id of the mp genre identified as the movie genre. int genreMapMovieGenreId; if (!int.TryParse(_tvBusiness.GetSetting("genreMapMovieGenreId").Value, out genreMapMovieGenreId)) { genreMapMovieGenreId = -1; } // Each genre map value is a '{' delimited list of "program" genre names (those that may be compared with the genre from the program listings). // It is an error if a single "program" genre is mapped to more than one guide genre; behavior is undefined for this condition. int genreIndex = 0; while (true) { // The genremap key is an integer value that is added to a base value in order to locate the correct localized genre name string. genre = _tvBusiness.GetSetting("genreMapName" + genreIndex).Value; if (string.IsNullOrEmpty(genre)) { break; } // Get the status of the mp genre. if (!bool.TryParse(_tvBusiness.GetSetting("genreMapNameEnabled" + genreIndex).Value, out enabled)) { enabled = true; } EpgGenre?epgGenre = null; if (enabled && genreIndex == genreMapMovieGenreId) { epgGenre = EpgGenre.Movie; } else if (enabled && !string.IsNullOrEmpty(genre)) { if (converter.GetGenreId(genre, GenreCategory.Epg, null, out int genreId)) { epgGenre = (EpgGenre)genreId; } } if (epgGenre.HasValue) { string genreMapEntry = _tvBusiness.GetSetting("genreMapEntry" + genreIndex).Value; if (!string.IsNullOrEmpty(genreMapEntry)) { _tvGenres.TryAdd(epgGenre.Value, genreMapEntry.Split(new char[] { '{' }, StringSplitOptions.RemoveEmptyEntries)); } } genreIndex++; } }
/// <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); } }
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)); }
private async Task <bool> ExtractMovieData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData) { // VideoAspect must be present to be sure it is actually a video resource. if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID)) { return(false); } // Calling EnsureLocalFileSystemAccess not necessary; only string operation string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() }; string title = null; string sortTitle = null; bool isReimport = extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID); MovieInfo movieInfo = new MovieInfo(); if (extractedAspectData.ContainsKey(MovieAspect.ASPECT_ID)) { movieInfo.FromMetadata(extractedAspectData); } if (movieInfo.MovieName.IsEmpty) { //Try to get title if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) && !string.IsNullOrEmpty(title) && !lfsra.ResourceName.StartsWith(title, StringComparison.InvariantCultureIgnoreCase)) { //The title may still contain tags and other noise, try and parse it for a title and year. MovieNameMatcher.MatchTitleYear(title, movieInfo); } } if (movieInfo.MovieNameSort.IsEmpty) { //Try to get sort title if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, out sortTitle) && !string.IsNullOrEmpty(sortTitle)) { movieInfo.MovieNameSort = sortTitle; } } if (!isReimport) //Ignore tags or file based information for reimport because they might be the cause of the wrong import { if (movieInfo.MovieDbId == 0) { try { // Try to use an existing TMDB id for exact mapping string tmdbId = await MatroskaMatcher.TryMatchTmdbIdAsync(lfsra).ConfigureAwait(false); if (!string.IsNullOrEmpty(tmdbId)) { movieInfo.MovieDbId = Convert.ToInt32(tmdbId); } } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading TMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath); } } if (string.IsNullOrEmpty(movieInfo.ImdbId)) { try { // Try to use an existing IMDB id for exact mapping string imdbId = await MatroskaMatcher.TryMatchImdbIdAsync(lfsra).ConfigureAwait(false); if (!string.IsNullOrEmpty(imdbId)) { movieInfo.ImdbId = imdbId; } else if (pathsToTest.Any(path => ImdbIdMatcher.TryMatchImdbId(path, out imdbId))) { movieInfo.ImdbId = imdbId; } } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading IMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath); } } if (!movieInfo.IsBaseInfoPresent || !movieInfo.ReleaseDate.HasValue) { // Also test the full path year. This is useful if the path contains the real name and year. foreach (string path in pathsToTest) { if (MovieNameMatcher.MatchTitleYear(path, movieInfo)) { break; } } //Fall back to MediaAspect.ATTR_TITLE if (movieInfo.MovieName.IsEmpty && !string.IsNullOrEmpty(title)) { movieInfo.MovieName = title; } /* Clear the names from unwanted strings */ MovieNameMatcher.CleanupTitle(movieInfo); } if (!movieInfo.ReleaseDate.HasValue && !movieInfo.HasExternalId) { // When searching movie title, the year can be relevant for multiple titles with same name but different years DateTime recordingDate; if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, out recordingDate)) { movieInfo.ReleaseDate = recordingDate; } } try { await MatroskaMatcher.ExtractFromTagsAsync(lfsra, movieInfo).ConfigureAwait(false); MP4Matcher.ExtractFromTags(lfsra, movieInfo); } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath); } } // Allow the online lookup to choose best matching language for metadata if (movieInfo.Languages.Count == 0) { IList <MultipleMediaItemAspect> audioAspects; if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects)) { foreach (MultipleMediaItemAspect aspect in audioAspects) { string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE); if (!string.IsNullOrEmpty(language) && !movieInfo.Languages.Contains(language)) { movieInfo.Languages.Add(language); } } } } if (SkipOnlineSearches && !SkipFanArtDownload) { MovieInfo tempInfo = movieInfo.Clone(); if (await OnlineMatcherService.Instance.FindAndUpdateMovieAsync(tempInfo).ConfigureAwait(false)) { movieInfo.CopyIdsFrom(tempInfo); movieInfo.HasChanged = tempInfo.HasChanged; } } else if (!SkipOnlineSearches) { await OnlineMatcherService.Instance.FindAndUpdateMovieAsync(movieInfo).ConfigureAwait(false); } //Asign genre ids if (movieInfo.Genres.Count > 0) { IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in movieInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId)) { genre.Id = genreId; movieInfo.HasChanged = true; } } } //Send it to the videos section if (!SkipOnlineSearches && !movieInfo.HasExternalId) { return(false); } //Create custom collection (overrides online collection) MovieCollectionInfo collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>(); string collectionName; if (string.IsNullOrEmpty(collectionInfo.NameId) && CollectionFolderHasFanArt(lfsra, out collectionName)) { collectionInfo = new MovieCollectionInfo(); collectionInfo.CollectionName = collectionName; if (!collectionInfo.CollectionName.IsEmpty) { movieInfo.CollectionName = collectionInfo.CollectionName; movieInfo.CopyIdsFrom(collectionInfo); //Reset ID's movieInfo.HasChanged = true; } } if (movieInfo.MovieNameSort.IsEmpty) { if (!movieInfo.CollectionName.IsEmpty && movieInfo.ReleaseDate.HasValue) { movieInfo.MovieNameSort = $"{movieInfo.CollectionName.Text} {movieInfo.ReleaseDate.Value.Year}-{movieInfo.ReleaseDate.Value.Month.ToString("00")}"; } else if (!movieInfo.MovieName.IsEmpty) { movieInfo.MovieNameSort = BaseInfo.GetSortTitle(movieInfo.MovieName.Text); } else { movieInfo.MovieNameSort = BaseInfo.GetSortTitle(title); } } movieInfo.SetMetadata(extractedAspectData); return(movieInfo.IsBaseInfoPresent); }
public override async Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode) { // If the base AudioMDE already extracted metadata, don't try here again to avoid conflicts. if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID)) { return(false); } ILocalFsResourceAccessor fsra = mediaItemAccessor as ILocalFsResourceAccessor; if (fsra == null) { return(false); } if (!fsra.IsFile) { return(false); } string fileName = fsra.ResourceName; if (!HasAudioExtension(fileName)) { return(false); } if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID)) //Ignore for reimports because the tags might be the cause of the wrong match { return(false); } try { TAG_INFO tags; using (fsra.EnsureLocalFileSystemAccess()) tags = BassTags.BASS_TAG_GetFromFile(fsra.LocalFileSystemPath); if (tags == null) { return(false); } fileName = ProviderPathHelper.GetFileNameWithoutExtension(fileName) ?? string.Empty; string title; string artist; uint? trackNo; GuessMetadataFromFileName(fileName, out title, out artist, out trackNo); if (!string.IsNullOrWhiteSpace(tags.title)) { title = tags.title; } IEnumerable <string> artists; if (!string.IsNullOrWhiteSpace(tags.artist)) { artists = SplitTagEnum(tags.artist); artists = PatchID3v23Enumeration(artists); } else { artists = artist == null ? null : new string[] { artist } }; if (!string.IsNullOrWhiteSpace(tags.track) && tags.track != "0") { int iTrackNo; if (int.TryParse(tags.track, out iTrackNo)) { trackNo = (uint?)iTrackNo; } else { trackNo = null; } } TrackInfo trackInfo = new TrackInfo(); if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID)) { trackInfo.FromMetadata(extractedAspectData); } else { MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title); IList <MultipleMediaItemAspect> providerResourceAspect; if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerResourceAspect)) { providerResourceAspect[0].SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size); // Calling EnsureLocalFileSystemAccess not necessary; only string operation providerResourceAspect[0].SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "audio/" + Path.GetExtension(fsra.LocalFileSystemPath).Substring(1)); } MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_BITRATE, tags.bitrate); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(tags.comment)); MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_DURATION, (long)tags.duration); } if (!trackInfo.IsBaseInfoPresent) { trackInfo.TrackName = title; trackInfo.Album = StringUtils.TrimToNull(tags.album); if (trackNo.HasValue) { trackInfo.TrackNum = (int)trackNo.Value; } trackInfo.Artists = new List <PersonInfo>(); foreach (string artistName in ApplyAdditionalSeparator(artists)) { trackInfo.Artists.Add(new PersonInfo() { Name = artistName, Occupation = PersonAspect.OCCUPATION_ARTIST, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } IEnumerable <string> albumArtists = SplitTagEnum(tags.albumartist); albumArtists = PatchID3v23Enumeration(albumArtists); trackInfo.AlbumArtists = new List <PersonInfo>(); foreach (string artistName in ApplyAdditionalSeparator(albumArtists)) { trackInfo.AlbumArtists.Add(new PersonInfo() { Name = artistName, Occupation = PersonAspect.OCCUPATION_ARTIST, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } IEnumerable <string> composers = SplitTagEnum(tags.composer); composers = PatchID3v23Enumeration(composers); trackInfo.Composers = new List <PersonInfo>(); foreach (string composerName in ApplyAdditionalSeparator(composers)) { trackInfo.Composers.Add(new PersonInfo() { Name = composerName, Occupation = PersonAspect.OCCUPATION_COMPOSER, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } IEnumerable <string> genres = SplitTagEnum(tags.genre); genres = PatchID3v23Enumeration(genres); trackInfo.Genres = ApplyAdditionalSeparator(genres).Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo { Name = s.Trim() }).ToList(); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); foreach (var genre in trackInfo.Genres) { if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId)) { genre.Id = genreId; } } int year; if (int.TryParse(tags.year, out year)) { if (year >= 30 && year <= 99) { year += 1900; } if (year >= 1930 && year <= 2030) { trackInfo.ReleaseDate = new DateTime(year, 1, 1); } } if (!trackInfo.HasThumbnail) { // The following code gets cover art images from file (embedded) or from windows explorer cache (supports folder.jpg). if (tags.PictureCount > 0) { try { using (Image cover = tags.PictureGetImage(0)) using (MemoryStream result = new MemoryStream()) { cover.Save(result, ImageFormat.Jpeg); trackInfo.Thumbnail = result.ToArray(); trackInfo.HasChanged = true; } } // Decoding of invalid image data can fail, but main MediaItem is correct. catch { } } } } if (!SkipOnlineSearches && !forceQuickMode) { await OnlineMatcherService.Instance.FindAndUpdateTrackAsync(trackInfo).ConfigureAwait(false); } if (!trackInfo.HasChanged) { return(false); } trackInfo.SetMetadata(extractedAspectData); return(trackInfo.IsBaseInfoPresent); } catch (Exception e) { // Only log at the info level here - And simply return false. This makes the importer know that we // couldn't perform our task here ServiceRegistration.Get <ILogger>().Info("BassAudioMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message); } return(false); }
public override async Task <ICollection <FilterValue> > GetAvailableValuesAsync(IEnumerable <Guid> necessaryMIATypeIds, IFilter selectAttributeFilter, IFilter filter) { IContentDirectory cd = ServiceRegistration.Get <IServerConnectionManager>().ContentDirectory; if (cd == null) { throw new NotConnectedException("The MediaLibrary is not connected"); } bool showVirtual = VirtualMediaHelper.ShowVirtualMedia(necessaryMIATypeIds); ViewSettings settings = ServiceRegistration.Get <ISettingsManager>().Load <ViewSettings>(); IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>(); Dictionary <int, FilterValue> genredFilters = new Dictionary <int, FilterValue>(); List <FilterValue> ungenredFilters = new List <FilterValue>(); if (_necessaryMIATypeIds != null) { necessaryMIATypeIds = _necessaryMIATypeIds; } HomogenousMap valueGroups = null; HomogenousMap valueKeys = null; if (_keyAttributeType != null) { Tuple <HomogenousMap, HomogenousMap> values = await cd.GetKeyValueGroupsAsync(_keyAttributeType, _valueAttributeType, selectAttributeFilter, ProjectionFunction.None, necessaryMIATypeIds, filter, true, showVirtual); valueGroups = values.Item1; valueKeys = values.Item2; } else { valueGroups = await cd.GetValueGroupsAsync(_valueAttributeType, selectAttributeFilter, ProjectionFunction.None, necessaryMIATypeIds, filter, true, showVirtual); } IList <FilterValue> result = new List <FilterValue>(valueGroups.Count); int numEmptyEntries = 0; foreach (KeyValuePair <object, object> group in valueGroups) { string name = GetDisplayName(group.Key); if (name == string.Empty) { numEmptyEntries += (int)group.Value; } else if (!string.IsNullOrEmpty(_genreCategory) && settings.UseLocalizedGenres) { int?genreId = valueKeys[group.Key] as int?; if (!genreId.HasValue) { ungenredFilters.Add(new FilterValue(valueKeys[group.Key], name, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key), null, (int)group.Value, this)); } else if (!genredFilters.ContainsKey(genreId.Value)) { if (converter.GetGenreName(genreId.Value, _genreCategory, null, out string genreName)) { name = genreName; } genredFilters.Add(genreId.Value, new FilterValue(genreId.Value, name, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key), null, (int)group.Value, this)); } else { genredFilters[genreId.Value] = new FilterValue(genreId.Value, genredFilters[genreId.Value].Title, BooleanCombinationFilter.CombineFilters(BooleanOperator.Or, genredFilters[genreId.Value].Filter, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key)), null, genredFilters[genreId.Value].NumItems.Value + (int)group.Value, this); } } else { result.Add(new FilterValue(valueKeys[group.Key], name, new RelationalFilter(_valueAttributeType, RelationalOperator.EQ, group.Key), null, (int)group.Value, this)); } } foreach (var gf in genredFilters.Values) { result.Add(gf); } foreach (var ugf in ungenredFilters) { result.Add(ugf); } if (numEmptyEntries > 0) { result.Insert(0, new FilterValue(Consts.RES_VALUE_EMPTY_TITLE, new EmptyFilter(_valueAttributeType), null, numEmptyEntries, this)); } return(result); }