Example #1
0
        public MockedDatabaseMovie(MediaLibraryMovie movie, int playPercentage)
        {
            IDictionary <Guid, IList <MediaItemAspect> > movieAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
            MultipleMediaItemAspect resourceAspect = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, "c:\\" + movie.Title + ".mkv");
            MediaItemAspect.AddOrUpdateAspect(movieAspects, resourceAspect);
            MediaItemAspect.AddOrUpdateExternalIdentifier(movieAspects, ExternalIdentifierAspect.SOURCE_IMDB, ExternalIdentifierAspect.TYPE_MOVIE, movie.Imdb);
            MediaItemAspect.SetAttribute(movieAspects, MovieAspect.ATTR_MOVIE_NAME, movie.Title);

            SingleMediaItemAspect mediaItemAspect = new SingleMediaItemAspect(MediaAspect.Metadata);

            mediaItemAspect.SetAttribute(MediaAspect.ATTR_PLAYCOUNT, 1);
            mediaItemAspect.SetAttribute(MediaAspect.ATTR_LASTPLAYED, DateTime.Now);
            MediaItemAspect.SetAspect(movieAspects, mediaItemAspect);

            SingleMediaItemAspect importerAspect = new SingleMediaItemAspect(ImporterAspect.Metadata);

            importerAspect.SetAttribute(ImporterAspect.ATTR_DATEADDED, DateTime.Now);

            IDictionary <string, string> userData = new Dictionary <string, string>();

            userData.Add(UserDataKeysKnown.KEY_PLAY_PERCENTAGE, playPercentage.ToString());
            MediaItemAspect.SetAspect(movieAspects, importerAspect);

            Movie = new MediaItem(Guid.NewGuid(), movieAspects, userData);
        }
        public void TestAudioItem()
        {
            IList <IDirectoryObject> objects = new List <IDirectoryObject>();

            Guid id = new Guid("11111111-aaaa-aaaa-aaaa-111111111111");
            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();

            SingleMediaItemAspect aspect1 = new SingleMediaItemAspect(MediaAspect.Metadata);

            aspect1.SetAttribute(MediaAspect.ATTR_TITLE, "The Track");
            MediaItemAspect.SetAspect(aspects, aspect1);

            MultipleMediaItemAspect aspect2 = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            aspect2.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, "c:\\file.mp3");
            MediaItemAspect.AddOrUpdateAspect(aspects, aspect2);

            SingleMediaItemAspect aspect3 = new SingleMediaItemAspect(AudioAspect.Metadata);

            MediaItemAspect.SetAspect(aspects, aspect3);

            MediaItem item = new MediaItem(id, aspects);

            objects.Add(MediaLibraryHelper.InstansiateMediaLibraryObject(item, null, null));

            GenericDidlMessageBuilder builder = new GenericDidlMessageBuilder();

            builder.BuildAll("*", objects);

            string xml = builder.ToString();

            Console.WriteLine("XML: {0}", xml);
        }
Example #3
0
        private static void TransferTransientAspects(IDictionary <Guid, IList <MediaItemAspect> > aspects, IEnumerable <MediaItem> destinationMediaItems)
        {
            var transientAspects = MediaItemAspect.GetAspects(aspects).Where(mia => mia.Metadata.IsTransientAspect);

            foreach (MediaItemAspect aspect in transientAspects)
            {
                SingleMediaItemAspect singleAspect = aspect as SingleMediaItemAspect;
                if (singleAspect != null)
                {
                    foreach (MediaItem destination in destinationMediaItems)
                    {
                        MediaItemAspect.SetAspect(destination.Aspects, singleAspect);
                    }
                }
                else
                {
                    MultipleMediaItemAspect multiAspect = aspect as MultipleMediaItemAspect;
                    if (multiAspect != null)
                    {
                        foreach (MediaItem destination in destinationMediaItems)
                        {
                            MediaItemAspect.AddOrUpdateAspect(destination.Aspects, multiAspect);
                        }
                    }
                }
            }
        }
Example #4
0
        public void TestExtractThumbnail()
        {
            // Arrange
            string testFileName            = "Test Media.mkv";
            string testDataDir             = TestContext.CurrentContext.TestDirectory + "\\VideoThumbnailer\\TestData\\";
            ILocalFsResourceAccessor lfsra = new LocalFsResourceAccessor(new LocalFsResourceProvider(), "/" + Path.Combine(testDataDir, testFileName));

            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();
            MultipleMediaItemAspect videoStreamAspect            = new MultipleMediaItemAspect(VideoStreamAspect.Metadata);

            videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
            videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 1);
            videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_DURATION, (long)1);
            videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_WIDTH, 1076);
            videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_HEIGHT, 1916);
            MediaItemAspect.AddOrUpdateAspect(aspects, videoStreamAspect);
            MultipleMediaItemAspect resourceAspect = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
            resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_PRIMARY);
            resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, lfsra.CanonicalLocalResourcePath.Serialize());
            MediaItemAspect.AddOrUpdateAspect(aspects, resourceAspect);

            // Act
            bool success = new OCVVideoThumbnailer().TryExtractMetadataAsync(lfsra, aspects, false).Result;

            // Assert
            Assert.IsTrue(success);
            Assert.IsTrue(aspects.ContainsKey(ThumbnailLargeAspect.ASPECT_ID));
        }
