public void TestNfoMovieReaderReadMetadata() { //Arrange var resourceProviders = new Dictionary <Guid, IResourceProvider>(); resourceProviders.Add(LocalFsResourceProviderBase.LOCAL_FS_RESOURCE_PROVIDER_ID, new LocalFsResourceProvider()); var mockMediaAccessor = new Mock <IMediaAccessor>(); ServiceRegistration.Set <IMediaAccessor>(mockMediaAccessor.Object); mockMediaAccessor.Setup(x => x.LocalResourceProviders).Returns(resourceProviders); Mock <IFileSystemResourceAccessor> mockRA = new Mock <IFileSystemResourceAccessor>(); mockRA.Setup(r => r.OpenReadAsync()).Returns(Task.FromResult( Assembly.GetExecutingAssembly().GetManifestResourceStream("Tests.Server.NfoMetadataExtractor.TestData.MovieNfo.movie.nfo"))); mockRA.SetupGet(t => t.CanonicalLocalResourcePath).Returns(ResourcePath.BuildBaseProviderPath(LocalFsResourceProviderBase.LOCAL_FS_RESOURCE_PROVIDER_ID, @"TestData\MovieNfo\movie.nfo")); NfoMovieReader reader = new NfoMovieReader(new ConsoleLogger(LogLevel.All, true), 1, false, false, false, null, new NfoMovieMetadataExtractorSettings()); //Act reader.TryReadMetadataAsync(mockRA.Object).Wait(); var stubs = reader.GetMovieStubs(); //Assert Assert.NotNull(stubs); Assert.AreEqual(1, stubs.Count); var stub = stubs[0]; Assert.NotNull(stub); Assert.AreEqual("The Lego Batman Movie", stub.Title); Assert.AreEqual("The Lego Batman Movie", stub.OriginalTitle); Assert.AreEqual(7.2m, stub.Rating); Assert.AreEqual(new DateTime(2017, 1, 1), stub.Year); Assert.AreEqual(1853, stub.Votes); Assert.AreEqual("In the irreverent spirit of fun that made “The Lego Movie” a worldwide phenomenon, the self-described leading man of that ensemble—Lego Batman—stars in his own big-screen adventure. But there are big changes...", stub.Outline); Assert.AreEqual("In the irreverent spirit of fun that made “The Lego Movie” a worldwide phenomenon, the self-described leading man of that ensemble—Lego Batman—stars in his own big-screen adventure. But there are big changes brewing in Gotham, and if he wants to save the city from The Joker’s hostile takeover, Batman may have to drop the lone vigilante thing, try to work with others and maybe, just maybe, learn to lighten up.", stub.Plot); Assert.AreEqual("Always be yourself. Unless you can be Batman.", stub.Tagline); Assert.AreEqual(TimeSpan.FromMinutes(104), stub.Runtime); CollectionAssert.AreEqual(new[] { "UK:U" }, stub.Mpaa); CollectionAssert.AreEqual(new[] { "UK:U" }, stub.Certification); Assert.AreEqual("tt4116284", stub.Id); Assert.AreEqual(324849, stub.TmdbId); CollectionAssert.AreEqual(new[] { "Denmark", "United States" }, stub.Countries); Assert.AreEqual(false, stub.Watched); Assert.AreEqual(0, stub.PlayCount); CollectionAssert.AreEqual(new[] { "Action", "Animation", "Comedy", "Family", "Fantasy" }, stub.Genres); CollectionAssert.AreEqual(new[] { "Lin Pictures", "Warner Bros. Animation", "Warner Bros.", "Animal Logic", "DC Entertainment", "Lord Miller", "LEGO System A", "S" }, stub.Studios); CollectionAssert.AreEqual(new[] { "Chris McKenna", "Erik Sommers", "Seth Grahame-Smith", "Jared Stern", "John Whittington" }, stub.Credits); Assert.AreEqual("Chris McKay", stub.Director); CollectionAssert.AreEqual(new[] { "English" }, stub.Languages); }
public void TestNfoMovieReaderWriteActorMetadata() { //Arrange IList <PersonStub> actors = CreateTestActors(); MovieStub movieStub = CreateTestMovieStub(actors); NfoMovieReader reader = new NfoMovieReader(new ConsoleLogger(LogLevel.All, true), 1, true, false, false, null, new NfoMovieMetadataExtractorSettings()); reader.GetMovieStubs().Add(movieStub); //Act IList <IDictionary <Guid, IList <MediaItemAspect> > > aspects = new List <IDictionary <Guid, IList <MediaItemAspect> > >(); reader.TryWriteActorMetadata(aspects); //Assert Assert.AreEqual(2, aspects.Count); MediaItemAspect personAspect0 = MediaItemAspect.GetAspect(aspects[0], PersonAspect.Metadata); Assert.NotNull(personAspect0); Assert.AreEqual(actors[0].Name, personAspect0.GetAttributeValue <string>(PersonAspect.ATTR_PERSON_NAME)); Assert.AreEqual(actors[0].Biography, personAspect0.GetAttributeValue <string>(PersonAspect.ATTR_BIOGRAPHY)); Assert.AreEqual(actors[0].Birthdate, personAspect0.GetAttributeValue <DateTime?>(PersonAspect.ATTR_DATEOFBIRTH)); Assert.AreEqual(actors[0].Deathdate, personAspect0.GetAttributeValue <DateTime?>(PersonAspect.ATTR_DATEOFDEATH)); Assert.AreEqual(PersonAspect.OCCUPATION_ACTOR, personAspect0.GetAttributeValue <string>(PersonAspect.ATTR_OCCUPATION)); IList <MediaItemAspect> externalIdentifiers0; Assert.IsTrue(aspects[0].TryGetValue(ExternalIdentifierAspect.ASPECT_ID, out externalIdentifiers0)); Assert.IsTrue(TestUtils.HasExternalId(externalIdentifiers0, ExternalIdentifierAspect.SOURCE_IMDB, ExternalIdentifierAspect.TYPE_PERSON, actors[0].ImdbId)); MediaItemAspect personAspect1 = MediaItemAspect.GetAspect(aspects[1], PersonAspect.Metadata); Assert.NotNull(personAspect1); Assert.AreEqual(actors[1].Name, personAspect1.GetAttributeValue <string>(PersonAspect.ATTR_PERSON_NAME)); Assert.AreEqual(actors[1].Biography, personAspect1.GetAttributeValue <string>(PersonAspect.ATTR_BIOGRAPHY)); Assert.AreEqual(actors[1].Birthdate, personAspect1.GetAttributeValue <DateTime?>(PersonAspect.ATTR_DATEOFBIRTH)); Assert.AreEqual(actors[1].Deathdate, personAspect1.GetAttributeValue <DateTime?>(PersonAspect.ATTR_DATEOFDEATH)); Assert.AreEqual(PersonAspect.OCCUPATION_ACTOR, personAspect1.GetAttributeValue <string>(PersonAspect.ATTR_OCCUPATION)); IList <MediaItemAspect> externalIdentifiers1; Assert.IsTrue(aspects[1].TryGetValue(ExternalIdentifierAspect.ASPECT_ID, out externalIdentifiers1)); Assert.IsTrue(TestUtils.HasExternalId(externalIdentifiers1, ExternalIdentifierAspect.SOURCE_IMDB, ExternalIdentifierAspect.TYPE_PERSON, actors[1].ImdbId)); }
public override async Task CollectFanArtAsync(Guid mediaItemId, IDictionary <Guid, IList <MediaItemAspect> > aspects) { IResourceLocator mediaItemLocator = null; if (!BaseInfo.IsVirtualResource(aspects)) { mediaItemLocator = GetResourceLocator(aspects); } if (!aspects.ContainsKey(MovieAspect.ASPECT_ID) || mediaItemLocator == null) { return; } IFanArtCache fanArtCache = ServiceRegistration.Get <IFanArtCache>(); using (IResourceAccessor mediaItemAccessor = mediaItemLocator.CreateAccessor()) { IList <Tuple <Guid, string> > actors = GetActors(aspects); //Check if loading nfo is needed if ((actors?.All(a => IsInCache(a.Item1)) ?? true) && IsInCache(mediaItemId)) { return; //Everything was already saved } NfoMovieReader movieNfoReader = await MOVIE_EXTRACTOR.TryGetNfoMovieReaderAsync(mediaItemAccessor, true).ConfigureAwait(false); if (movieNfoReader != null) { //Movie fanart var stubs = movieNfoReader.GetMovieStubs(); var mainStub = stubs?.FirstOrDefault(); if (AddToCache(mediaItemId)) { if (mainStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(mediaItemId, mainStub.Title, FanArtTypes.Poster, p => TrySaveFileImage(mainStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } await TrySaveFanArt(fanArtCache, FanArtTypes.FanArt, "FanArt", mainStub?.FanArt, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.DiscArt, "DiscArt", mainStub?.DiscArt, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.Logo, "Logo", mainStub?.Logos, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.ClearArt, "ClearArt", mainStub?.ClearArt, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.Banner, "Banner", mainStub?.Banners, mediaItemId, mainStub?.Title).ConfigureAwait(false); await TrySaveFanArt(fanArtCache, FanArtTypes.Thumbnail, "Landscape", mainStub?.Landscape, mediaItemId, mainStub?.Title).ConfigureAwait(false); } //Actor fanart if (actors != null) { foreach (var actor in actors) { if (!IsInCache(actor.Item1)) { var existingThumbs = fanArtCache.GetFanArtFiles(actor.Item1, FanArtTypes.Thumbnail); var actorStub = mainStub?.Actors?.FirstOrDefault(a => string.Equals(a?.Name, actor.Item2, StringComparison.InvariantCultureIgnoreCase)); if (actorStub != null || existingThumbs.Any()) //We have a thumb already or no thumb is available, so no need to check again { AddToCache(actor.Item1); } if (actorStub?.Thumb != null) { await fanArtCache.TrySaveFanArt(actor.Item1, actor.Item2, FanArtTypes.Thumbnail, p => TrySaveFileImage(actorStub.Thumb, p, "Thumb", "Nfo.")).ConfigureAwait(false); } } } } } } }
public void TestNfoMovieReaderWriteMetadata() { //Arrange MovieStub movieStub = CreateTestMovieStub(CreateTestActors()); NfoMovieReader readerVideoOnly = new NfoMovieReader(new ConsoleLogger(LogLevel.All, true), 1, true, false, false, null, new NfoMovieMetadataExtractorSettings()); readerVideoOnly.GetMovieStubs().Add(movieStub); NfoMovieReader readerMovieOnly = new NfoMovieReader(new ConsoleLogger(LogLevel.All, true), 1, false, false, false, null, new NfoMovieMetadataExtractorSettings()); readerMovieOnly.GetMovieStubs().Add(movieStub); //Act IDictionary <Guid, IList <MediaItemAspect> > aspectsVideoOnly = new Dictionary <Guid, IList <MediaItemAspect> >(); readerVideoOnly.TryWriteMetadata(aspectsVideoOnly); IDictionary <Guid, IList <MediaItemAspect> > aspectsMovieOnly = new Dictionary <Guid, IList <MediaItemAspect> >(); readerMovieOnly.TryWriteMetadata(aspectsMovieOnly); //Assert //Video aspects only MediaItemAspect mediaAspect = MediaItemAspect.GetAspect(aspectsVideoOnly, MediaAspect.Metadata); Assert.NotNull(mediaAspect); Assert.AreEqual(movieStub.Title, mediaAspect.GetAttributeValue <string>(MediaAspect.ATTR_TITLE)); Assert.AreEqual(movieStub.SortTitle, mediaAspect.GetAttributeValue <string>(MediaAspect.ATTR_SORT_TITLE)); Assert.AreEqual(movieStub.Premiered, mediaAspect.GetAttributeValue <DateTime?>(MediaAspect.ATTR_RECORDINGTIME)); Assert.AreEqual(movieStub.PlayCount, mediaAspect.GetAttributeValue <int?>(MediaAspect.ATTR_PLAYCOUNT)); Assert.AreEqual(movieStub.LastPlayed, mediaAspect.GetAttributeValue <DateTime?>(MediaAspect.ATTR_LASTPLAYED)); MediaItemAspect videoAspect = MediaItemAspect.GetAspect(aspectsVideoOnly, VideoAspect.Metadata); Assert.NotNull(videoAspect); Assert.AreEqual(movieStub.Plot, videoAspect.GetAttributeValue <string>(VideoAspect.ATTR_STORYPLOT)); CollectionAssert.AreEqual(movieStub.Actors.OrderBy(p => p.Order).Select(p => p.Name), videoAspect.GetCollectionAttribute <string>(VideoAspect.ATTR_ACTORS)); Assert.AreEqual(movieStub.Director, videoAspect.GetCollectionAttribute <string>(VideoAspect.ATTR_DIRECTORS).First()); CollectionAssert.AreEqual(movieStub.Credits, videoAspect.GetCollectionAttribute <string>(VideoAspect.ATTR_WRITERS)); //ToDo: Rework Genre Mapper to make it testable, currently depends on IPathManager/language files! //IList<MediaItemAspect> genreAspects; //Assert.IsTrue(aspects.TryGetValue(GenreAspect.ASPECT_ID, out genreAspects)); //CollectionAssert.AreEqual(movieStub.Genres, genreAspects.Select(g => g.GetAttributeValue<string>(GenreAspect.ATTR_GENRE))); MediaItemAspect thumbnailAspect = MediaItemAspect.GetAspect(aspectsVideoOnly, ThumbnailLargeAspect.Metadata); Assert.NotNull(thumbnailAspect); CollectionAssert.AreEqual(movieStub.Thumb, thumbnailAspect.GetAttributeValue <byte[]>(ThumbnailLargeAspect.ATTR_THUMBNAIL)); //Movie aspects only MediaItemAspect movieAspect = MediaItemAspect.GetAspect(aspectsMovieOnly, MovieAspect.Metadata); Assert.NotNull(movieAspect); CollectionAssert.AreEqual(movieStub.Companies, movieAspect.GetCollectionAttribute <string>(MovieAspect.ATTR_COMPANIES)); Assert.AreEqual(movieStub.Title, movieAspect.GetAttributeValue <string>(MovieAspect.ATTR_MOVIE_NAME)); Assert.AreEqual(movieStub.OriginalTitle, movieAspect.GetAttributeValue <string>(MovieAspect.ATTR_ORIG_MOVIE_NAME)); Assert.AreEqual(movieStub.Sets.OrderBy(set => set.Order).First().Name, movieAspect.GetAttributeValue <string>(MovieAspect.ATTR_COLLECTION_NAME)); Assert.AreEqual((int)movieStub.Runtime.Value.TotalMinutes, movieAspect.GetAttributeValue <int?>(MovieAspect.ATTR_RUNTIME_M)); Assert.AreEqual("US_PG", movieAspect.GetAttributeValue <string>(MovieAspect.ATTR_CERTIFICATION)); Assert.AreEqual(movieStub.Tagline, movieAspect.GetAttributeValue <string>(MovieAspect.ATTR_TAGLINE)); Assert.AreEqual((double)movieStub.Rating, movieAspect.GetAttributeValue <double?>(MovieAspect.ATTR_TOTAL_RATING)); Assert.AreEqual(movieStub.Votes, movieAspect.GetAttributeValue <int?>(MovieAspect.ATTR_RATING_COUNT)); IList <MediaItemAspect> externalIdentifiers; Assert.IsTrue(aspectsMovieOnly.TryGetValue(ExternalIdentifierAspect.ASPECT_ID, out externalIdentifiers)); Assert.IsTrue(TestUtils.HasExternalId(externalIdentifiers, ExternalIdentifierAspect.SOURCE_TMDB, ExternalIdentifierAspect.TYPE_MOVIE, movieStub.TmdbId.ToString())); Assert.IsTrue(TestUtils.HasExternalId(externalIdentifiers, ExternalIdentifierAspect.SOURCE_TMDB, ExternalIdentifierAspect.TYPE_COLLECTION, movieStub.TmdbCollectionId.ToString())); Assert.IsTrue(TestUtils.HasExternalId(externalIdentifiers, ExternalIdentifierAspect.SOURCE_IMDB, ExternalIdentifierAspect.TYPE_MOVIE, movieStub.Id)); Assert.IsTrue(TestUtils.HasExternalId(externalIdentifiers, ExternalIdentifierAspect.SOURCE_ALLOCINE, ExternalIdentifierAspect.TYPE_MOVIE, movieStub.Allocine.ToString())); Assert.IsTrue(TestUtils.HasExternalId(externalIdentifiers, ExternalIdentifierAspect.SOURCE_CINEPASSION, ExternalIdentifierAspect.TYPE_MOVIE, movieStub.Cinepassion.ToString())); }
/// <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); } }