Example #1
0
        static IEnumerable <Release> CheckReleasesConsistency(TrackDirInfo dirInfo, IReadOnlyList <Release> releases)
        {
            if (releases.Count == 0)
            {
                return(releases);
            }

            IEnumerable <string> GetTokens(string s) => StringUtils.SplitToTokens(s).Filter(x => x.Length > 3);

            var dirInfoTokens   = dirInfo.Tracks.Map(x => x.MeaningfulTrackTitleTokens).Bind(GetTokens).ToList();
            var idealConformity = dirInfoTokens.Count;

            int MeasureTracklistConformity(IEnumerable <Track> tracklist)
            {
                var tokens = tracklist.Map(x => x.Title).Bind(GetTokens);

                return(tokens.Intersect(dirInfoTokens).Count());
            }

            const int coefficient = 3;

            return(releases
                   .Map(release => (release, conformity: MeasureTracklistConformity(release.Tracklist)))
                   .Filter(x => x.conformity >= idealConformity / coefficient)
                   .Map(x => x.release));
        }
Example #2
0
        static async Task <Arr <Release> > FindMatchingReleases(
            DiscogsApiClient discogsApiClient,
            TrackDirInfo dirInfo,
            string query,
            int tracksCount
            )
        {
            var db           = discogsApiClient.Database;
            var searchResult = await db.Search(query);

            var goodBriefReleases = searchResult.Results
                                    .Filter(x => x.Id != x.MasterId)
                                    .Filter(x => CheckReleaseNameConsistency(x.Title, query))
                                    .Take(5);

            var releases = new List <Release>();

            foreach (var releaseBrief in goodBriefReleases)
            {
                try
                {
                    var release = await db.GetRelease(releaseBrief.Id);

                    if (release.Tracklist == null || release.Tracklist.Count != tracksCount)
                    {
                        continue;
                    }
                    release.Tracklist =
                        release.Tracklist.Filter(x => !string.IsNullOrEmpty(x.Position)).ToList(); // TODO

                    releases.Add(release);
                }
                catch (DiscogsApiException) { } // TODO: not found
            }

            return(CheckReleasesConsistency(dirInfo, releases).ToArr());
        }
Example #3
0
        public static async Task <Option <OutputRoot> > Match(DiscogsApiClient discogsApiClient, TrackDirInfo dirInfo)
        {
            var matchingReleases = Arr <Release> .Empty;
            var queries          = dirInfo.CommonPrefix.Bind(DirNameToQueries)
                                   .Concat(DirNameToQueries(Path.GetFileName(dirInfo.Path)));

            foreach (var q in queries)
            {
                if (!matchingReleases.IsEmpty)
                {
                    break;
                }
                matchingReleases = await FindMatchingReleases(discogsApiClient, dirInfo, q, dirInfo.Tracks.Count);
            }

            if (matchingReleases.IsEmpty)
            {
                return(None);
            }

            var canonicity = fun <string, int>(StringUtils.MeasureStringCanonicity);

            var sortedReleases = matchingReleases
                                 .OrderByDescending(x => (x.Tracklist.Select(y => y.Title).Sum(canonicity), x.Community.Have)).ToArray();
            var bestRelease = sortedReleases.First();

            var trackInfos = new List <MatchedTrackInfo>();

            foreach (var trackFileInfo in dirInfo.Tracks)
            {
                var releaseTrack = trackFileInfo.TrackNumber.Match(
                    n => sortedReleases.Map(x => x.Tracklist[n - 1]).OrderByDescending(x => canonicity(x.Title))
                    .First(),
                    () => bestRelease.Tracklist
                    .OrderBy(x => StringUtils.CalculateLevenshteinDistance(x.Title, trackFileInfo.TrackTitle))
                    .First()
                    );
                var isReleaseTrackMoreCanonical =
                    canonicity(releaseTrack.Title) >= canonicity(trackFileInfo.TrackTitle);
                var isTrackTitleUseless =
                    trackFileInfo.MeaningfulTrackTitleTokens.Length * 3 < releaseTrack.Title.Length;
                var trackTitle = isReleaseTrackMoreCanonical || isTrackTitleUseless
                    ? releaseTrack.Title.Trim()
                    : trackFileInfo.TrackTitle;

                var trackNewNumber = trackFileInfo.TrackNumber.IfNone(() => int.Parse(releaseTrack.Position));

                trackInfos.Add(new MatchedTrackInfo(
                                   path: Path.Combine(dirInfo.Path, trackFileInfo.FileName),
                                   trackNumber: trackNewNumber,
                                   title: trackTitle
                                   ));
            }

            var bestReleaseTitle = sortedReleases.Map(x => x.Title)
                                   .Append(Path.GetFileName(dirInfo.Path))
                                   .OrderByDescending(canonicity)
                                   .First();

            return(new OutputRoot(
                       album: bestReleaseTitle,
                       performers: bestRelease.Artists.Map(x => x.Name).ToArr(),
                       tracks: trackInfos.ToArr()
                       ));
        }
Example #4
0
        static async Task ProcessDir(CliEnvironment env, DiscogsApiClient discogsApiClient, TrackDirInfo trackDirInfo)
        {
            var dirPath = trackDirInfo.Path;

            var outputRootOpt = await Matcher.Match(discogsApiClient, trackDirInfo);

            if (outputRootOpt.IsNone)
            {
                return;
            }
            var outputRoot = outputRootOpt.ValueUnsafe();

            foreach (var track in outputRoot.Tracks)
            {
                var trackNewFileName = $"{track.TrackNumber:00}. {track.Title}{Path.GetExtension(track.Path)}";

                var trackOldPath = track.Path;
                var trackNewPath = Path.Combine(dirPath, trackNewFileName);
                if (trackOldPath != trackNewPath)
                {
                    env.WriteInfoLine($"Rename '{Path.GetFileName(track.Path)}' -> '{trackNewFileName}'");
                    if (!env.Args.Dry)
                    {
                        File.Move(trackOldPath, trackNewPath);
                    }
                }

                if (!env.Args.Dry)
                {
                    var tagLibFile = TagLib.File.Create(trackNewPath);
                    var tags       = tagLibFile.Tag;
                    tags.Performers = outputRoot.Performers.ToArray();
                    tags.Album      = outputRoot.Album;
                    tags.Title      = track.Title;
                    tags.Track      = (uint)track.TrackNumber;
                    tagLibFile.Save();
                }
            }

            var newDirPath = Path.Combine(Path.GetDirectoryName(dirPath), outputRoot.Album);

            if (dirPath == newDirPath)
            {
                return;
            }
            env.WriteInfoLine($"Rename '{dirPath}' -> '{newDirPath}'");
            if (!env.Args.Dry)
            {
                Directory.Move(dirPath, newDirPath);
            }
        }