Example #5
0
        /// <summary>
        /// Copies the contained person information into MediaItemAspect.
        /// </summary>
        /// <param name="aspectData">Dictionary with extracted aspects.</param>
        public override bool SetMetadata(IDictionary <Guid, IList <MediaItemAspect> > aspectData, bool force = false)
        {
            if (!force && !IsBaseInfoPresent)
            {
                return(false);
            }

            SingleMediaItemAspect subtitleAspect = MediaItemAspect.GetOrCreateAspect(aspectData, TempSubtitleAspect.Metadata);

            subtitleAspect.SetAttribute(TempSubtitleAspect.ATTR_PROVIDER, string.Join(";", DataProviders));
            subtitleAspect.SetAttribute(TempSubtitleAspect.ATTR_CATEGORY, string.Join(";", Categories));
            subtitleAspect.SetAttribute(TempSubtitleAspect.ATTR_NAME, Name);
            subtitleAspect.SetAttribute(TempSubtitleAspect.ATTR_DISPLAY_NAME, DisplayName);
            subtitleAspect.SetAttribute(TempSubtitleAspect.ATTR_SUBTITLEID, SubtitleId);
            subtitleAspect.SetAttribute(TempSubtitleAspect.ATTR_LANGUAGE, Language);

            int idx = 0;

            foreach (var mediaFile in MediaFiles)
            {
                MultipleMediaItemAspect resourceAspect = MediaItemAspect.CreateAspect(aspectData, ProviderResourceAspect.Metadata);
                resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, idx++);
                resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_PRIMARY);
                resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "subtitle/unknown");
                resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, mediaFile.NativeSystemId);
                resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, mediaFile.NativeResourcePath.Serialize());
            }

            return(true);
        }
        public PlaylistItem(VideoViewModel videoInfo, string resolvedPlaybackUrl)
            : base(Guid.Empty, new Dictionary <Guid, IList <MediaItemAspect> >
        {
            { ProviderResourceAspect.ASPECT_ID, new MediaItemAspect[] { new MultipleMediaItemAspect(ProviderResourceAspect.Metadata) } },
            { MediaAspect.ASPECT_ID, new MediaItemAspect[] { new SingleMediaItemAspect(MediaAspect.Metadata) } },
            { VideoAspect.ASPECT_ID, new MediaItemAspect[] { new SingleMediaItemAspect(VideoAspect.Metadata) } },
            { OnlineVideosAspect.ASPECT_ID, new MediaItemAspect[] { new SingleMediaItemAspect(OnlineVideosAspect.Metadata) } },
        })
        {
            SiteName  = videoInfo.SiteName;
            VideoInfo = videoInfo.VideoInfo;

            Aspects[OnlineVideosAspect.ASPECT_ID].First().SetAttribute(OnlineVideosAspect.ATTR_SITEUTIL, SiteName);

            ISystemResolver systemResolver = ServiceRegistration.Get <ISystemResolver>();

            IList <MultipleMediaItemAspect> providerResourceAspects;

            MediaItemAspect.TryGetAspects(Aspects, ProviderResourceAspect.Metadata, out providerResourceAspects);
            MultipleMediaItemAspect providerResourceAspect = providerResourceAspects.First();

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, systemResolver.LocalSystemId);

            if (videoInfo.SiteUtilName == "DownloadedVideo")
            {
                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, LocalFsResourceProviderBase.ToResourcePath(resolvedPlaybackUrl).Serialize());
                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/unknown");
            }
            else
            {
                Uri uri;
                // Test if the resolved "url" is a real Uri (Sites can provide any content here)
                var isUriSource = Uri.TryCreate(resolvedPlaybackUrl, UriKind.Absolute, out uri);

                var value = isUriSource
                    ? RawUrlResourceProvider.ToProviderResourcePath(resolvedPlaybackUrl).Serialize()
                    : RawTokenResourceProvider.ToProviderResourcePath(resolvedPlaybackUrl).Serialize();
                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, value);
                Aspects[OnlineVideosAspect.ASPECT_ID].First().SetAttribute(OnlineVideosAspect.ATTR_LONGURL, value);

                var isBrowser = videoInfo.SiteSettings.Player == PlayerType.Browser;
                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE,
                                                    isBrowser
                        ? WebBrowserVideoPlayer.ONLINEVIDEOSBROWSER_MIMETYPE
                        : OnlineVideosPlayer.ONLINEVIDEOS_MIMETYPE);
            }

            MediaItemAspect.SetAttribute(Aspects, MediaAspect.ATTR_TITLE, videoInfo.Title);

            // TODO: Restore line after story plot was moved back to VideoAspect!
            // MediaItemAspect.SetAttribute(aspects, VideoAspect.ATTR_STORYPLOT, videoInfo.Description);

            DateTime parsedAirDate;

            if (DateTime.TryParse(videoInfo.VideoInfo.Airdate, out parsedAirDate))
            {
                MediaItemAspect.SetAttribute(Aspects, MediaAspect.ATTR_RECORDINGTIME, parsedAirDate);
            }
        }
Example #7
0
 /// <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);
 }
        /// <summary>
        /// Checks if the current MediaItem contains fps information, if not it tries to get it from
        /// the source filter.
        /// </summary>
        protected virtual void UpdateVideoFps()
        {
            IList <MultipleMediaItemAspect> videoAspects;

            // If there are VideoStreamAspects we don't need to fill it.
            if (_mediaItem == null || MediaItemAspect.TryGetAspects(_mediaItem.Aspects, VideoStreamAspect.Metadata, out videoAspects))
            {
                return;
            }

            using (DSFilter d = new DSFilter((IBaseFilter)_tsReader))
            {
                // Would release the filter which causes errors in later access (like stream enumeration)
                d.ReleaseOnDestroy = false;
                var videoOutPin = d.Pins.FirstOrDefault(p => p.Direction == PinDirection.Output && p.ConnectionMediaType?.majorType == MediaType.Video);
                if (videoOutPin != null)
                {
                    const long nTenMillion        = 10000000;
                    long       avgTimePerFrameHns = videoOutPin.ConnectionMediaType.GetFrameRate();
                    if (avgTimePerFrameHns == 0)
                    {
                        return;
                    }

                    float fps = (float)nTenMillion / avgTimePerFrameHns;

                    MultipleMediaItemAspect videoStreamAspects = MediaItemAspect.CreateAspect(_mediaItem.Aspects, VideoStreamAspect.Metadata);
                    videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
                    videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, 0);
                    videoStreamAspects.SetAttribute(VideoStreamAspect.ATTR_FPS, fps);
                }
            }
        }
Example #9
0
        private static void SetProviderResourceAspect(string resolvedPlaybackUrl, MediaItem item, string mimeType)
        {
            MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(item.Aspects, ProviderResourceAspect.Metadata);

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, ServiceRegistration.Get <ISystemResolver>().LocalSystemId);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, RawUrlResourceProvider.ToProviderResourcePath(resolvedPlaybackUrl).Serialize());
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mimeType);
        }
