public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects)
        {
            AlbumInfo albumInfo = new AlbumInfo();

            if (!albumInfo.FromMetadata(aspects))
            {
                return(false);
            }

            AudioMetadataExtractor.TryUpdateAlbum(mediaItemAccessor, albumInfo);
            if (AudioMetadataExtractor.IncludeArtistDetails && !AudioMetadataExtractor.SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.UpdateAlbumPersonsAsync(albumInfo, PersonAspect.OCCUPATION_ARTIST).ConfigureAwait(false);
            }

            foreach (PersonInfo person in albumInfo.Artists)
            {
                IDictionary <Guid, IList <MediaItemAspect> > personAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                if (person.SetMetadata(personAspects) && personAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID))
                {
                    extractedLinkedAspects.Add(personAspects);
                }
            }
            return(extractedLinkedAspects.Count > 0);
        }
Ejemplo n.º 2
0
        public MusicAlbumInfo(MediaItem mediaItem)
        {
            try
            {
                AlbumInfo album = new AlbumInfo();
                album.FromMetadata(mediaItem.Aspects);

                ItemId      = mediaItem.MediaItemId;
                Summery     = album.Description.Text;
                Artist      = String.Join(", ", album.Artists);
                Genre       = String.Join(", ", album.Genres.Select(g => g.Name));
                Disc        = album.DiscNum;
                DiscTotal   = album.TotalDiscs;
                Rating      = Convert.ToString(album.Rating.RatingValue ?? 0);
                RatingCount = Convert.ToString(album.Rating.VoteCount ?? 0);
                Title       = album.Album;
                TrackTotal  = album.TotalTracks;
                Year        = album.ReleaseDate.Value.Year;
                ImageName   = Helper.GetImageBaseURL(mediaItem, FanArtMediaTypes.Album, FanArtTypes.Cover);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Error("WifiRemote: Error getting album info", e);
            }
        }
        public void CacheExtractedItem(Guid extractedItemId, IDictionary <Guid, IList <MediaItemAspect> > extractedAspects)
        {
            AlbumInfo album = new AlbumInfo();

            album.FromMetadata(extractedAspects);
            AddToCache(extractedItemId, album, false);
        }
Ejemplo n.º 4
0
        public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects)
        {
            TrackInfo trackInfo = new TrackInfo();

            if (!trackInfo.FromMetadata(aspects))
            {
                return(false);
            }

            AlbumInfo reimport = null;

            if (aspects.ContainsKey(ReimportAspect.ASPECT_ID))
            {
                reimport = trackInfo.CloneBasicInstance <AlbumInfo>();
            }

            IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData = extractedLinkedAspects.Count > 0 ?
                                                                               extractedLinkedAspects[0] : new Dictionary <Guid, IList <MediaItemAspect> >();

            if (!await TryExtractAlbumMetadataAsync(mediaItemAccessor, extractedAspectData, reimport).ConfigureAwait(false))
            {
                return(false);
            }

            AlbumInfo albumInfo = new AlbumInfo();

            if (!albumInfo.FromMetadata(extractedAspectData))
            {
                return(false);
            }

            IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();

            foreach (var genre in albumInfo.Genres)
            {
                if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId))
                {
                    genre.Id = genreId;
                }
            }
            albumInfo.SetMetadata(extractedAspectData);

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

            bool trackVirtual;

            if (MediaItemAspect.TryGetAttribute(aspects, MediaAspect.ATTR_ISVIRTUAL, false, out trackVirtual))
            {
                MediaItemAspect.SetAttribute(extractedAspectData, MediaAspect.ATTR_ISVIRTUAL, trackVirtual);
            }

            extractedLinkedAspects.Clear();
            extractedLinkedAspects.Add(extractedAspectData);
            return(true);
        }
