コード例 #1
0
        public List <CandidateAlbumRelease> GetCandidatesFromFingerprint(LocalAlbumRelease localAlbumRelease, Artist artist, Album album, AlbumRelease release, bool includeExisting)
        {
            var recordingIds = localAlbumRelease.LocalTracks.Where(x => x.AcoustIdResults != null).SelectMany(x => x.AcoustIdResults).ToList();
            var allReleases  = _releaseService.GetReleasesByRecordingIds(recordingIds);

            // make sure releases are consistent with those selected by the user
            if (release != null)
            {
                allReleases = allReleases.Where(x => x.Id == release.Id).ToList();
            }
            else if (album != null)
            {
                allReleases = allReleases.Where(x => x.AlbumId == album.Id).ToList();
            }
            else if (artist != null)
            {
                allReleases = allReleases.Where(x => x.Album.Value.ArtistMetadataId == artist.ArtistMetadataId).ToList();
            }

            return(GetCandidatesByRelease(allReleases.Select(x => new {
                Release = x,
                TrackCount = x.TrackCount,
                CommonProportion = x.Tracks.Value.Select(y => y.ForeignRecordingId).Intersect(recordingIds).Count() / localAlbumRelease.TrackCount
            })
                                          .Where(x => x.CommonProportion > 0.6)
                                          .ToList()
                                          .OrderBy(x => Math.Abs(x.TrackCount - localAlbumRelease.TrackCount))
                                          .ThenByDescending(x => x.CommonProportion)
                                          .Select(x => x.Release)
                                          .Take(10)
                                          .ToList(), includeExisting));
        }
コード例 #2
0
        private List <CandidateAlbumRelease> GetCandidates(LocalAlbumRelease localAlbumRelease, bool includeExisting)
        {
            // most general version, nothing has been specified.
            // get all plausible artists, then all plausible albums, then get releases for each of these.

            // check if it looks like VA.
            if (TrackGroupingService.IsVariousArtists(localAlbumRelease.LocalTracks))
            {
                throw new NotImplementedException("Various artists not supported");
            }

            var candidateReleases = new List <CandidateAlbumRelease>();

            var artistTag = MostCommon(localAlbumRelease.LocalTracks.Select(x => x.FileTrackInfo.ArtistTitle)) ?? "";

            if (artistTag.IsNotNullOrWhiteSpace())
            {
                var possibleArtists = _artistService.GetCandidates(artistTag);
                foreach (var artist in possibleArtists)
                {
                    candidateReleases.AddRange(GetCandidatesByArtist(localAlbumRelease, artist, includeExisting));
                }
            }

            return(candidateReleases);
        }
コード例 #3
0
        private List <CandidateAlbumRelease> GetDbCandidates(LocalAlbumRelease localAlbumRelease, bool includeExisting)
        {
            // most general version, nothing has been specified.
            // get all plausible artists, then all plausible albums, then get releases for each of these.
            var candidateReleases = new List <CandidateAlbumRelease>();

            // check if it looks like VA.
            if (TrackGroupingService.IsVariousArtists(localAlbumRelease.LocalTracks))
            {
                var va = _artistService.FindById(DistanceCalculator.VariousArtistIds[0]);
                if (va != null)
                {
                    candidateReleases.AddRange(GetDbCandidatesByArtist(localAlbumRelease, va, includeExisting));
                }
            }

            var artistTag = localAlbumRelease.LocalTracks.MostCommon(x => x.FileTrackInfo.ArtistTitle) ?? "";

            if (artistTag.IsNotNullOrWhiteSpace())
            {
                var possibleArtists = _artistService.GetCandidates(artistTag);
                foreach (var artist in possibleArtists)
                {
                    candidateReleases.AddRange(GetDbCandidatesByArtist(localAlbumRelease, artist, includeExisting));
                }
            }

            return(candidateReleases);
        }