Example #10
0
 public static void StorePersons(IDictionary <Guid, IList <MediaItemAspect> > aspects, List <PersonInfo> infoPersons, bool forAlbum)
 {
     foreach (PersonInfo person in infoPersons)
     {
         MultipleMediaItemAspect personAspect = MediaItemAspect.CreateAspect(aspects, TempPersonAspect.Metadata);
         personAspect.SetAttribute(TempPersonAspect.ATTR_MBID, person.MusicBrainzId);
         personAspect.SetAttribute(TempPersonAspect.ATTR_NAME, person.Name);
         personAspect.SetAttribute(TempPersonAspect.ATTR_OCCUPATION, person.Occupation);
         personAspect.SetAttribute(TempPersonAspect.ATTR_FROMALBUM, forAlbum);
     }
 }
Example #11
0
 public static void SetVideoMetaData(this MediaItem item, DmapData metaData)
 {
     MediaItemAspect.SetAttribute(item.Aspects, MediaAspect.ATTR_TITLE, metaData.Title);
     MediaItemAspect.SetCollectionAttribute(item.Aspects, VideoAspect.ATTR_ACTORS, metaData.Actors);
     foreach (string genre in metaData.Genres)
     {
         MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(item.Aspects, GenreAspect.Metadata);
         genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre);
     }
     MediaItemAspect.SetCollectionAttribute(item.Aspects, VideoAspect.ATTR_DIRECTORS, metaData.Directors);
 }
 public static void StoreCharacters(IDictionary <Guid, IList <MediaItemAspect> > aspects, List <CharacterInfo> infoCharacters, bool forSeries)
 {
     foreach (CharacterInfo character in infoCharacters)
     {
         MultipleMediaItemAspect personAspect = MediaItemAspect.CreateAspect(aspects, TempPersonAspect.Metadata);
         personAspect.SetAttribute(TempPersonAspect.ATTR_IMDBID, character.ActorImdbId);
         personAspect.SetAttribute(TempPersonAspect.ATTR_NAME, character.ActorName);
         personAspect.SetAttribute(TempPersonAspect.ATTR_CHARACTER, character.Name);
         personAspect.SetAttribute(TempPersonAspect.ATTR_ORDER, character.Order);
         personAspect.SetAttribute(TempPersonAspect.ATTR_FROMSERIES, forSeries);
     }
 }
Example #13
0
 public static void SetAudioMetaData(this MediaItem item, DmapData metaData)
 {
     MediaItemAspect.SetAttribute(item.Aspects, MediaAspect.ATTR_TITLE, metaData.Title);
     MediaItemAspect.SetAttribute(item.Aspects, AudioAspect.ATTR_ALBUM, metaData.Album);
     MediaItemAspect.SetCollectionAttribute(item.Aspects, AudioAspect.ATTR_ARTISTS, metaData.Artists);
     foreach (string genre in metaData.Genres)
     {
         MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(item.Aspects, GenreAspect.Metadata);
         genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre);
     }
     MediaItemAspect.SetAttribute(item.Aspects, AudioAspect.ATTR_TRACK, metaData.OriginalTrackNumber);
     MediaItemAspect.SetAttribute(item.Aspects, AudioAspect.ATTR_NUMDISCS, metaData.OriginalDiscCount);
 }
        public static void StorePersonAndCharacter(IDictionary <Guid, IList <MediaItemAspect> > aspects, PersonStub person, string occupation, bool forSeries)
        {
            MultipleMediaItemAspect personAspect = MediaItemAspect.CreateAspect(aspects, TempPersonAspect.Metadata);

            personAspect.SetAttribute(TempPersonAspect.ATTR_IMDBID, person.ImdbId);
            personAspect.SetAttribute(TempPersonAspect.ATTR_NAME, person.Name);
            personAspect.SetAttribute(TempPersonAspect.ATTR_OCCUPATION, occupation);
            personAspect.SetAttribute(TempPersonAspect.ATTR_CHARACTER, person.Role);
            personAspect.SetAttribute(TempPersonAspect.ATTR_BIOGRAPHY, !string.IsNullOrEmpty(person.Biography) ? person.Biography : person.MiniBiography);
            personAspect.SetAttribute(TempPersonAspect.ATTR_DATEOFBIRTH, person.Birthdate);
            personAspect.SetAttribute(TempPersonAspect.ATTR_DATEOFDEATH, person.Deathdate);
            personAspect.SetAttribute(TempPersonAspect.ATTR_ORDER, person.Order);
            personAspect.SetAttribute(TempPersonAspect.ATTR_ORIGIN, person.Birthplace);
            personAspect.SetAttribute(TempPersonAspect.ATTR_FROMSERIES, forSeries);
        }
 public static void StorePersons(IDictionary <Guid, IList <MediaItemAspect> > aspects, List <PersonInfo> infoPersons, bool forSeries)
 {
     foreach (PersonInfo person in infoPersons)
     {
         MultipleMediaItemAspect personAspect = MediaItemAspect.CreateAspect(aspects, TempPersonAspect.Metadata);
         personAspect.SetAttribute(TempPersonAspect.ATTR_IMDBID, person.ImdbId);
         personAspect.SetAttribute(TempPersonAspect.ATTR_NAME, person.Name);
         personAspect.SetAttribute(TempPersonAspect.ATTR_OCCUPATION, person.Occupation);
         personAspect.SetAttribute(TempPersonAspect.ATTR_BIOGRAPHY, person.Biography);
         personAspect.SetAttribute(TempPersonAspect.ATTR_DATEOFBIRTH, person.DateOfBirth);
         personAspect.SetAttribute(TempPersonAspect.ATTR_DATEOFDEATH, person.DateOfDeath);
         personAspect.SetAttribute(TempPersonAspect.ATTR_ORDER, person.Order);
         personAspect.SetAttribute(TempPersonAspect.ATTR_ORIGIN, person.Orign);
         personAspect.SetAttribute(TempPersonAspect.ATTR_FROMSERIES, forSeries);
     }
 }
        private static LiveTvMediaItem.LiveTvMediaItem CreateCommonMediaItem(int slotIndex, string path, bool isTv, string customMimeType = null)
        {
            ISystemResolver systemResolver = ServiceRegistration.Get <ISystemResolver>();
            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();

            SlimTvResourceAccessor resourceAccessor = new SlimTvResourceAccessor(slotIndex, path);

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

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

            String raPath = resourceAccessor.CanonicalLocalResourcePath.Serialize();

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, raPath);

            string title;
            string mimeType;

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

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

            MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_TITLE, title);
            MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(title));
            MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mimeType); // Custom mimetype for LiveTv or Radio
            LiveTvMediaItem.LiveTvMediaItem tvStream = new LiveTvMediaItem.LiveTvMediaItem(new Guid(), aspects);
            return(tvStream);
        }