Ejemplo n.º 5
0
        public override void Update(MediaItem mediaItem)
        {
            base.Update(mediaItem);
            if (mediaItem == null)
            {
                return;
            }

            AlbumInfo album = new AlbumInfo();

            if (!album.FromMetadata(mediaItem.Aspects))
            {
                return;
            }

            Album       = album.Album ?? "";
            Description = album.Description.Text ?? "";

            int?count;

            if (mediaItem.Aspects.ContainsKey(AudioAlbumAspect.ASPECT_ID))
            {
                if (MediaItemAspect.TryGetAttribute(mediaItem.Aspects, AudioAlbumAspect.ATTR_AVAILABLE_TRACKS, out count))
                {
                    AvailableTracks = count.Value.ToString();
                }
                else
                {
                    AvailableTracks = "";
                }

                if (MediaItemAspect.TryGetAttribute(mediaItem.Aspects, AudioAlbumAspect.ATTR_NUMTRACKS, out count))
                {
                    TotalTracks = count.Value.ToString();
                }
                else
                {
                    TotalTracks = "";
                }

                if (VirtualMediaHelper.ShowVirtualAudioMedia)
                {
                    Tracks = string.IsNullOrEmpty(TotalTracks) ? AvailableTracks : TotalTracks;
                }
                else
                {
                    Tracks = AvailableTracks;
                }
            }

            FireChange();
        }
        public bool TryMatch(IDictionary <Guid, IList <MediaItemAspect> > extractedAspects, IDictionary <Guid, IList <MediaItemAspect> > existingAspects)
        {
            if (existingAspects.ContainsKey(AudioAlbumAspect.ASPECT_ID))
            {
                AlbumInfo extracted = new AlbumInfo();
                extracted.FromMetadata(extractedAspects);
                AlbumInfo existing = new AlbumInfo();
                existing.FromMetadata(existingAspects);

                return(existing.Equals(extracted));
            }
            return(false);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Verifies if the album being reimported matches the album in the nfo file
        /// </summary>
        /// <param name="reader">Reader used read the album information from the nfo file</param>
        /// <param name="reimport">The album being reimported</param>
        /// <returns>Result of the verification</returns>
        protected bool VerifyAlbumReimport(NfoAlbumReader reader, AlbumInfo reimport)
        {
            if (reimport == null)
            {
                return(true);
            }

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

            if (reader.TryWriteMetadata(aspectData))
            {
                AlbumInfo info = new AlbumInfo();
                info.FromMetadata(aspectData);
                if (reimport.Equals(info))
                {
                    return(true);
                }
            }
            return(false);
        }
Ejemplo n.º 8
0
        public IDictionary <Guid, IList <MediaItemAspect> > GetBaseChildAspectsFromExistingAspects(IDictionary <Guid, IList <MediaItemAspect> > existingChildAspects, IDictionary <Guid, IList <MediaItemAspect> > existingParentAspects)
        {
            if (existingParentAspects.ContainsKey(AudioAlbumAspect.ASPECT_ID))
            {
                AlbumInfo album = new AlbumInfo();
                album.FromMetadata(existingParentAspects);

                if (existingChildAspects.ContainsKey(AudioAspect.ASPECT_ID))
                {
                    TrackInfo track = new TrackInfo();
                    track.FromMetadata(existingChildAspects);

                    TrackInfo basicTrack = album.CloneBasicInstance <TrackInfo>();
                    basicTrack.TrackNum = track.TrackNum;
                    IDictionary <Guid, IList <MediaItemAspect> > aspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                    basicTrack.SetMetadata(aspects, true);
                    return(aspects);
                }
            }
            return(null);
        }
        public async Task <bool> TryExtractRelationshipsAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > aspects, IList <IDictionary <Guid, IList <MediaItemAspect> > > extractedLinkedAspects)
        {
            AlbumInfo albumInfo = new AlbumInfo();

            if (!albumInfo.FromMetadata(aspects))
            {
                return(false);
            }

            if (AudioMetadataExtractor.IncludeMusicLabelDetails && !AudioMetadataExtractor.SkipOnlineSearches)
            {
                await OnlineMatcherService.Instance.UpdateAlbumCompaniesAsync(albumInfo, CompanyAspect.COMPANY_MUSIC_LABEL).ConfigureAwait(false);
            }

            foreach (CompanyInfo company in albumInfo.MusicLabels)
            {
                IDictionary <Guid, IList <MediaItemAspect> > companyAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                if (company.SetMetadata(companyAspects) && companyAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID))
                {
                    extractedLinkedAspects.Add(companyAspects);
                }
            }
            return(extractedLinkedAspects.Count > 0);
        }