コード例 #4
0
        private ImportDecision <LocalAlbumRelease> GetDecision(LocalAlbumRelease localAlbumRelease, DownloadClientItem downloadClientItem)
        {
            ImportDecision <LocalAlbumRelease> decision = null;

            if (localAlbumRelease.AlbumRelease == null)
            {
                decision = new ImportDecision <LocalAlbumRelease>(localAlbumRelease, new Rejection($"Couldn't find similar album for {localAlbumRelease}"));
            }
            else
            {
                var reasons = _albumSpecifications.Select(c => EvaluateSpec(c, localAlbumRelease, downloadClientItem))
                              .Where(c => c != null);

                decision = new ImportDecision <LocalAlbumRelease>(localAlbumRelease, reasons.ToArray());
            }

            if (decision == null)
            {
                _logger.Error("Unable to make a decision on {0}", localAlbumRelease);
            }
            else if (decision.Rejections.Any())
            {
                _logger.Debug("Album rejected for the following reasons: {0}", string.Join(", ", decision.Rejections));
            }
            else
            {
                _logger.Debug("Album accepted");
            }

            return(decision);
        }
コード例 #5
0
 private List <CandidateAlbumRelease> GetCandidatesByAlbum(LocalAlbumRelease localAlbumRelease, Album album, bool includeExisting)
 {
     // sort candidate releases by closest track count so that we stand a chance of
     // getting a perfect match early on
     return(GetCandidatesByRelease(_releaseService.GetReleasesByAlbum(album.Id)
                                   .OrderBy(x => Math.Abs(localAlbumRelease.TrackCount - x.TrackCount))
                                   .ToList(), includeExisting));
 }
コード例 #6
0
        private void EnsureData(LocalAlbumRelease release)
        {
            if (release.AlbumRelease != null && release.AlbumRelease.Album.Value.Artist.Value.QualityProfileId == 0)
            {
                var rootFolder     = _rootFolderService.GetBestRootFolder(release.LocalTracks.First().Path);
                var qualityProfile = _qualityProfileService.Get(rootFolder.DefaultQualityProfileId);

                var artist = release.AlbumRelease.Album.Value.Artist.Value;
                artist.QualityProfileId = qualityProfile.Id;
                artist.QualityProfile   = qualityProfile;
            }
        }
コード例 #7
0
        public void get_candidates_should_only_return_specified_release_if_set()
        {
            var tracks            = GivenTracks(3);
            var release           = GivenAlbumRelease("album", tracks);
            var localTracks       = GivenLocalTracks(tracks, release);
            var localAlbumRelease = new LocalAlbumRelease(localTracks);

            Subject.GetCandidatesFromTags(localAlbumRelease, null, null, release, false).ShouldBeEquivalentTo(
                new List <CandidateAlbumRelease> {
                new CandidateAlbumRelease(release)
            }
                );
        }
コード例 #8
0
        public void get_candidates_should_only_return_specified_release_if_set()
        {
            var tracks            = GivenTracks(3);
            var release           = GivenAlbumRelease("album", tracks);
            var localTracks       = GivenLocalTracks(tracks, release);
            var localAlbumRelease = new LocalAlbumRelease(localTracks);
            var idOverrides       = new IdentificationOverrides
            {
                AlbumRelease = release
            };

            Subject.GetDbCandidatesFromTags(localAlbumRelease, idOverrides, false).Should().BeEquivalentTo(
                new List <CandidateAlbumRelease> {
                new CandidateAlbumRelease(release)
            });
        }
コード例 #9
0
ファイル: AggregationService.cs プロジェクト: zobe123/Lidarr
        public LocalAlbumRelease Augment(LocalAlbumRelease localAlbum)
        {
            foreach (var augmenter in _albumAugmenters)
            {
                try
                {
                    augmenter.Aggregate(localAlbum, false);
                }
                catch (Exception ex)
                {
                    _logger.Warn(ex, ex.Message);
                }
            }

            return(localAlbum);
        }