Example #17
0
        public void TestConvenienceMethod()
        {
            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();

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

            providerAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
            providerAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true);
            providerAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "audio/mp3");

            Assert.AreEqual(aspects.Keys.Count, 1, "aspect key count");

            IList <MultipleMediaItemAspect> providerAspects2;

            Assert.IsTrue(MediaItemAspect.TryGetAspects(aspects, ProviderResourceAspect.Metadata, out providerAspects2), "ProviderResource");
            Assert.AreEqual("audio/mp3", providerAspects2[0][ProviderResourceAspect.ATTR_MIME_TYPE], "MIME type");
        }
        public MockedDatabaseMovie(string imdbId, string tmdbId, string title, int year, int playPercentage)
        {
            IDictionary <Guid, IList <MediaItemAspect> > movieAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
            MultipleMediaItemAspect resourceAspect = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, "c:\\" + title + ".mkv");
            MediaItemAspect.AddOrUpdateAspect(movieAspects, resourceAspect);
            MediaItemAspect.AddOrUpdateExternalIdentifier(movieAspects, ExternalIdentifierAspect.SOURCE_IMDB, ExternalIdentifierAspect.TYPE_MOVIE, imdbId);
            MediaItemAspect.AddOrUpdateExternalIdentifier(movieAspects, ExternalIdentifierAspect.SOURCE_TMDB, ExternalIdentifierAspect.TYPE_MOVIE, tmdbId);
            MediaItemAspect.SetAttribute(movieAspects, MovieAspect.ATTR_MOVIE_NAME, title);
            SingleMediaItemAspect smia = new SingleMediaItemAspect(MediaAspect.Metadata);

            smia.SetAttribute(MediaAspect.ATTR_RECORDINGTIME, new DateTime(year, 1, 1));
            MediaItemAspect.SetAspect(movieAspects, smia);
            IDictionary <string, string> userData = new Dictionary <string, string>();

            userData.Add(UserDataKeysKnown.KEY_PLAY_PERCENTAGE, playPercentage.ToString());
            Movie = new MediaItem(Guid.NewGuid(), movieAspects, userData);
        }
Example #19
0
        public MockedDatabaseEpisode(string tvDbId, int seasonIndex, List <int> episodeIndex, int playPercentage)
        {
            IDictionary <Guid, IList <MediaItemAspect> > episodeAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
            MultipleMediaItemAspect resourceAspect = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, "c:\\" + tvDbId + ".mkv");
            MediaItemAspect.AddOrUpdateAspect(episodeAspects, resourceAspect);
            MediaItemAspect.AddOrUpdateExternalIdentifier(episodeAspects, ExternalIdentifierAspect.SOURCE_TVDB, ExternalIdentifierAspect.TYPE_SERIES, tvDbId);
            MediaItemAspect.SetAttribute(episodeAspects, EpisodeAspect.ATTR_SEASON, seasonIndex);
            MediaItemAspect.SetCollectionAttribute(episodeAspects, EpisodeAspect.ATTR_EPISODE, episodeIndex);
            SingleMediaItemAspect smia = new SingleMediaItemAspect(MediaAspect.Metadata);

            MediaItemAspect.SetAspect(episodeAspects, smia);
            IDictionary <string, string> userData = new Dictionary <string, string>();

            userData.Add(UserDataKeysKnown.KEY_PLAY_PERCENTAGE, playPercentage.ToString());

            Episode = new MediaItem(Guid.NewGuid(), episodeAspects, userData);
        }
        /// <summary>
        /// Constructs a dynamic <see cref="MediaItem"/> that contains the URL for the given <paramref name="stream"/>.
        /// </summary>
        /// <param name="stream">Stream.</param>
        private static MediaItem CreateStreamMediaItem(MyStream stream)
        {
            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();

            MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(aspects, ProviderResourceAspect.Metadata);
            SingleMediaItemAspect   mediaAspect            = MediaItemAspect.GetOrCreateAspect(aspects, MediaAspect.Metadata);
            SingleMediaItemAspect   audioAspect            = MediaItemAspect.GetOrCreateAspect(aspects, AudioAspect.Metadata);

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_PRIMARY);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, RawUrlResourceProvider.ToProviderResourcePath(stream.StreamUrls[0].StreamUrl).Serialize());
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, ServiceRegistration.Get <ISystemResolver>().LocalSystemId);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, WEBRADIO_MIMETYPE);

            mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, stream.Title);

            MediaItemAspect.SetAttribute(aspects, ThumbnailLargeAspect.ATTR_THUMBNAIL, ImageFromLogo(stream.Logo));

            var mediaItem = new MediaItem(Guid.Empty, aspects);

            return(mediaItem);
        }
Example #21
0
        /// <summary>
        /// Constructs a dynamic <see cref="MediaItem"/> that contains the URL for the given <paramref name="trailer"/>.
        /// </summary>
        /// <param name="trailer">Trailer.</param>
        private static MediaItem CreateStreamMediaItem(Trailer trailer)
        {
            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();

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

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, ServiceRegistration.Get <ISystemResolver>().LocalSystemId);

            MediaItemAspect.GetOrCreateAspect(aspects, VideoAspect.Metadata);

            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, RawUrlResourceProvider.ToProviderResourcePath(trailer.Url).Serialize());
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, ServiceRegistration.Get <ISystemResolver>().LocalSystemId);
            providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, CINEMA_MIMETYPE);

            MediaItemAspect.SetAttribute(aspects, MediaAspect.ATTR_TITLE, trailer.Title);

            var mediaItem = new MediaItem(Guid.Empty, aspects);

            return(mediaItem);
        }
