public virtual bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode) { IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor; if (fsra == null) { return(false); } if (!fsra.IsFile) { return(false); } string fileName = fsra.ResourceName; if (!HasAudioExtension(fileName)) { return(false); } bool refresh = false; if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID)) { refresh = true; } try { TrackInfo trackInfo = new TrackInfo(); if (refresh) { trackInfo.FromMetadata(extractedAspectData); } if (!trackInfo.IsBaseInfoPresent) { File tag = null; try { ByteVector.UseBrokenLatin1Behavior = true; // Otherwise we have problems retrieving non-latin1 chars tag = File.Create(new ResourceProviderFileAbstraction(fsra)); } catch (CorruptFileException) { // Only log at the info level here - And simply return false. This makes the importer know that we // couldn't perform our task here. ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Audio file '{0}' seems to be broken", fsra.CanonicalLocalResourcePath); return(false); } using (tag) { // Some file extensions like .mp4 can contain audio and video. Do not handle files with video content here. if (tag.Properties.VideoHeight > 0 && tag.Properties.VideoWidth > 0) { return(false); } fileName = ProviderPathHelper.GetFileNameWithoutExtension(fileName) ?? string.Empty; string title; string sortTitle; string artist; uint? trackNo; GuessMetadataFromFileName(fileName, out title, out artist, out trackNo); if (!string.IsNullOrEmpty(title)) { title = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(title.ToLowerInvariant()); } if (!string.IsNullOrEmpty(artist)) { artist = CultureInfo.InvariantCulture.TextInfo.ToTitleCase(artist.ToLowerInvariant()); } if (!string.IsNullOrEmpty(tag.Tag.Title)) { title = tag.Tag.Title.Trim(); } sortTitle = BaseInfo.GetSortTitle(title); if (!string.IsNullOrEmpty(tag.Tag.TitleSort)) { sortTitle = tag.Tag.TitleSort.Trim(); } IEnumerable <string> artists; if (tag.Tag.Performers.Length > 0) { artists = tag.Tag.Performers; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { artists = PatchID3v23Enumeration(artists); } } else { artists = artist == null ? null : new string[] { artist.Trim() } }; if (tag.Tag.Track != 0) { trackNo = tag.Tag.Track; } if (importOnly) { MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size); providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, fsra.CanonicalLocalResourcePath.Serialize()); // FIXME Albert: tag.MimeType returns taglib/mp3 for an MP3 file. This is not what we want and collides with the // mimetype handling in the BASS player, which expects audio/xxx. if (!string.IsNullOrWhiteSpace(tag.MimeType)) { providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, tag.MimeType.Replace("taglib/", "audio/")); } MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, title); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, sortTitle); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_COMMENT, StringUtils.TrimToNull(tag.Tag.Comment)); MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, fsra.LastChanged); } trackInfo.TrackName = title; trackInfo.TrackNameSort = sortTitle; if (tag.Properties.Codecs.Count() > 0) { trackInfo.Encoding = tag.Properties.Codecs.First().Description; } if (tag.Properties.Duration.TotalSeconds != 0) { trackInfo.Duration = (long)tag.Properties.Duration.TotalSeconds; } if (tag.Properties.AudioBitrate != 0) { trackInfo.BitRate = (int)tag.Properties.AudioBitrate; } if (tag.Properties.AudioChannels != 0) { trackInfo.Channels = (int)tag.Properties.AudioChannels; } if (tag.Properties.AudioSampleRate != 0) { trackInfo.SampleRate = (int)tag.Properties.AudioSampleRate; } TagLib.Id3v2.Tag id3Tag = (TagLib.Id3v2.Tag)tag.GetTag(TagTypes.Id3v2, false); if (id3Tag != null && !id3Tag.IsEmpty) { trackInfo.Compilation = id3Tag.IsCompilation; } trackInfo.Album = !string.IsNullOrEmpty(tag.Tag.Album) ? tag.Tag.Album.Trim() : null; if (!string.IsNullOrEmpty(tag.Tag.AlbumSort)) { IAudioRelationshipExtractor.StoreAlbum(extractedAspectData, tag.Tag.Album, tag.Tag.AlbumSort.Trim()); } if (trackNo.HasValue) { trackInfo.TrackNum = (int)trackNo.Value; } if (tag.Tag.TrackCount != 0) { trackInfo.TotalTracks = (int)tag.Tag.TrackCount; } if (tag.Tag.Disc != 0) { trackInfo.DiscNum = (int)tag.Tag.Disc; } if (tag.Tag.DiscCount != 0) { trackInfo.TotalDiscs = (int)tag.Tag.DiscCount; } if (!string.IsNullOrEmpty(tag.Tag.Lyrics)) { trackInfo.TrackLyrics = tag.Tag.Lyrics; } if (tag.Tag.TrackCount != 0) { trackInfo.TotalTracks = (int)tag.Tag.TrackCount; } if (!string.IsNullOrEmpty(tag.Tag.MusicBrainzTrackId)) { trackInfo.MusicBrainzId = tag.Tag.MusicBrainzTrackId; } if (!string.IsNullOrEmpty(tag.Tag.MusicBrainzReleaseId)) { trackInfo.AlbumMusicBrainzId = tag.Tag.MusicBrainzReleaseId; } if (!string.IsNullOrEmpty(tag.Tag.MusicBrainzDiscId)) { trackInfo.AlbumMusicBrainzDiscId = tag.Tag.MusicBrainzDiscId; } if (!string.IsNullOrEmpty(tag.Tag.AmazonId)) { trackInfo.AlbumAmazonId = tag.Tag.AmazonId; } if (!string.IsNullOrEmpty(tag.Tag.MusicIpId)) { trackInfo.MusicIpId = tag.Tag.MusicIpId; } trackInfo.Artists = new List <PersonInfo>(); if (artists != null) { foreach (string artistName in ApplyAdditionalSeparator(artists)) { trackInfo.Artists.Add(new PersonInfo() { Name = artistName.Trim(), Occupation = PersonAspect.OCCUPATION_ARTIST, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } } //Save id if possible if (trackInfo.Artists.Count == 1 && !string.IsNullOrEmpty(tag.Tag.MusicBrainzArtistId)) { trackInfo.Artists[0].MusicBrainzId = tag.Tag.MusicBrainzArtistId; } IEnumerable <string> albumArtists = tag.Tag.AlbumArtists; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { albumArtists = PatchID3v23Enumeration(albumArtists); } trackInfo.AlbumArtists = new List <PersonInfo>(); if (albumArtists != null) { foreach (string artistName in ApplyAdditionalSeparator(albumArtists)) { trackInfo.AlbumArtists.Add(new PersonInfo() { Name = artistName.Trim(), Occupation = PersonAspect.OCCUPATION_ARTIST, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } } //Save id if possible if (trackInfo.AlbumArtists.Count == 1 && !string.IsNullOrEmpty(tag.Tag.MusicBrainzReleaseArtistId)) { trackInfo.AlbumArtists[0].MusicBrainzId = tag.Tag.MusicBrainzReleaseArtistId; } IEnumerable <string> composers = tag.Tag.Composers; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { composers = PatchID3v23Enumeration(composers); } trackInfo.Composers = new List <PersonInfo>(); if (composers != null) { foreach (string composerName in ApplyAdditionalSeparator(composers)) { trackInfo.Composers.Add(new PersonInfo() { Name = composerName.Trim(), Occupation = PersonAspect.OCCUPATION_COMPOSER, ParentMediaName = trackInfo.Album, MediaName = trackInfo.TrackName }); } } if (tag.Tag.Genres.Length > 0) { IEnumerable <string> genres = tag.Tag.Genres; if ((tag.TagTypes & TagTypes.Id3v2) != 0) { genres = PatchID3v23Enumeration(genres); } trackInfo.Genres = ApplyAdditionalSeparator(genres).Select(s => new GenreInfo { Name = s.Trim() }).ToList(); OnlineMatcherService.Instance.AssignMissingMusicGenreIds(trackInfo.Genres); } int year = (int)tag.Tag.Year; if (year >= 30 && year <= 99) { year += 1900; } if (year >= 1930 && year <= 2030) { trackInfo.ReleaseDate = new DateTime(year, 1, 1); } if (!trackInfo.HasThumbnail) { // The following code gets cover art images from file (embedded) or from windows explorer cache (supports folder.jpg). IPicture[] pics = tag.Tag.Pictures; if (pics.Length > 0) { try { using (MemoryStream stream = new MemoryStream(pics[0].Data.Data)) { trackInfo.Thumbnail = stream.ToArray(); trackInfo.HasChanged = true; } } // Decoding of invalid image data can fail, but main MediaItem is correct. catch { } } else { // In quick mode only allow thumbs taken from cache. bool cachedOnly = importOnly || forceQuickMode; // Thumbnail extraction fileName = mediaItemAccessor.ResourcePathName; IThumbnailGenerator generator = ServiceRegistration.Get <IThumbnailGenerator>(); byte[] thumbData; ImageType imageType; if (generator.GetThumbnail(fileName, cachedOnly, out thumbData, out imageType)) { trackInfo.Thumbnail = thumbData; trackInfo.HasChanged = true; } } } } if (string.IsNullOrEmpty(trackInfo.Album) || trackInfo.Artists.Count == 0) { MusicNameMatcher.MatchTrack(fileName, trackInfo); } } //Determine compilation if (importOnly && !trackInfo.Compilation) { if (trackInfo.AlbumArtists.Count > 0 && (trackInfo.AlbumArtists[0].Name.IndexOf("Various", StringComparison.InvariantCultureIgnoreCase) >= 0 || trackInfo.AlbumArtists[0].Name.Equals("VA", StringComparison.InvariantCultureIgnoreCase))) { trackInfo.Compilation = true; } else { //Look for itunes compilation folder var mediaItemPath = mediaItemAccessor.CanonicalLocalResourcePath; var albumMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../"); var artistMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../"); if (IsDiscFolder(trackInfo.Album, albumMediaItemDirectoryPath.FileName)) { //Probably a CD folder so try next parent artistMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../../"); } if (artistMediaItemDirectoryPath.FileName.IndexOf("Compilation", StringComparison.InvariantCultureIgnoreCase) >= 0) { trackInfo.Compilation = true; } } } if (!refresh) { //Check artists trackInfo.Artists = GetCorrectedArtistsList(trackInfo, trackInfo.Artists); trackInfo.AlbumArtists = GetCorrectedArtistsList(trackInfo, trackInfo.AlbumArtists); } trackInfo.AssignNameId(); if (!forceQuickMode) { AudioCDMatcher.GetDiscMatchAndUpdate(mediaItemAccessor.ResourcePathName, trackInfo); if (SkipOnlineSearches && !SkipFanArtDownload) { TrackInfo tempInfo = trackInfo.Clone(); OnlineMatcherService.Instance.FindAndUpdateTrack(tempInfo, importOnly); trackInfo.CopyIdsFrom(tempInfo); trackInfo.HasChanged = tempInfo.HasChanged; } else if (!SkipOnlineSearches) { OnlineMatcherService.Instance.FindAndUpdateTrack(trackInfo, importOnly); } } if (refresh) { if ((IncludeArtistDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ARTIST) && trackInfo.Artists.Count > 0) || (IncludeArtistDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ALBUMARTIST) && trackInfo.AlbumArtists.Count > 0) || (IncludeComposerDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_COMPOSER) && trackInfo.Composers.Count > 0)) { trackInfo.HasChanged = true; } } if (!trackInfo.HasChanged && !importOnly) { return(false); } trackInfo.SetMetadata(extractedAspectData); if (importOnly) { //Store metadata for the Relationship Extractors if (IncludeArtistDetails) { IAudioRelationshipExtractor.StorePersons(extractedAspectData, trackInfo.Artists, false); IAudioRelationshipExtractor.StorePersons(extractedAspectData, trackInfo.AlbumArtists, true); } if (IncludeComposerDetails) { IAudioRelationshipExtractor.StorePersons(extractedAspectData, trackInfo.Composers, false); } } return(trackInfo.IsBaseInfoPresent); } catch (UnsupportedFormatException) { ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Unsupported audio file '{0}'", fsra.CanonicalLocalResourcePath); return(false); } catch (Exception e) { // Only log at the info level here - And simply return false. This makes the importer know that we // couldn't perform our task here ServiceRegistration.Get <ILogger>().Info("AudioMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message); } return(false); }
public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects) { extractedLinkedAspects = null; MovieInfo movieInfo = new MovieInfo(); if (!movieInfo.FromMetadata(aspects)) { return(false); } MovieCollectionInfo cachedCollection; Guid collectionId; MovieCollectionInfo collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>(); if (TryGetInfoFromCache(collectionInfo, out cachedCollection, out collectionId)) { collectionInfo = cachedCollection; } else if (!MovieMetadataExtractor.SkipOnlineSearches && collectionInfo.HasExternalId) { OnlineMatcherService.Instance.UpdateCollection(collectionInfo, false, false); } bool hasId = false; if (!MovieMetadataExtractor.SkipOnlineSearches) { hasId = collectionInfo.HasExternalId || !string.IsNullOrEmpty(movieInfo.CollectionNameId); } else { hasId = !string.IsNullOrEmpty(movieInfo.CollectionNameId); } if (!BaseInfo.HasRelationship(aspects, LinkedRole) && hasId) { collectionInfo.HasChanged = true; //Force save if no relationship exists } if (!collectionInfo.HasChanged && !importOnly) { return(false); } extractedLinkedAspects = new List <RelationshipItem>(); IDictionary <Guid, IList <MediaItemAspect> > collectionAspects = new Dictionary <Guid, IList <MediaItemAspect> >(); if (collectionId != Guid.Empty) { collectionInfo.SetMetadata(collectionAspects); bool movieVirtual = true; if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out movieVirtual)) { MediaItemAspect.SetAttribute(collectionAspects, MediaAspect.ATTR_ISVIRTUAL, movieVirtual); } if (collectionAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID)) { extractedLinkedAspects.Add(new RelationshipItem(collectionAspects, collectionId)); } } else { //Create custom collection if (!string.IsNullOrEmpty(movieInfo.CollectionNameId) && !collectionInfo.HasExternalId) { collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>(); collectionInfo.HasChanged = true; } collectionInfo.SetMetadata(collectionAspects); bool movieVirtual = true; if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out movieVirtual)) { MediaItemAspect.SetAttribute(collectionAspects, MediaAspect.ATTR_ISVIRTUAL, movieVirtual); } if (collectionAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID)) { extractedLinkedAspects.Add(new RelationshipItem(collectionAspects, Guid.Empty)); } } return(extractedLinkedAspects.Count > 0); }
public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects) { extractedLinkedAspects = null; EpisodeInfo episodeInfo = new EpisodeInfo(); if (!episodeInfo.FromMetadata(aspects)) { return(false); } if (CheckCacheContains(episodeInfo)) { return(false); } SeriesInfo cachedSeries; Guid seriesId; SeriesInfo seriesInfo = episodeInfo.CloneBasicInstance <SeriesInfo>(); if (TryGetInfoFromCache(seriesInfo, out cachedSeries, out seriesId)) { seriesInfo = cachedSeries; } else if (!SeriesMetadataExtractor.SkipOnlineSearches) { OnlineMatcherService.Instance.UpdateSeries(seriesInfo, false, importOnly); } if (!BaseInfo.HasRelationship(aspects, LinkedRole)) { seriesInfo.HasChanged = true; //Force save if no relationship exists } if (!seriesInfo.HasChanged && !importOnly) { return(false); } AddToCheckCache(episodeInfo); extractedLinkedAspects = new List <RelationshipItem>(); IDictionary <Guid, IList <MediaItemAspect> > seriesAspects = new Dictionary <Guid, IList <MediaItemAspect> >(); seriesInfo.SetMetadata(seriesAspects); if (aspects.ContainsKey(EpisodeAspect.ASPECT_ID)) { bool episodeVirtual = true; if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out episodeVirtual)) { MediaItemAspect.SetAttribute(seriesAspects, MediaAspect.ATTR_ISVIRTUAL, episodeVirtual); } } if (!seriesAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID)) { return(false); } if (seriesId != Guid.Empty) { extractedLinkedAspects.Add(new RelationshipItem(seriesAspects, seriesId)); } else { extractedLinkedAspects.Add(new RelationshipItem(seriesAspects, Guid.Empty)); } return(true); }
protected bool ExtractSeriesData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly) { // VideoAspect must be present to be sure it is actually a video resource. if (!extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID)) { return(false); } if (extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID) && !importOnly) { return(false); //Subtitles can only be imported not refreshed } bool refresh = false; if (extractedAspectData.ContainsKey(EpisodeAspect.ASPECT_ID)) { refresh = true; } EpisodeInfo episodeInfo = new EpisodeInfo(); if (refresh) { episodeInfo.FromMetadata(extractedAspectData); } ISeriesRelationshipExtractor.UpdateEpisodeSeries(extractedAspectData, episodeInfo); if (!episodeInfo.IsBaseInfoPresent) { string title = null; int seasonNumber; SingleMediaItemAspect episodeAspect; MediaItemAspect.TryGetAspect(extractedAspectData, EpisodeAspect.Metadata, out episodeAspect); IEnumerable <int> episodeNumbers; if (MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_SERIES_NAME, out title) && MediaItemAspect.TryGetAttribute(extractedAspectData, EpisodeAspect.ATTR_SEASON, out seasonNumber) && (episodeNumbers = episodeAspect.GetCollectionAttribute <int>(EpisodeAspect.ATTR_EPISODE)) != null) { episodeInfo.SeriesName = title; episodeInfo.SeasonNumber = seasonNumber; episodeInfo.EpisodeNumbers.Clear(); episodeNumbers.ToList().ForEach(n => episodeInfo.EpisodeNumbers.Add(n)); } } // If there was no complete match, yet, try to get extended information out of matroska files) if (!episodeInfo.IsBaseInfoPresent || !episodeInfo.HasExternalId) { try { MatroskaMatcher matroskaMatcher = new MatroskaMatcher(); if (matroskaMatcher.MatchSeries(lfsra, episodeInfo)) { ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Found EpisodeInfo by MatroskaMatcher for {0}, IMDB {1}, TVDB {2}, TMDB {3}, AreReqiredFieldsFilled {4}", episodeInfo.SeriesName, episodeInfo.SeriesImdbId, episodeInfo.SeriesTvdbId, episodeInfo.SeriesMovieDbId, episodeInfo.IsBaseInfoPresent); } } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Debug("ExtractSeriesData: Exception reading matroska tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath); } } // If no information was found before, try name matching if (!episodeInfo.IsBaseInfoPresent) { // Try to match series from folder and file naming SeriesMatcher seriesMatcher = new SeriesMatcher(); seriesMatcher.MatchSeries(lfsra, episodeInfo); } //Prepare online search improvements if (episodeInfo.SeriesFirstAired == null) { EpisodeInfo tempEpisodeInfo = new EpisodeInfo(); SeriesMatcher seriesMatcher = new SeriesMatcher(); seriesMatcher.MatchSeries(lfsra, tempEpisodeInfo); if (tempEpisodeInfo.SeriesFirstAired.HasValue) { episodeInfo.SeriesFirstAired = tempEpisodeInfo.SeriesFirstAired; } } if (string.IsNullOrEmpty(episodeInfo.SeriesAlternateName)) { var mediaItemPath = lfsra.CanonicalLocalResourcePath; var seriesMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../"); episodeInfo.SeriesAlternateName = seriesMediaItemDirectoryPath.FileName; } if (episodeInfo.Languages.Count == 0) { IList <MultipleMediaItemAspect> audioAspects; if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects)) { foreach (MultipleMediaItemAspect aspect in audioAspects) { string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE); if (!string.IsNullOrEmpty(language) && !episodeInfo.Languages.Contains(language)) { episodeInfo.Languages.Add(language); } } } } episodeInfo.AssignNameId(); if (SkipOnlineSearches && !SkipFanArtDownload) { EpisodeInfo tempInfo = episodeInfo.Clone(); OnlineMatcherService.Instance.FindAndUpdateEpisode(tempInfo, importOnly); episodeInfo.CopyIdsFrom(tempInfo); episodeInfo.HasChanged = tempInfo.HasChanged; } else if (!SkipOnlineSearches) { OnlineMatcherService.Instance.FindAndUpdateEpisode(episodeInfo, importOnly); } //Send it to the videos section if (!SkipOnlineSearches && !episodeInfo.HasExternalId) { return(false); } if (refresh) { if ((IncludeActorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ACTOR) && episodeInfo.Actors.Count > 0) || (IncludeCharacterDetails && !BaseInfo.HasRelationship(extractedAspectData, CharacterAspect.ROLE_CHARACTER) && episodeInfo.Characters.Count > 0) || (IncludeDirectorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_DIRECTOR) && episodeInfo.Directors.Count > 0) || (IncludeWriterDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_WRITER) && episodeInfo.Writers.Count > 0) || (!BaseInfo.HasRelationship(extractedAspectData, SeriesAspect.ROLE_SERIES) && !episodeInfo.SeriesName.IsEmpty) || (!BaseInfo.HasRelationship(extractedAspectData, SeasonAspect.ROLE_SEASON) && episodeInfo.SeasonNumber.HasValue)) { episodeInfo.HasChanged = true; } } if (!episodeInfo.HasChanged && !importOnly) { return(false); } episodeInfo.SetMetadata(extractedAspectData); return(episodeInfo.IsBaseInfoPresent); }
private bool ExtractMovieData(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly) { // Calling EnsureLocalFileSystemAccess not necessary; only string operation string[] pathsToTest = new[] { lfsra.LocalFileSystemPath, lfsra.CanonicalLocalResourcePath.ToString() }; string title = null; string sortTitle = null; // VideoAspect must be present to be sure it is actually a video resource. if (!extractedAspectData.ContainsKey(VideoStreamAspect.ASPECT_ID) && !extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID)) { return(false); } if (extractedAspectData.ContainsKey(SubtitleAspect.ASPECT_ID) && !importOnly) { return(false); //Subtitles can only be imported not refreshed } bool refresh = false; if (extractedAspectData.ContainsKey(MovieAspect.ASPECT_ID)) { refresh = true; } MovieInfo movieInfo = new MovieInfo(); if (refresh) { movieInfo.FromMetadata(extractedAspectData); } if (movieInfo.MovieName.IsEmpty) { //Try to get title if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, out title) && !string.IsNullOrEmpty(title) && !lfsra.ResourceName.StartsWith(title, StringComparison.InvariantCultureIgnoreCase)) { movieInfo.MovieName = title; /* Clear the names from unwanted strings */ MovieNameMatcher.CleanupTitle(movieInfo); } } if (movieInfo.MovieNameSort.IsEmpty) { //Try to get sort title if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, out sortTitle) && !string.IsNullOrEmpty(sortTitle)) { movieInfo.MovieNameSort = sortTitle; } } if (movieInfo.MovieDbId == 0) { try { // Try to use an existing TMDB id for exact mapping string tmdbId; if (MatroskaMatcher.TryMatchTmdbId(lfsra, out tmdbId)) { movieInfo.MovieDbId = Convert.ToInt32(tmdbId); } } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading TMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath); } } if (string.IsNullOrEmpty(movieInfo.ImdbId)) { try { // Try to use an existing IMDB id for exact mapping string imdbId = null; if (pathsToTest.Any(path => MatroskaMatcher.TryMatchImdbId(lfsra, out imdbId))) { movieInfo.ImdbId = imdbId; } else if (pathsToTest.Any(path => ImdbIdMatcher.TryMatchImdbId(path, out imdbId))) { movieInfo.ImdbId = imdbId; } } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading IMDB ID for '{0}'", ex, lfsra.CanonicalLocalResourcePath); } } if (!movieInfo.IsBaseInfoPresent) { // Also test the full path year. This is useful if the path contains the real name and year. foreach (string path in pathsToTest) { if (MovieNameMatcher.MatchTitleYear(path, movieInfo)) { break; } } //Fall back to MediaAspect.ATTR_TITLE if (movieInfo.MovieName.IsEmpty && !string.IsNullOrEmpty(title)) { movieInfo.MovieName = title; } /* Clear the names from unwanted strings */ MovieNameMatcher.CleanupTitle(movieInfo); } if (!movieInfo.ReleaseDate.HasValue && !movieInfo.HasExternalId) { // When searching movie title, the year can be relevant for multiple titles with same name but different years DateTime recordingDate; if (MediaItemAspect.TryGetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, out recordingDate)) { movieInfo.ReleaseDate = recordingDate; } } // Allow the online lookup to choose best matching language for metadata if (movieInfo.Languages.Count == 0) { IList <MultipleMediaItemAspect> audioAspects; if (MediaItemAspect.TryGetAspects(extractedAspectData, VideoAudioStreamAspect.Metadata, out audioAspects)) { foreach (MultipleMediaItemAspect aspect in audioAspects) { string language = (string)aspect.GetAttributeValue(VideoAudioStreamAspect.ATTR_AUDIOLANGUAGE); if (!string.IsNullOrEmpty(language) && !movieInfo.Languages.Contains(language)) { movieInfo.Languages.Add(language); } } } } if (importOnly) { try { MatroskaMatcher.ExtractFromTags(lfsra, movieInfo); MP4Matcher.ExtractFromTags(lfsra, movieInfo); } catch (Exception ex) { ServiceRegistration.Get <ILogger>().Debug("MoviesMetadataExtractor: Exception reading tags for '{0}'", ex, lfsra.CanonicalLocalResourcePath); } } if (SkipOnlineSearches && !SkipFanArtDownload) { MovieInfo tempInfo = movieInfo.Clone(); OnlineMatcherService.Instance.FindAndUpdateMovie(tempInfo, importOnly); movieInfo.CopyIdsFrom(tempInfo); movieInfo.HasChanged = tempInfo.HasChanged; } else if (!SkipOnlineSearches) { OnlineMatcherService.Instance.FindAndUpdateMovie(movieInfo, importOnly); } //Send it to the videos section if (!SkipOnlineSearches && !movieInfo.HasExternalId) { return(false); } if (importOnly) { //Create custom collection (overrides online collection) MovieCollectionInfo collectionInfo = movieInfo.CloneBasicInstance <MovieCollectionInfo>(); string collectionName; if (string.IsNullOrEmpty(collectionInfo.NameId) && CollectionFolderHasFanArt(lfsra, out collectionName)) { collectionInfo = new MovieCollectionInfo(); collectionInfo.CollectionName = collectionName; if (!collectionInfo.CollectionName.IsEmpty) { movieInfo.CollectionName = collectionInfo.CollectionName; movieInfo.CopyIdsFrom(collectionInfo); //Reset ID's movieInfo.HasChanged = true; } } } movieInfo.AssignNameId(); if (refresh) { if ((IncludeActorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_ACTOR) && movieInfo.Actors.Count > 0) || (IncludeCharacterDetails && !BaseInfo.HasRelationship(extractedAspectData, CharacterAspect.ROLE_CHARACTER) && movieInfo.Characters.Count > 0) || (IncludeDirectorDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_DIRECTOR) && movieInfo.Directors.Count > 0) || (IncludeWriterDetails && !BaseInfo.HasRelationship(extractedAspectData, PersonAspect.ROLE_WRITER) && movieInfo.Writers.Count > 0) || (IncludeProductionCompanyDetails && !BaseInfo.HasRelationship(extractedAspectData, CompanyAspect.ROLE_COMPANY) && movieInfo.ProductionCompanies.Count > 0)) { movieInfo.HasChanged = true; } } if (!movieInfo.HasChanged && !importOnly) { return(false); } movieInfo.SetMetadata(extractedAspectData); return(movieInfo.IsBaseInfoPresent); }
public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects) { extractedLinkedAspects = null; TrackInfo trackInfo = new TrackInfo(); if (!trackInfo.FromMetadata(aspects)) { return(false); } if (CheckCacheContains(trackInfo)) { return(false); } IList <Guid> linkedIds; Guid albumId = BaseInfo.TryGetLinkedIds(aspects, LinkedRole, out linkedIds) ? linkedIds[0] : Guid.Empty; AlbumInfo cachedAlbum; Guid cachedId; AlbumInfo albumInfo = trackInfo.CloneBasicInstance <AlbumInfo>(); UpdateAlbum(aspects, albumInfo); UpdatePersons(aspects, albumInfo.Artists, true); if (TryGetInfoFromCache(albumInfo, out cachedAlbum, out cachedId)) { albumInfo = cachedAlbum; if (albumId == Guid.Empty) { albumId = cachedId; } } else if (!AudioMetadataExtractor.SkipOnlineSearches) { OnlineMatcherService.Instance.UpdateAlbum(albumInfo, false, importOnly); } if (!BaseInfo.HasRelationship(aspects, LinkedRole)) { albumInfo.HasChanged = true; //Force save if no relationship exists } if (!albumInfo.HasChanged && !importOnly) { return(false); } AddToCheckCache(trackInfo); extractedLinkedAspects = new List <RelationshipItem>(); IDictionary <Guid, IList <MediaItemAspect> > albumAspects = new Dictionary <Guid, IList <MediaItemAspect> >(); albumInfo.SetMetadata(albumAspects); bool trackVirtual = true; if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out trackVirtual)) { MediaItemAspect.SetAttribute(albumAspects, MediaAspect.ATTR_ISVIRTUAL, trackVirtual); } byte[] data; if (MediaItemAspect.TryGetAttribute(aspects, ThumbnailLargeAspect.ATTR_THUMBNAIL, out data)) { //Use image from track as image MediaItemAspect.SetAttribute(albumAspects, ThumbnailLargeAspect.ATTR_THUMBNAIL, data); } if (importOnly) { StorePersons(albumAspects, albumInfo.Artists, true); } if (!albumAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID)) { return(false); } if (albumId != Guid.Empty) { extractedLinkedAspects.Add(new RelationshipItem(albumAspects, albumId, albumInfo.HasChanged)); } else { extractedLinkedAspects.Add(new RelationshipItem(albumAspects, Guid.Empty)); } return(true); }