コード例 #10
0
        private void GetBestRelease(LocalAlbumRelease localAlbumRelease, List <CandidateAlbumRelease> candidateReleases, List <LocalTrack> extraTracksOnDisk)
        {
            var watch = System.Diagnostics.Stopwatch.StartNew();

            _logger.Debug("Matching {0} track files against {1} candidates", localAlbumRelease.TrackCount, candidateReleases.Count);
            _logger.Trace("Processing files:\n{0}", string.Join("\n", localAlbumRelease.LocalTracks.Select(x => x.Path)));

            double bestDistance = 1.0;

            foreach (var candidateRelease in candidateReleases)
            {
                var release = candidateRelease.AlbumRelease;
                _logger.Debug("Trying Release {0} [{1}, {2} tracks, {3} existing]", release, release.Title, release.TrackCount, candidateRelease.ExistingTracks.Count);
                var rwatch = System.Diagnostics.Stopwatch.StartNew();

                var extraTrackPaths = candidateRelease.ExistingTracks.Select(x => x.Path).ToList();
                var extraTracks     = extraTracksOnDisk.Where(x => extraTrackPaths.Contains(x.Path)).ToList();
                var allLocalTracks  = localAlbumRelease.LocalTracks.Concat(extraTracks).DistinctBy(x => x.Path).ToList();

                var mapping      = MapReleaseTracks(allLocalTracks, release.Tracks.Value);
                var distance     = DistanceCalculator.AlbumReleaseDistance(allLocalTracks, release, mapping);
                var currDistance = distance.NormalizedDistance();

                rwatch.Stop();
                _logger.Debug("Release {0} [{1} tracks] has distance {2} vs best distance {3} [{4}ms]",
                              release,
                              release.TrackCount,
                              currDistance,
                              bestDistance,
                              rwatch.ElapsedMilliseconds);
                if (currDistance < bestDistance)
                {
                    bestDistance = currDistance;
                    localAlbumRelease.Distance       = distance;
                    localAlbumRelease.AlbumRelease   = release;
                    localAlbumRelease.ExistingTracks = extraTracks;
                    localAlbumRelease.TrackMapping   = mapping;
                    if (currDistance == 0.0)
                    {
                        break;
                    }
                }
            }

            watch.Stop();
            _logger.Debug($"Best release: {localAlbumRelease.AlbumRelease} Distance {localAlbumRelease.Distance.NormalizedDistance()} found in {watch.ElapsedMilliseconds}ms");
        }
コード例 #11
0
        private bool ShouldFingerprint(LocalAlbumRelease localAlbumRelease)
        {
            var worstTrackMatchDist = localAlbumRelease.TrackMapping?.Mapping
                                      .OrderByDescending(x => x.Value.Item2.NormalizedDistance())
                                      .First()
                                      .Value.Item2.NormalizedDistance() ?? 1.0;

            if (localAlbumRelease.Distance.NormalizedDistance() > 0.15 ||
                localAlbumRelease.TrackMapping.LocalExtra.Any() ||
                localAlbumRelease.TrackMapping.MBExtra.Any() ||
                worstTrackMatchDist > 0.40)
            {
                return(true);
            }

            return(false);
        }
コード例 #12
0
        public void get_candidates_should_use_consensus_release_id()
        {
            var tracks  = GivenTracks(3);
            var release = GivenAlbumRelease("album", tracks);

            release.ForeignReleaseId = "xxx";
            var localTracks       = GivenLocalTracks(tracks, release);
            var localAlbumRelease = new LocalAlbumRelease(localTracks);

            Mocker.GetMock <IReleaseService>()
            .Setup(x => x.GetReleaseByForeignReleaseId("xxx", true))
            .Returns(release);

            Subject.GetDbCandidatesFromTags(localAlbumRelease, null, false).Should().BeEquivalentTo(
                new List <CandidateAlbumRelease> {
                new CandidateAlbumRelease(release)
            });
        }
コード例 #13
0
        private List <CandidateAlbumRelease> GetCandidatesByArtist(LocalAlbumRelease localAlbumRelease, Artist artist, bool includeExisting)
        {
            _logger.Trace("Getting candidates for {0}", artist);
            var candidateReleases = new List <CandidateAlbumRelease>();

            var albumTag = MostCommon(localAlbumRelease.LocalTracks.Select(x => x.FileTrackInfo.AlbumTitle)) ?? "";

            if (albumTag.IsNotNullOrWhiteSpace())
            {
                var possibleAlbums = _albumService.GetCandidates(artist.ArtistMetadataId, albumTag);
                foreach (var album in possibleAlbums)
                {
                    candidateReleases.AddRange(GetCandidatesByAlbum(localAlbumRelease, album, includeExisting));
                }
            }

            return(candidateReleases);
        }