Example #22
0
        public MediaItem CreateLocalMediaItem(IResourceAccessor mediaItemAccessor, IEnumerable <Guid> metadataExtractorIds)
        {
            ISystemResolver systemResolver = ServiceRegistration.Get <ISystemResolver>();
            IDictionary <Guid, IList <MediaItemAspect> > aspects = ExtractMetadataAsync(mediaItemAccessor, metadataExtractorIds, true).Result;

            if (aspects == null)
            {
                return(null);
            }
            IList <MultipleMediaItemAspect> providerResourceAspects;

            if (MediaItemAspect.TryGetAspects(aspects, ProviderResourceAspect.Metadata, out providerResourceAspects) && providerResourceAspects.Count > 0)
            {
                MultipleMediaItemAspect providerResourceAspect = providerResourceAspects.First();
                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_TYPE, ProviderResourceAspect.TYPE_PRIMARY);
                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SYSTEM_ID, systemResolver.LocalSystemId);
                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, mediaItemAccessor.CanonicalLocalResourcePath.Serialize());
                return(new MediaItem(Guid.Empty, aspects));
            }
            return(null);
        }
        public void TestAudioItem()
        {
            IList <IDirectoryObject> objects = new List <IDirectoryObject>();

            BasicContainer root = new BasicContainer("TEST", new EndPointSettings
            {
                PreferredSubtitleLanguages = new string[] { "EN" },
                PreferredAudioLanguages    = new string[] { "EN" },
                Profile = new EndPointProfile()
            });

            Guid id = new Guid("11111111-aaaa-aaaa-aaaa-111111111111");
            IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();

            SingleMediaItemAspect aspect1 = new SingleMediaItemAspect(MediaAspect.Metadata);

            aspect1.SetAttribute(MediaAspect.ATTR_TITLE, "The Track");
            MediaItemAspect.SetAspect(aspects, aspect1);

            MultipleMediaItemAspect aspect2 = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            aspect2.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, "c:\\file.mp3");
            MediaItemAspect.AddOrUpdateAspect(aspects, aspect2);

            SingleMediaItemAspect aspect3 = new SingleMediaItemAspect(AudioAspect.Metadata);

            MediaItemAspect.SetAspect(aspects, aspect3);

            MediaItem item = new MediaItem(id, aspects);

            objects.Add(MediaLibraryHelper.InstansiateMediaLibraryObject(item, root, "Test"));

            GenericDidlMessageBuilder builder = new GenericDidlMessageBuilder();

            builder.BuildAll("*", objects);

            string xml = builder.ToString();

            Console.WriteLine("XML: {0}", xml);
        }
        public MockedDatabaseMovie(MediaLibraryMovie movie)
        {
            IDictionary <Guid, IList <MediaItemAspect> > movieAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
            MultipleMediaItemAspect resourceAspect = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            resourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, "c:\\" + movie.Title + ".mkv");
            MediaItemAspect.AddOrUpdateAspect(movieAspects, resourceAspect);
            MediaItemAspect.AddOrUpdateExternalIdentifier(movieAspects, ExternalIdentifierAspect.SOURCE_IMDB, ExternalIdentifierAspect.TYPE_MOVIE, movie.Imdb);
            MediaItemAspect.SetAttribute(movieAspects, MovieAspect.ATTR_MOVIE_NAME, movie.Title);

            SingleMediaItemAspect mediaItemAspect = new SingleMediaItemAspect(MediaAspect.Metadata);

            mediaItemAspect.SetAttribute(MediaAspect.ATTR_PLAYCOUNT, movie.PlayCount);
            mediaItemAspect.SetAttribute(MediaAspect.ATTR_LASTPLAYED, DateTime.Parse(movie.LastPlayed));
            MediaItemAspect.SetAspect(movieAspects, mediaItemAspect);

            SingleMediaItemAspect importerAspect = new SingleMediaItemAspect(ImporterAspect.Metadata);

            importerAspect.SetAttribute(ImporterAspect.ATTR_DATEADDED, DateTime.Parse(movie.AddedToDb));
            MediaItemAspect.SetAspect(movieAspects, importerAspect);

            Movie = new MediaItem(Guid.NewGuid(), movieAspects);
        }
        public virtual Task <bool> TryExtractMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            try
            {
                IResourceAccessor metaFileAccessor;
                if (!CanExtract(mediaItemAccessor, extractedAspectData, out metaFileAccessor))
                {
                    return(Task.FromResult(false));
                }

                Argus.Recording recording;
                using (metaFileAccessor)
                {
                    using (Stream metaStream = ((IFileSystemResourceAccessor)metaFileAccessor).OpenRead())
                        recording = (Argus.Recording)GetTagsXmlSerializer().Deserialize(metaStream);
                }

                // Force MimeType
                IList <MultipleMediaItemAspect> providerAspects;
                MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerAspects);
                foreach (MultipleMediaItemAspect aspect in providerAspects)
                {
                    aspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/arg");
                }

                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false);

                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, recording.Title);
                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(recording.Title));

                if (!string.IsNullOrEmpty(recording.Category?.Trim()))
                {
                    List <GenreInfo> genreList = new List <GenreInfo>(new GenreInfo[] { new GenreInfo {
                                                                                            Name = recording.Category.Trim()
                                                                                        } });
                    IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                    foreach (var genre in genreList)
                    {
                        if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId))
                        {
                            genre.Id = genreId;
                        }
                    }
                    if (genreList.Count > 0)
                    {
                        MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata);
                        genreAspect.SetAttribute(GenreAspect.ATTR_ID, genreList[0].Id);
                        genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genreList[0].Name);
                    }
                }

                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, recording.Description);

                Match yearMatch = _yearMatcher.Match(recording.Description);
                int   guessedYear;
                if (int.TryParse(yearMatch.Value, out guessedYear))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, new DateTime(guessedYear, 1, 1));
                }

                MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, recording.ChannelDisplayName);

                MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, recording.ProgramStartTime);

                MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, recording.ProgramStopTime);
                RecordingUtils.CheckAndPrepareAspectRefresh(extractedAspectData);

                if (!string.IsNullOrWhiteSpace(recording.Director))
                {
                    MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_DIRECTORS, recording.Director.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
                }

                if (!string.IsNullOrWhiteSpace(recording.Actors))
                {
                    MediaItemAspect.SetCollectionAttribute(extractedAspectData, VideoAspect.ATTR_ACTORS, recording.Actors.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
                }

                return(Task.FromResult(true));
            }
            catch (Exception e)
            {
                // Only log at the info level here - And simply return false. This lets the caller know that we
                // couldn't perform our task here.
                ServiceRegistration.Get <ILogger>().Info("ArgusRecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", mediaItemAccessor.CanonicalLocalResourcePath, e.Message);
            }
            return(Task.FromResult(false));
        }