Ejemplo n.º 10
0
        public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects)
        {
            extractedLinkedAspects = null;

            if (!AudioMetadataExtractor.IncludeArtistDetails)
            {
                return(false);
            }

            AlbumInfo albumInfo = new AlbumInfo();

            if (!albumInfo.FromMetadata(aspects))
            {
                return(false);
            }

            if (CheckCacheContains(albumInfo))
            {
                return(false);
            }

            UpdatePersons(aspects, albumInfo.Artists, true);

            int count = 0;

            if (!AudioMetadataExtractor.SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.UpdateAlbumPersons(albumInfo, PersonAspect.OCCUPATION_ARTIST, importOnly);
                count = albumInfo.Artists.Where(p => p.HasExternalId).Count();
                if (!albumInfo.IsRefreshed)
                {
                    albumInfo.HasChanged = true; //Force save to update external Ids for metadata found by other MDEs
                }
            }
            else
            {
                count = albumInfo.Artists.Where(p => !string.IsNullOrEmpty(p.Name)).Count();
            }

            if (albumInfo.Artists.Count == 0)
            {
                return(false);
            }

            if (BaseInfo.CountRelationships(aspects, LinkedRole) < count || (BaseInfo.CountRelationships(aspects, LinkedRole) == 0 && albumInfo.Artists.Count > 0))
            {
                albumInfo.HasChanged = true; //Force save if no relationship exists
            }
            if (!albumInfo.HasChanged && !importOnly)
            {
                return(false);
            }

            AddToCheckCache(albumInfo);

            extractedLinkedAspects = new List <RelationshipItem>();
            foreach (PersonInfo person in albumInfo.Artists)
            {
                person.AssignNameId();
                person.HasChanged = albumInfo.HasChanged;
                IDictionary <Guid, IList <MediaItemAspect> > personAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                person.SetMetadata(personAspects);

                if (personAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID))
                {
                    Guid existingId;
                    if (TryGetIdFromCache(person, out existingId))
                    {
                        extractedLinkedAspects.Add(new RelationshipItem(personAspects, existingId));
                    }
                    else
                    {
                        extractedLinkedAspects.Add(new RelationshipItem(personAspects, Guid.Empty));
                    }
                }
            }
            return(extractedLinkedAspects.Count > 0);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Asynchronously tries to extract metadata for the given <param name="mediaItemAccessor"></param>
        /// </summary>
        /// <param name="mediaItemAccessor">Points to the resource for which we try to extract metadata</param>
        /// <param name="extractedAspectData">Dictionary of <see cref="MediaItemAspect"/>s with the extracted metadata</param>
        /// <param name="forceQuickMode">If <c>true</c>, nothing is downloaded from the internet</param>
        /// <returns><c>true</c> if metadata was found and stored into <param name="extractedAspectData"></param>, else <c>false</c></returns>
        private async Task <bool> TryExtractAudioMetadataAsync(IResourceAccessor mediaItemAccessor, IDictionary <Guid, IList <MediaItemAspect> > extractedAspectData, bool forceQuickMode)
        {
            // Get a unique number for this call to TryExtractMetadataAsync. We use this to make reading the debug log easier.
            // This MetadataExtractor is called in parallel for multiple MediaItems so that the respective debug log entries
            // for one call are not contained one after another in debug log. We therefore prepend this number before every log entry.
            var  miNumber = Interlocked.Increment(ref _lastMediaItemNumber);
            bool isStub   = extractedAspectData.ContainsKey(StubAspect.ASPECT_ID);

            if (!isStub)
            {
                _debugLogger.Info("[#{0}]: Ignoring non-stub track", miNumber);
                return(false);
            }
            try
            {
                _debugLogger.Info("[#{0}]: Start extracting metadata for resource '{1}' (forceQuickMode: {2})", miNumber, mediaItemAccessor, forceQuickMode);

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

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

                // First we try to find an IFileSystemResourceAccessor pointing to the album nfo-file.
                IFileSystemResourceAccessor albumNfoFsra;
                if (TryGetAlbumNfoSResourceAccessor(miNumber, mediaItemAccessor as IFileSystemResourceAccessor, out albumNfoFsra))
                {
                    // If we found one, we (asynchronously) extract the metadata into a stub object and, if metadata was found,
                    // we store it into the MediaItemAspects.
                    var albumNfoReader = new NfoAlbumReader(_debugLogger, miNumber, forceQuickMode, isStub, _httpClient, _settings);
                    using (albumNfoFsra)
                    {
                        if (await albumNfoReader.TryReadMetadataAsync(albumNfoFsra).ConfigureAwait(false))
                        {
                            //Check reimport
                            if (extractedAspectData.ContainsKey(ReimportAspect.ASPECT_ID))
                            {
                                AlbumInfo reimport = new AlbumInfo();
                                reimport.FromMetadata(extractedAspectData);
                                if (!VerifyAlbumReimport(albumNfoReader, reimport))
                                {
                                    ServiceRegistration.Get <ILogger>().Info("NfoMovieMetadataExtractor: Nfo album metadata from resource '{0}' ignored because it does not match reimport {1}", mediaItemAccessor, reimport);
                                    return(false);
                                }
                            }

                            Stubs.AlbumStub album = albumNfoReader.GetAlbumStubs().FirstOrDefault();
                            if (album != null)
                            {
                                int trackNo = 0;
                                if (album.Tracks != null && album.Tracks.Count > 0 && MediaItemAspect.TryGetAttribute(extractedAspectData, AudioAspect.ATTR_TRACK, out trackNo))
                                {
                                    var track = album.Tracks.FirstOrDefault(t => t.TrackNumber.HasValue && trackNo == t.TrackNumber.Value);
                                    if (track != null)
                                    {
                                        TrackInfo trackInfo = new TrackInfo();
                                        string    title;
                                        string    sortTitle;

                                        title     = track.Title.Trim();
                                        sortTitle = BaseInfo.GetSortTitle(title);

                                        IEnumerable <string> artists;
                                        if (track.Artists.Count > 0)
                                        {
                                            artists = track.Artists;
                                        }

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

                                        trackInfo.TrackName               = title;
                                        trackInfo.TrackNameSort           = sortTitle;
                                        trackInfo.Duration                = track.Duration.HasValue ? Convert.ToInt64(track.Duration.Value.TotalSeconds) : 0;
                                        trackInfo.Album                   = !string.IsNullOrEmpty(album.Title) ? album.Title.Trim() : null;
                                        trackInfo.TrackNum                = track.TrackNumber.HasValue ? track.TrackNumber.Value : 0;
                                        trackInfo.TotalTracks             = album.Tracks.Count;
                                        trackInfo.MusicBrainzId           = track.MusicBrainzId;
                                        trackInfo.IsrcId                  = track.Isrc;
                                        trackInfo.AudioDbId               = track.AudioDbId.HasValue ? track.AudioDbId.Value : 0;
                                        trackInfo.AlbumMusicBrainzId      = album.MusicBrainzAlbumId;
                                        trackInfo.AlbumMusicBrainzGroupId = album.MusicBrainzReleaseGroupId;
                                        trackInfo.ReleaseDate             = album.ReleaseDate;
                                        if (track.FileInfo != null && track.FileInfo.Count > 0 && track.FileInfo.First().AudioStreams != null && track.FileInfo.First().AudioStreams.Count > 0)
                                        {
                                            var audio = track.FileInfo.First().AudioStreams.First();
                                            trackInfo.Encoding = audio.Codec;
                                            trackInfo.BitRate  = audio.Bitrate != null?Convert.ToInt32(audio.Bitrate / 1000) : 0;

                                            trackInfo.Channels = audio.Channels != null ? audio.Channels.Value : 0;
                                        }
                                        trackInfo.Artists = new List <PersonInfo>();
                                        if (track.Artists != null && track.Artists.Count > 0)
                                        {
                                            foreach (string artistName in track.Artists)
                                            {
                                                trackInfo.Artists.Add(new PersonInfo()
                                                {
                                                    Name            = artistName.Trim(),
                                                    Occupation      = PersonAspect.OCCUPATION_ARTIST,
                                                    ParentMediaName = trackInfo.Album,
                                                    MediaName       = trackInfo.TrackName
                                                });
                                            }
                                        }
                                        trackInfo.AlbumArtists = new List <PersonInfo>();
                                        if (album.Artists != null && album.Artists.Count > 0)
                                        {
                                            foreach (string artistName in album.Artists)
                                            {
                                                trackInfo.AlbumArtists.Add(new PersonInfo()
                                                {
                                                    Name            = artistName.Trim(),
                                                    Occupation      = PersonAspect.OCCUPATION_ARTIST,
                                                    ParentMediaName = trackInfo.Album,
                                                    MediaName       = trackInfo.TrackName
                                                });
                                            }
                                        }
                                        if (album.Genres != null && album.Genres.Count > 0)
                                        {
                                            trackInfo.Genres = album.Genres.Where(s => !string.IsNullOrEmpty(s?.Trim())).Select(s => new GenreInfo {
                                                Name = s.Trim()
                                            }).ToList();
                                            IGenreConverter converter = ServiceRegistration.Get <IGenreConverter>();
                                            foreach (var genre in trackInfo.Genres)
                                            {
                                                if (!genre.Id.HasValue && converter.GetGenreId(genre.Name, GenreCategory.Music, null, out int genreId))
                                                {
                                                    genre.Id = genreId;
                                                }
                                            }
                                        }

                                        if (album.Thumb != null && album.Thumb.Length > 0)
                                        {
                                            try
                                            {
                                                using (MemoryStream stream = new MemoryStream(album.Thumb))
                                                {
                                                    trackInfo.Thumbnail  = stream.ToArray();
                                                    trackInfo.HasChanged = true;
                                                }
                                            }
                                            // Decoding of invalid image data can fail, but main MediaItem is correct.
                                            catch { }
                                        }

                                        //Determine compilation
                                        if (trackInfo.AlbumArtists.Count > 0 &&
                                            (trackInfo.AlbumArtists[0].Name.IndexOf("Various", StringComparison.InvariantCultureIgnoreCase) >= 0 ||
                                             trackInfo.AlbumArtists[0].Name.Equals("VA", StringComparison.InvariantCultureIgnoreCase)))
                                        {
                                            trackInfo.Compilation = true;
                                        }
                                        else
                                        {
                                            //Look for itunes compilation folder
                                            var mediaItemPath = mediaItemAccessor.CanonicalLocalResourcePath;
                                            var artistMediaItemDirectoryPath = ResourcePathHelper.Combine(mediaItemPath, "../../");
                                            if (artistMediaItemDirectoryPath.FileName.IndexOf("Compilation", StringComparison.InvariantCultureIgnoreCase) >= 0)
                                            {
                                                trackInfo.Compilation = true;
                                            }
                                        }
                                        trackInfo.SetMetadata(extractedAspectData);
                                    }
                                }
                            }
                        }
                        else
                        {
                            _debugLogger.Warn("[#{0}]: No valid metadata found in album nfo-file", miNumber);
                        }
                    }
                }

                _debugLogger.Info("[#{0}]: Successfully finished extracting metadata", miNumber);
                ServiceRegistration.Get <ILogger>().Debug("NfoAudioMetadataExtractor: Assigned nfo audio metadata for resource '{0}'", mediaItemAccessor);
                return(true);
            }
            catch (Exception e)
            {
                ServiceRegistration.Get <ILogger>().Warn("NfoAudioMetadataExtractor: Exception while extracting metadata for resource '{0}'; enable debug logging for more details.", mediaItemAccessor);
                _debugLogger.Error("[#{0}]: Exception while extracting metadata", e, miNumber);
                return(false);
            }
        }
        public bool TryExtractRelationships(IDictionary <Guid, IList <MediaItemAspect> > aspects, bool importOnly, out IList <RelationshipItem> extractedLinkedAspects)
        {
            extractedLinkedAspects = null;

            if (!AudioMetadataExtractor.IncludeMusicLabelDetails)
            {
                return(false);
            }

            if (importOnly)
            {
                return(false);
            }

            AlbumInfo albumInfo = new AlbumInfo();

            if (!albumInfo.FromMetadata(aspects))
            {
                return(false);
            }

            if (CheckCacheContains(albumInfo))
            {
                return(false);
            }

            int count = 0;

            if (!AudioMetadataExtractor.SkipOnlineSearches)
            {
                OnlineMatcherService.Instance.UpdateAlbumCompanies(albumInfo, CompanyAspect.COMPANY_MUSIC_LABEL, importOnly);
                count = albumInfo.MusicLabels.Where(c => c.HasExternalId).Count();
                if (!albumInfo.IsRefreshed)
                {
                    albumInfo.HasChanged = true; //Force save to update external Ids for metadata found by other MDEs
                }
            }
            else
            {
                count = albumInfo.MusicLabels.Where(c => !string.IsNullOrEmpty(c.Name)).Count();
            }

            if (albumInfo.MusicLabels.Count == 0)
            {
                return(false);
            }

            if (BaseInfo.CountRelationships(aspects, LinkedRole) < count || (BaseInfo.CountRelationships(aspects, LinkedRole) == 0 && albumInfo.MusicLabels.Count > 0))
            {
                albumInfo.HasChanged = true; //Force save if no relationship exists
            }
            if (!albumInfo.HasChanged)
            {
                return(false);
            }

            AddToCheckCache(albumInfo);

            extractedLinkedAspects = new List <RelationshipItem>();
            foreach (CompanyInfo company in albumInfo.MusicLabels)
            {
                company.AssignNameId();
                company.HasChanged = albumInfo.HasChanged;
                IDictionary <Guid, IList <MediaItemAspect> > companyAspects = new Dictionary <Guid, IList <MediaItemAspect> >();
                company.SetMetadata(companyAspects);

                if (companyAspects.ContainsKey(ExternalIdentifierAspect.ASPECT_ID))
                {
                    Guid existingId;
                    if (TryGetIdFromCache(company, out existingId))
                    {
                        extractedLinkedAspects.Add(new RelationshipItem(companyAspects, existingId));
                    }
                    else
                    {
                        extractedLinkedAspects.Add(new RelationshipItem(companyAspects, Guid.Empty));
                    }
                }
            }
            return(extractedLinkedAspects.Count > 0);
        }