コード例 #14
0
        private List <LocalTrack> ToLocalTrack(IEnumerable <TrackFile> trackfiles, LocalAlbumRelease localRelease)
        {
            var scanned     = trackfiles.Join(localRelease.LocalTracks, t => t.Path, l => l.Path, (track, localTrack) => localTrack);
            var toScan      = trackfiles.ExceptBy(t => t.Path, scanned, s => s.Path, StringComparer.InvariantCulture);
            var localTracks = scanned.Concat(toScan.Select(x => new LocalTrack {
                Path           = x.Path,
                Size           = x.Size,
                Modified       = x.Modified,
                FileTrackInfo  = _audioTagService.ReadTags(x.Path),
                ExistingFile   = true,
                AdditionalFile = true,
                Quality        = x.Quality
            }))
                              .ToList();

            localTracks.ForEach(x => _augmentingService.Augment(x, true));

            return(localTracks);
        }
コード例 #15
0
        public List <CandidateAlbumRelease> GetRemoteCandidates(LocalAlbumRelease localAlbumRelease)
        {
            // Gets candidate album releases from the metadata server.
            // Will eventually need adding locally if we find a match
            var watch = System.Diagnostics.Stopwatch.StartNew();

            List <Album> remoteAlbums;
            var          candidates = new List <CandidateAlbumRelease>();

            var albumIds     = localAlbumRelease.LocalTracks.Select(x => x.FileTrackInfo.AlbumMBId).Distinct().ToList();
            var recordingIds = localAlbumRelease.LocalTracks.Where(x => x.AcoustIdResults != null).SelectMany(x => x.AcoustIdResults).Distinct().ToList();

            try
            {
                if (albumIds.Count == 1 && albumIds[0].IsNotNullOrWhiteSpace())
                {
                    // Use mbids in tags if set
                    remoteAlbums = _albumSearchService.SearchForNewAlbum($"mbid:{albumIds[0]}", null);
                }
                else if (recordingIds.Any())
                {
                    // If fingerprints present use those
                    remoteAlbums = _albumSearchService.SearchForNewAlbumByRecordingIds(recordingIds);
                }
                else
                {
                    // fall back to artist / album name search
                    string artistTag;

                    if (TrackGroupingService.IsVariousArtists(localAlbumRelease.LocalTracks))
                    {
                        artistTag = "Various Artists";
                    }
                    else
                    {
                        artistTag = localAlbumRelease.LocalTracks.MostCommon(x => x.FileTrackInfo.ArtistTitle) ?? "";
                    }

                    var albumTag = localAlbumRelease.LocalTracks.MostCommon(x => x.FileTrackInfo.AlbumTitle) ?? "";

                    if (artistTag.IsNullOrWhiteSpace() || albumTag.IsNullOrWhiteSpace())
                    {
                        return(candidates);
                    }

                    remoteAlbums = _albumSearchService.SearchForNewAlbum(albumTag, artistTag);
                }
            }
            catch (SkyHookException e)
            {
                _logger.Info(e, "Skipping album due to SkyHook error");
                remoteAlbums = new List <Album>();
            }

            foreach (var album in remoteAlbums)
            {
                // We have to make sure various bits and pieces are populated that are normally handled
                // by a database lazy load
                foreach (var release in album.AlbumReleases.Value)
                {
                    release.Album = album;
                    candidates.Add(new CandidateAlbumRelease
                    {
                        AlbumRelease   = release,
                        ExistingTracks = new List <TrackFile>()
                    });
                }
            }

            watch.Stop();
            _logger.Debug($"Getting {candidates.Count} remote candidates from tags for {localAlbumRelease.LocalTracks.Count} tracks took {watch.ElapsedMilliseconds}ms");

            return(candidates);
        }