Example #26
0
        protected virtual Task <bool> ExtractMetadataAsync(ILocalFsResourceAccessor lfsra, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            if (!CanExtract(lfsra, extractedAspectData))
            {
                return(Task.FromResult(false));
            }

            using (var rec = new MCRecMetadataEditor(lfsra.LocalFileSystemPath))
            {
                // Handle series information
                IDictionary tags = rec.GetAttributes();

                // Force MimeType
                IList <MultipleMediaItemAspect> providerAspects;
                MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerAspects);
                foreach (MultipleMediaItemAspect aspect in providerAspects)
                {
                    aspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/wtv");
                }

                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, false);
                MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_ISDVD, false);

                string value;
                if (TryGet(tags, TAG_TITLE, out value) && !string.IsNullOrEmpty(value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, value);
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_SORT_TITLE, BaseInfo.GetSortTitle(value));
                }

                if (TryGet(tags, TAG_GENRE, out value))
                {
                    List <GenreInfo> genreList = new List <GenreInfo>(value.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenreInfo {
                        Name = s.Trim()
                    }));
                    IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                    foreach (var genre in genreList)
                    {
                        if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Movie, null, out int genreId))
                        {
                            genre.Id = genreId;
                        }
                    }
                    foreach (GenreInfo genre in genreList)
                    {
                        MultipleMediaItemAspect genreAspect = MediaItemAspect.CreateAspect(extractedAspectData, GenreAspect.Metadata);
                        genreAspect.SetAttribute(GenreAspect.ATTR_ID, genre.Id);
                        genreAspect.SetAttribute(GenreAspect.ATTR_GENRE, genre.Name);
                    }
                }

                if (TryGet(tags, TAG_PLOT, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, VideoAspect.ATTR_STORYPLOT, value);
                }

                if (TryGet(tags, TAG_ORIGINAL_TIME, out value))
                {
                    DateTime origTime;
                    if (DateTime.TryParse(value, out origTime))
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_RECORDINGTIME, origTime);
                    }
                }

                if (TryGet(tags, TAG_CHANNEL, out value))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_CHANNEL, value);
                }

                long lValue;
                if (TryGet(tags, TAG_STARTTIME, out lValue))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_STARTTIME, FromMCEFileTime(lValue));
                }
                if (TryGet(tags, TAG_ENDTIME, out lValue))
                {
                    MediaItemAspect.SetAttribute(extractedAspectData, RecordingAspect.ATTR_ENDTIME, FromMCEFileTime(lValue));
                }
            }
            return(Task.FromResult(true));
        }
Example #27
0
        public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode)
        {
            try
            {
                if (!(mediaItemAccessor is IFileSystemResourceAccessor))
                {
                    return(false);
                }

                if (extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
                {
                    return(false);
                }

                using (LocalFsResourceAccessorHelper rah = new LocalFsResourceAccessorHelper(mediaItemAccessor))
                {
                    if (!rah.LocalFsResourceAccessor.IsFile && rah.LocalFsResourceAccessor.ResourceExists("BDMV"))
                    {
                        using (IFileSystemResourceAccessor fsraBDMV = rah.LocalFsResourceAccessor.GetResource("BDMV"))
                            if (fsraBDMV != null && fsraBDMV.ResourceExists("index.bdmv"))
                            {
                                MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata);
                                // Calling EnsureLocalFileSystemAccess not necessary; only string operation
                                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_INDEX, 0);
                                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_PRIMARY, true);
                                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "video/bluray"); // BluRay disc
                                providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, mediaItemAccessor.CanonicalLocalResourcePath.Serialize());

                                // This line is important to keep in, if no VideoAspect is created here, the MediaItems is not detected as Video!
                                SingleMediaItemAspect videoAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, VideoAspect.Metadata);
                                videoAspect.SetAttribute(VideoAspect.ATTR_ISDVD, true);

                                MultipleMediaItemAspect videoStreamAspect = MediaItemAspect.CreateAspect(extractedAspectData, VideoStreamAspect.Metadata);
                                videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_RESOURCE_INDEX, 0);
                                videoStreamAspect.SetAttribute(VideoStreamAspect.ATTR_STREAM_INDEX, -1);

                                MediaItemAspect mediaAspect = MediaItemAspect.GetOrCreateAspect(extractedAspectData, MediaAspect.Metadata);
                                mediaAspect.SetAttribute(MediaAspect.ATTR_ISVIRTUAL, false);

                                using (rah.LocalFsResourceAccessor.EnsureLocalFileSystemAccess())
                                {
                                    BDInfoExt bdinfo = new BDInfoExt(rah.LocalFsResourceAccessor.LocalFileSystemPath);
                                    string    title  = bdinfo.GetTitle();
                                    mediaAspect.SetAttribute(MediaAspect.ATTR_TITLE, title ?? mediaItemAccessor.ResourceName);

                                    // Check for BD disc thumbs
                                    FileInfo thumbnail = bdinfo.GetBiggestThumb();
                                    if (thumbnail != null)
                                    {
                                        try
                                        {
                                            using (FileStream fileStream = new FileStream(thumbnail.FullName, FileMode.Open, FileAccess.Read))
                                                using (MemoryStream resized = (MemoryStream)ImageUtilities.ResizeImage(fileStream, ImageFormat.Jpeg, MAX_COVER_WIDTH, MAX_COVER_HEIGHT))
                                                {
                                                    MediaItemAspect.SetAttribute(extractedAspectData, ThumbnailLargeAspect.ATTR_THUMBNAIL, resized.ToArray());
                                                }
                                        }
                                        // Decoding of invalid image data can fail, but main MediaItem is correct.
                                        catch
                                        {
                                        }
                                    }
                                }
                                return(true);
                            }
                    }
                }
                return(false);
            }
            catch
            {
                // Only log at the info level here - And simply return false. This makes the importer know that we
                // couldn't perform our task here
                if (mediaItemAccessor != null)
                {
                    ServiceRegistration.Get <ILogger>().Info("BluRayMetadataExtractor: Exception reading source '{0}'", mediaItemAccessor.ResourcePathName);
                }
                return(false);
            }
        }
Example #28
0
        public bool TryExtractMetadata(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool importOnly, bool forceQuickMode)
        {
            IFileSystemResourceAccessor fsra = mediaItemAccessor as IFileSystemResourceAccessor;

            if (fsra == null || !fsra.IsFile)
            {
                return(false);
            }
            if (extractedAspectData.ContainsKey(AudioAspect.ASPECT_ID))
            {
                return(false);
            }

            try
            {
                var extension = DosPathHelper.GetExtension(fsra.ResourceName).ToLowerInvariant();
                if (extension != ".ts")
                {
                    return(false);
                }

                using (MediaInfoWrapper mediaInfo = ReadMediaInfo(fsra))
                {
                    // Before we start evaluating the file, check if it is not a video file (
                    if (mediaInfo.IsValid && (mediaInfo.GetVideoCount() != 0 || mediaInfo.GetAudioCount() == 0))
                    {
                        return(false);
                    }
                    string fileName = ProviderPathHelper.GetFileNameWithoutExtension(fsra.Path) ?? string.Empty;
                    MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_TITLE, fileName);
                    MultipleMediaItemAspect providerResourceAspect = MediaItemAspect.CreateAspect(extractedAspectData, ProviderResourceAspect.Metadata);
                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_SIZE, fsra.Size);
                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, "slimtv/radio");

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

                    MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_ENCODING, mediaInfo.GetAudioCodec(0));
                    // MediaInfo returns milliseconds, we need seconds
                    long?time = mediaInfo.GetPlaytime(0);
                    if (time.HasValue && time > 1000)
                    {
                        MediaItemAspect.SetAttribute(extractedAspectData, AudioAspect.ATTR_DURATION, time.Value / 1000);
                    }
                }
                return(true);
            }
            catch (Exception e)
            {
                // Only log at the info level here - And simply return false. This makes the importer know that we
                // couldn't perform our task here
                ServiceRegistration.Get <ILogger>().Info("RadioRecordingMetadataExtractor: Exception reading resource '{0}' (Text: '{1}')", fsra.CanonicalLocalResourcePath, e.Message);
                return(false);
            }
        }
        /// <summary>
        /// Asynchronously tries to extract episode metadata for the given <param name="mediaItemAccessor"></param>
        /// </summary>
        /// <param name="mediaItemAccessor">Points to the resource for which we try to extract metadata</param>
        /// <param name="extractedAspectData">Dictionary of <see cref="MediaItemAspect"/>s with the extracted metadata</param>
        /// <param name="forceQuickMode">If <c>true</c>, nothing is downloaded from the internet</param>
        /// <returns><c>true</c> if metadata was found and stored into <param name="extractedAspectData"></param>, else <c>false</c></returns>
        private async Task <bool> TryExtractEpsiodeMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            // Get a unique number for this call to TryExtractMetadataAsync. We use this to make reading the debug log easier.
            // This MetadataExtractor is called in parallel for multiple MediaItems so that the respective debug log entries
            // for one call are not contained one after another in debug log. We therefore prepend this number before every log entry.
            var  miNumber = Interlocked.Increment(ref _lastMediaItemNumber);
            bool isStub   = extractedAspectData.ContainsKey(StubAspect.ASPECT_ID);

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

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

                // We only extract metadata with this MetadataExtractor, if another MetadataExtractor that was applied before
                // has identified this MediaItem as a video and therefore added a VideoAspect.
                if (!extractedAspectData.ContainsKey(VideoAspect.ASPECT_ID))
                {
                    _debugLogger.Info("[#{0}]: Cannot extract metadata; this resource is not a video", miNumber);
                    return(false);
                }

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

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

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

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

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

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

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

                                            IList <MultipleMediaItemAspect> providerResourceAspects;
                                            if (MediaItemAspect.TryGetAspects(extractedAspectData, ProviderResourceAspect.Metadata, out providerResourceAspects))
                                            {
                                                MultipleMediaItemAspect providerResourceAspect = providerResourceAspects.First(pa => pa.GetAttributeValue <int>(ProviderResourceAspect.ATTR_TYPE) == ProviderResourceAspect.TYPE_STUB);
                                                string mime = null;
                                                if (mergedEpisode.FileInfo != null && mergedEpisode.FileInfo.Count > 0)
                                                {
                                                    mime = MimeTypeDetector.GetMimeTypeFromExtension("file" + mergedEpisode.FileInfo.First().Container);
                                                }
                                                if (mime != null)
                                                {
                                                    providerResourceAspect.SetAttribute(ProviderResourceAspect.ATTR_MIME_TYPE, mime);
                                                }
                                            }

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

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

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

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

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

                _debugLogger.Info("[#{0}]: Successfully finished extracting metadata", miNumber);
                ServiceRegistration.Get <ILogger>().Debug("NfoSeriesMetadataExtractor: Assigned nfo episode metadata for resource '{0}'", mediaItemAccessor);
                return(true);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Warn("NfoSeriesMetadataExtractor: Exception while extracting metadata for resource '{0}'; enable debug logging for more details.", mediaItemAccessor);
                _debugLogger.Error("[#{0}]: Exception while extracting metadata", e, miNumber);
                return(false);
            }
        }