コード例 #16
0
        public void Setup()
        {
            _albumpass1 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >();
            _albumpass2 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >();
            _albumpass3 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >();

            _albumfail1 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >();
            _albumfail2 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >();
            _albumfail3 = new Mock <IImportDecisionEngineSpecification <LocalAlbumRelease> >();


            _pass1 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >();
            _pass2 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >();
            _pass3 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >();

            _fail1 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >();
            _fail2 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >();
            _fail3 = new Mock <IImportDecisionEngineSpecification <LocalTrack> >();

            _albumpass1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Accept());
            _albumpass2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Accept());
            _albumpass3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Accept());

            _albumfail1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Reject("_albumfail1"));
            _albumfail2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Reject("_albumfail2"));
            _albumfail3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalAlbumRelease>())).Returns(Decision.Reject("_albumfail3"));

            _pass1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Accept());
            _pass2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Accept());
            _pass3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Accept());

            _fail1.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Reject("_fail1"));
            _fail2.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Reject("_fail2"));
            _fail3.Setup(c => c.IsSatisfiedBy(It.IsAny <LocalTrack>())).Returns(Decision.Reject("_fail3"));

            _artist = Builder <Artist> .CreateNew()
                      .With(e => e.QualityProfile = new QualityProfile {
                Items = Qualities.QualityFixture.GetDefaultQualities()
            })
                      .Build();

            _albumRelease = Builder <AlbumRelease> .CreateNew()
                            .Build();

            _quality = new QualityModel(Quality.MP3_256);

            _localTrack = new LocalTrack
            {
                Artist  = _artist,
                Quality = _quality,
                Tracks  = new List <Track> {
                    new Track()
                },
                Path = @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi".AsOsAgnostic()
            };

            GivenAudioFiles(new List <string> {
                @"C:\Test\Unsorted\The.Office.S03E115.DVDRip.XviD-OSiTV.avi".AsOsAgnostic()
            });

            Mocker.GetMock <IIdentificationService>()
            .Setup(s => s.Identify(It.IsAny <List <LocalTrack> >(), It.IsAny <Artist>(), It.IsAny <Album>(), It.IsAny <AlbumRelease>(), It.IsAny <bool>(), It.IsAny <bool>(), It.IsAny <bool>()))
            .Returns((List <LocalTrack> tracks, Artist artist, Album album, AlbumRelease release, bool newDownload, bool singleRelease, bool includeExisting) => {
                var ret          = new LocalAlbumRelease(tracks);
                ret.AlbumRelease = _albumRelease;
                return(new List <LocalAlbumRelease> {
                    ret
                });
            });

            Mocker.GetMock <IMediaFileService>()
            .Setup(c => c.FilterUnchangedFiles(It.IsAny <List <IFileInfo> >(), It.IsAny <Artist>(), It.IsAny <FilterFilesType>()))
            .Returns((List <IFileInfo> files, Artist artist, FilterFilesType filter) => files);

            GivenSpecifications(_albumpass1);
        }
コード例 #17
0
        private void IdentifyRelease(LocalAlbumRelease localAlbumRelease, IdentificationOverrides idOverrides, ImportDecisionMakerConfig config)
        {
            var  watch         = System.Diagnostics.Stopwatch.StartNew();
            bool fingerprinted = false;

            var candidateReleases = _candidateService.GetDbCandidatesFromTags(localAlbumRelease, idOverrides, config.IncludeExisting);

            if (candidateReleases.Count == 0 && config.AddNewArtists)
            {
                candidateReleases = _candidateService.GetRemoteCandidates(localAlbumRelease);
            }

            if (candidateReleases.Count == 0 && FingerprintingAllowed(config.NewDownload))
            {
                _logger.Debug("No candidates found, fingerprinting");
                _fingerprintingService.Lookup(localAlbumRelease.LocalTracks, 0.5);
                fingerprinted     = true;
                candidateReleases = _candidateService.GetDbCandidatesFromFingerprint(localAlbumRelease, idOverrides, config.IncludeExisting);

                if (candidateReleases.Count == 0 && config.AddNewArtists)
                {
                    // Now fingerprints are populated this will return a different answer
                    candidateReleases = _candidateService.GetRemoteCandidates(localAlbumRelease);
                }
            }

            if (candidateReleases.Count == 0)
            {
                // can't find any candidates even after fingerprinting
                // populate the overrides and return
                foreach (var localTrack in localAlbumRelease.LocalTracks)
                {
                    localTrack.Release = idOverrides.AlbumRelease;
                    localTrack.Album   = idOverrides.Album;
                    localTrack.Artist  = idOverrides.Artist;
                }

                return;
            }

            _logger.Debug($"Got {candidateReleases.Count} candidates for {localAlbumRelease.LocalTracks.Count} tracks in {watch.ElapsedMilliseconds}ms");

            PopulateTracks(candidateReleases);

            // convert all the TrackFiles that represent extra files to List<LocalTrack>
            var allLocalTracks = ToLocalTrack(candidateReleases
                                              .SelectMany(x => x.ExistingTracks)
                                              .DistinctBy(x => x.Path), localAlbumRelease);

            _logger.Debug($"Retrieved {allLocalTracks.Count} possible tracks in {watch.ElapsedMilliseconds}ms");

            GetBestRelease(localAlbumRelease, candidateReleases, allLocalTracks);

            // If result isn't great and we haven't fingerprinted, try that
            // Note that this can improve the match even if we try the same candidates
            if (!fingerprinted && FingerprintingAllowed(config.NewDownload) && ShouldFingerprint(localAlbumRelease))
            {
                _logger.Debug($"Match not good enough, fingerprinting");
                _fingerprintingService.Lookup(localAlbumRelease.LocalTracks, 0.5);

                // Only include extra possible candidates if neither album nor release are specified
                // Will generally be specified as part of manual import
                if (idOverrides?.Album == null && idOverrides?.AlbumRelease == null)
                {
                    var dbCandidates     = _candidateService.GetDbCandidatesFromFingerprint(localAlbumRelease, idOverrides, config.IncludeExisting);
                    var remoteCandidates = config.AddNewArtists ? _candidateService.GetRemoteCandidates(localAlbumRelease) : new List <CandidateAlbumRelease>();
                    var extraCandidates  = dbCandidates.Concat(remoteCandidates);
                    var newCandidates    = extraCandidates.ExceptBy(x => x.AlbumRelease.Id, candidateReleases, y => y.AlbumRelease.Id, EqualityComparer <int> .Default);
                    candidateReleases.AddRange(newCandidates);

                    PopulateTracks(candidateReleases);

                    allLocalTracks.AddRange(ToLocalTrack(newCandidates
                                                         .SelectMany(x => x.ExistingTracks)
                                                         .DistinctBy(x => x.Path)
                                                         .ExceptBy(x => x.Path, allLocalTracks, x => x.Path, PathEqualityComparer.Instance),
                                                         localAlbumRelease));
                }

                // fingerprint all the local files in candidates we might be matching against
                _fingerprintingService.Lookup(allLocalTracks, 0.5);

                GetBestRelease(localAlbumRelease, candidateReleases, allLocalTracks);
            }

            _logger.Debug($"Best release found in {watch.ElapsedMilliseconds}ms");

            localAlbumRelease.PopulateMatch();

            _logger.Debug($"IdentifyRelease done in {watch.ElapsedMilliseconds}ms");
        }