Example #30
0
        public void TestComplexItem()
        {
            Guid trackId  = new Guid("11111111-aaaa-aaaa-aaaa-100000000000");
            Guid albumId  = new Guid("11111111-aaaa-aaaa-aaaa-100000000001");
            Guid artistId = new Guid("11111111-aaaa-aaaa-aaaa-100000000002");

            Guid trackRelationship  = new Guid("22222222-bbbb-bbbb-bbbb-200000000001");
            Guid albumRelationship  = new Guid("33333333-cccc-cccc-cccc-300000000001");
            Guid artistRelationship = new Guid("44444444-dddd-dddd-dddd-400000000001");

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

            MultipleMediaItemAspect resourceAspect1 = new MultipleMediaItemAspect(ProviderResourceAspect.Metadata);

            resourceAspect1.Deleted = true;
            resourceAspect1.SetAttribute(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH, "c:\\file.mp3");
            MediaItemAspect.AddOrUpdateAspect(aspects1, resourceAspect1);

            MediaItemAspect.AddOrUpdateRelationship(aspects1, trackRelationship, albumRelationship, albumId, 1);
            MediaItemAspect.AddOrUpdateRelationship(aspects1, trackRelationship, artistRelationship, artistId, 0);

            MediaItem track1 = new MediaItem(trackId, aspects1);

            TextWriter writer     = new StringWriter();
            XmlWriter  serialiser = new XmlTextWriter(writer);

            serialiser.WriteStartElement("Test"); // Wrapper around the tracks
            // Write the track twice
            track1.Serialize(serialiser);
            track1.Serialize(serialiser);
            serialiser.WriteEndElement();

            //Console.WriteLine("XML: {0}", writer.ToString());
            //Assert.AreEqual("<MI Id=\"" + trackId + "\"><Relationship ItemType=\"" + AudioAspect.RELATIONSHIP_TRACK + "\" RelationshipType=\"" + AlbumAspect.RELATIONSHIP_ALBUM + "\" RelationshipId=\"" + albumId + "\" /></MI>", trackText.ToString(), "Track XML");

            XmlReader reader = XmlReader.Create(new StringReader(writer.ToString()));

            reader.Read(); // Test
            //Console.WriteLine("Reader state Test, {0} {1}", reader.NodeType, reader.Name);

            // Read the track once
            reader.Read(); // MI
            //Console.WriteLine("Reader state track2, {0} {1}", reader.NodeType, reader.Name);
            MediaItem track2 = MediaItem.Deserialize(reader);

            IList <MultipleMediaItemAspect> resourceAspect2;

            Assert.IsTrue(MediaItemAspect.TryGetAspects(track2.Aspects, ProviderResourceAspect.Metadata, out resourceAspect2), "Resource aspects");
            Assert.AreEqual(true, resourceAspect2[0].Deleted, "Track deleted status");
            Assert.AreEqual("c:\\file.mp3", resourceAspect2[0].GetAttributeValue <string>(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH), "Track location");
            IList <MediaItemAspect> relationships2 = track2[RelationshipAspect.ASPECT_ID];

            Assert.IsTrue(track2[RelationshipAspect.ASPECT_ID] != null, "Relationship aspects");
            Assert.AreEqual(relationships2.Count, 2, "Track relationship count");
            Assert.AreEqual(trackRelationship, relationships2[0].GetAttributeValue(RelationshipAspect.ATTR_ROLE), "Track -> album item type");
            Assert.AreEqual(albumRelationship, relationships2[0].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ROLE), "Track -> album relationship type");
            Assert.AreEqual(albumId, relationships2[0].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ID), "Track -> album relationship ID");
            Assert.AreEqual(trackRelationship, relationships2[1].GetAttributeValue(RelationshipAspect.ATTR_ROLE), "Track -> album item type");
            Assert.AreEqual(artistRelationship, relationships2[1].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ROLE), "Track -> album relationship type");
            Assert.AreEqual(artistId, relationships2[1].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ID), "Track -> album relationship ID");

            // Read the track a second time (
            //Console.WriteLine("Reader state track3, {0} {1}", reader.NodeType, reader.Name);
            MediaItem track3 = MediaItem.Deserialize(reader);

            IList <MultipleMediaItemAspect> resourceAspect3;

            Assert.IsTrue(MediaItemAspect.TryGetAspects(track3.Aspects, ProviderResourceAspect.Metadata, out resourceAspect3), "Resource aspects");
            Assert.AreEqual("c:\\file.mp3", resourceAspect3[0].GetAttributeValue <string>(ProviderResourceAspect.ATTR_RESOURCE_ACCESSOR_PATH), "Track location");
            IList <MediaItemAspect> relationships3 = track3[RelationshipAspect.ASPECT_ID];

            Assert.IsTrue(track3[RelationshipAspect.ASPECT_ID] != null, "Relationship aspects");
            Assert.AreEqual(2, relationships3.Count, "Track relationship count");
            Assert.AreEqual(trackRelationship, relationships3[0].GetAttributeValue(RelationshipAspect.ATTR_ROLE), "Track -> album item type");
            Assert.AreEqual(albumRelationship, relationships3[0].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ROLE), "Track -> album relationship type");
            Assert.AreEqual(albumId, relationships3[0].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ID), "Track -> album relationship ID");
            Assert.AreEqual(trackRelationship, relationships3[1].GetAttributeValue(RelationshipAspect.ATTR_ROLE), "Track -> album item type");
            Assert.AreEqual(artistRelationship, relationships3[1].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ROLE), "Track -> album relationship type");
            Assert.AreEqual(artistId, relationships3[1].GetAttributeValue(RelationshipAspect.ATTR_LINKED_ID), "Track -> album relationship ID");

            reader.Read(); // Test
        }