コード例 #18
0
        public List <CandidateAlbumRelease> GetCandidatesFromTags(LocalAlbumRelease localAlbumRelease, Artist artist, Album album, AlbumRelease release, bool includeExisting)
        {
            var watch = System.Diagnostics.Stopwatch.StartNew();

            // Generally artist, album and release are null.  But if they're not then limit candidates appropriately.
            // We've tried to make sure that tracks are all for a single release.

            List <CandidateAlbumRelease> candidateReleases;

            // if we have a release ID, use that
            AlbumRelease tagMbidRelease = null;
            List <CandidateAlbumRelease> tagCandidate = null;

            var releaseIds = localAlbumRelease.LocalTracks.Select(x => x.FileTrackInfo.ReleaseMBId).Distinct().ToList();

            if (releaseIds.Count == 1 && releaseIds[0].IsNotNullOrWhiteSpace())
            {
                _logger.Debug("Selecting release from consensus ForeignReleaseId [{0}]", releaseIds[0]);
                tagMbidRelease = _releaseService.GetReleaseByForeignReleaseId(releaseIds[0], true);

                if (tagMbidRelease != null)
                {
                    tagCandidate = GetCandidatesByRelease(new List <AlbumRelease> {
                        tagMbidRelease
                    }, includeExisting);
                }
            }

            if (release != null)
            {
                // this case overrides the release picked up from the file tags
                _logger.Debug("Release {0} [{1} tracks] was forced", release, release.TrackCount);
                candidateReleases = GetCandidatesByRelease(new List <AlbumRelease> {
                    release
                }, includeExisting);
            }
            else if (album != null)
            {
                // use the release from file tags if it exists and agrees with the specified album
                if (tagMbidRelease?.AlbumId == album.Id)
                {
                    candidateReleases = tagCandidate;
                }
                else
                {
                    candidateReleases = GetCandidatesByAlbum(localAlbumRelease, album, includeExisting);
                }
            }
            else if (artist != null)
            {
                // use the release from file tags if it exists and agrees with the specified album
                if (tagMbidRelease?.Album.Value.ArtistMetadataId == artist.ArtistMetadataId)
                {
                    candidateReleases = tagCandidate;
                }
                else
                {
                    candidateReleases = GetCandidatesByArtist(localAlbumRelease, artist, includeExisting);
                }
            }
            else
            {
                if (tagMbidRelease != null)
                {
                    candidateReleases = tagCandidate;
                }
                else
                {
                    candidateReleases = GetCandidates(localAlbumRelease, includeExisting);
                }
            }

            watch.Stop();
            _logger.Debug($"Getting candidates from tags for {localAlbumRelease.LocalTracks.Count} tracks took {watch.ElapsedMilliseconds}ms");

            // if we haven't got any candidates then try fingerprinting
            return(candidateReleases);
        }
コード例 #19
0
        private void IdentifyRelease(LocalAlbumRelease localAlbumRelease, Artist artist, Album album, AlbumRelease release, bool newDownload, bool includeExisting)
        {
            var  watch         = System.Diagnostics.Stopwatch.StartNew();
            bool fingerprinted = false;

            var candidateReleases = GetCandidatesFromTags(localAlbumRelease, artist, album, release, includeExisting);

            if (candidateReleases.Count == 0 && FingerprintingAllowed(newDownload))
            {
                _logger.Debug("No candidates found, fingerprinting");
                _fingerprintingService.Lookup(localAlbumRelease.LocalTracks, 0.5);
                fingerprinted     = true;
                candidateReleases = GetCandidatesFromFingerprint(localAlbumRelease, artist, album, release, includeExisting);
            }

            if (candidateReleases.Count == 0)
            {
                // can't find any candidates even after fingerprinting
                return;
            }

            _logger.Debug($"Got {candidateReleases.Count} candidates for {localAlbumRelease.LocalTracks.Count} tracks in {watch.ElapsedMilliseconds}ms");

            var allTracks = _trackService.GetTracksByReleases(candidateReleases.Select(x => x.AlbumRelease.Id).ToList());

            // convert all the TrackFiles that represent extra files to List<LocalTrack>
            var allLocalTracks = ToLocalTrack(candidateReleases
                                              .SelectMany(x => x.ExistingTracks)
                                              .DistinctBy(x => x.Path), localAlbumRelease);

            _logger.Debug($"Retrieved {allTracks.Count} possible tracks in {watch.ElapsedMilliseconds}ms");

            GetBestRelease(localAlbumRelease, candidateReleases, allTracks, allLocalTracks);

            // If result isn't great and we haven't fingerprinted, try that
            // Note that this can improve the match even if we try the same candidates
            if (!fingerprinted && FingerprintingAllowed(newDownload) && ShouldFingerprint(localAlbumRelease))
            {
                _logger.Debug($"Match not good enough, fingerprinting");
                _fingerprintingService.Lookup(localAlbumRelease.LocalTracks, 0.5);

                // Only include extra possible candidates if neither album nor release are specified
                // Will generally be specified as part of manual import
                if (album == null && release == null)
                {
                    var extraCandidates = GetCandidatesFromFingerprint(localAlbumRelease, artist, album, release, includeExisting);
                    var newCandidates   = extraCandidates.ExceptBy(x => x.AlbumRelease.Id, candidateReleases, y => y.AlbumRelease.Id, EqualityComparer <int> .Default);
                    candidateReleases.AddRange(newCandidates);
                    allTracks.AddRange(_trackService.GetTracksByReleases(newCandidates.Select(x => x.AlbumRelease.Id).ToList()));
                    allLocalTracks.AddRange(ToLocalTrack(newCandidates
                                                         .SelectMany(x => x.ExistingTracks)
                                                         .DistinctBy(x => x.Path)
                                                         .ExceptBy(x => x.Path, allLocalTracks, x => x.Path, PathEqualityComparer.Instance),
                                                         localAlbumRelease));
                }

                // fingerprint all the local files in candidates we might be matching against
                _fingerprintingService.Lookup(allLocalTracks, 0.5);

                GetBestRelease(localAlbumRelease, candidateReleases, allTracks, allLocalTracks);
            }

            _logger.Debug($"Best release found in {watch.ElapsedMilliseconds}ms");

            localAlbumRelease.PopulateMatch();

            _logger.Debug($"IdentifyRelease done in {watch.ElapsedMilliseconds}ms");
        }