diff --git a/README.md b/README.md index e8915b2..f9d7982 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,7 @@ The id and secret can be obtained at https://developer.spotify.com/dashboard/app Create an app and add http://localhost:48721/callback as a redirect url in its settings. ### Bandcamp -An bandcamp url: Download a single track, and album, or an artist's entire discography. +A bandcamp url: Download a single track, and album, or an artist's entire discography. Extracts the artist name, album name and sets --album-track-count="n+", where n is the number of visible tracks on the bandcamp page. diff --git a/slsk-batchdl/ExistingChecker.cs b/slsk-batchdl/ExistingChecker.cs index d02fb3d..ee2054f 100644 --- a/slsk-batchdl/ExistingChecker.cs +++ b/slsk-batchdl/ExistingChecker.cs @@ -31,7 +31,7 @@ namespace ExistingCheckers public class NameExistingChecker : ExistingChecker { - readonly string[] ignore = new string[] { " ", "_", "-", ".", "(", ")", "[", "]" }; + readonly string[] ignore = new string[] { "_", "-", ".", "(", ")", "[", "]" }; readonly string dir; readonly List<(string, string, string)> index = new(); // (Path, PreprocessedPath, PreprocessedName) @@ -42,15 +42,22 @@ namespace ExistingCheckers private string Preprocess(string s, bool removeSlash) { - s = s.ToLower().Replace(ignore, ""); - s = s.ReplaceInvalidChars("", false, removeSlash); + s = s.ToLower(); s = s.RemoveFt(); - s = s.RemoveDiacritics(); + s = s.Replace(ignore, " "); + s = s.ReplaceInvalidChars(' ', false, removeSlash); + s = s.RemoveConsecutiveWs(); return s; } public override void BuildIndex() { + if (!Directory.Exists(dir)) + { + IndexIsBuilt = true; + return; + } + var files = Directory.GetFiles(dir, "*", SearchOption.AllDirectories); int removeLen = Preprocess(dir, false).Length + 1; @@ -59,7 +66,7 @@ namespace ExistingCheckers { if (Utils.IsMusicFile(path)) { - string ppath = Preprocess(path[..path.LastIndexOf('.')], false)[removeLen..]; + string ppath = Preprocess(path[removeLen..path.LastIndexOf('.')], false); string pname = Path.GetFileName(ppath); index.Add((path, ppath, pname)); } @@ -80,8 +87,7 @@ namespace ExistingCheckers foreach ((var path, var ppath, var pname) in index) { - if (pname.ContainsWithBoundaryIgnoreWs(title, acceptLeftDigit: true) - && ppath.ContainsWithBoundaryIgnoreWs(artist, acceptLeftDigit: true)) + if (pname.ContainsWithBoundary(title) && ppath.ContainsWithBoundary(artist)) { foundPath = path; return true; @@ -95,7 +101,7 @@ namespace ExistingCheckers public class NameConditionExistingChecker : ExistingChecker { - readonly string[] ignore = new string[] { " ", "_", "-", ".", "(", ")", "[", "]" }; + readonly string[] ignore = new string[] { "_", "-", ".", "(", ")", "[", "]" }; readonly string dir; readonly List<(string, string, SimpleFile)> index = new(); // (PreprocessedPath, PreprocessedName, file) FileConditions conditions; @@ -108,15 +114,22 @@ namespace ExistingCheckers private string Preprocess(string s, bool removeSlash) { - s = s.ToLower().Replace(ignore, ""); - s = s.ReplaceInvalidChars("", false, removeSlash); + s = s.ToLower(); s = s.RemoveFt(); - s = s.RemoveDiacritics(); + s = s.Replace(ignore, " "); + s = s.ReplaceInvalidChars(' ', false, removeSlash); + s = s.RemoveConsecutiveWs(); return s; } public override void BuildIndex() { + if (!Directory.Exists(dir)) + { + IndexIsBuilt = true; + return; + } + var files = Directory.GetFiles(dir, "*", SearchOption.AllDirectories); int removeLen = Preprocess(dir, false).Length + 1; @@ -150,9 +163,7 @@ namespace ExistingCheckers foreach ((var ppath, var pname, var musicFile) in index) { - if (pname.ContainsWithBoundaryIgnoreWs(title, acceptLeftDigit: true) - && ppath.ContainsWithBoundaryIgnoreWs(artist, acceptLeftDigit: true) - && conditions.FileSatisfies(musicFile, track)) + if (pname.ContainsWithBoundary(title) && ppath.ContainsWithBoundary(artist) && conditions.FileSatisfies(musicFile, track)) { foundPath = musicFile.Path; return true; @@ -176,11 +187,17 @@ namespace ExistingCheckers private string Preprocess(string s) { - return s.Replace(" ", "").RemoveFt().ToLower(); + return s.RemoveFt().Replace(" ", "").ToLower(); } public override void BuildIndex() { + if (!Directory.Exists(dir)) + { + IndexIsBuilt = true; + return; + } + var files = Directory.GetFiles(dir, "*", SearchOption.AllDirectories); foreach (var path in files) @@ -237,11 +254,17 @@ namespace ExistingCheckers private string Preprocess(string s) { - return s.Replace(" ", "").RemoveFt().ToLower(); + return s.RemoveFt().Replace(" ", "").ToLower(); } public override void BuildIndex() { + if (!Directory.Exists(dir)) + { + IndexIsBuilt = true; + return; + } + var files = Directory.GetFiles(dir, "*", SearchOption.AllDirectories); foreach (var path in files) @@ -293,6 +316,7 @@ namespace ExistingCheckers { this.m3uEditor = m3UEditor; this.checkFileExists = checkFileExists; + IndexIsBuilt = true; } public override bool TrackExists(Track track, out string? foundPath) @@ -334,6 +358,7 @@ namespace ExistingCheckers { this.m3uEditor = m3UEditor; this.conditions = conditions; + IndexIsBuilt = true; } public override bool TrackExists(Track track, out string? foundPath) diff --git a/slsk-batchdl/Help.cs b/slsk-batchdl/Help.cs index 51f796c..fc57940 100644 --- a/slsk-batchdl/Help.cs +++ b/slsk-batchdl/Help.cs @@ -221,7 +221,7 @@ public static class Help Create an app and add http://localhost:48721/callback as a redirect url in its settings. Bandcamp - An bandcamp url: Download a single track, and album, or an artist's entire discography. + A bandcamp url: Download a single track, and album, or an artist's entire discography. Extracts the artist name, album name and sets --album-track-count=""n+"", where n is the number of visible tracks on the bandcamp page. diff --git a/slsk-batchdl/Program.cs b/slsk-batchdl/Program.cs index 33778ae..9d50ce8 100644 --- a/slsk-batchdl/Program.cs +++ b/slsk-batchdl/Program.cs @@ -76,7 +76,7 @@ static partial class Program m3uEditor = new M3uEditor(Config.m3uFilePath, trackLists, Config.m3uOption, Config.offset); - InitExistingChecker(); + InitExistingCheckers(); await MainLoop(); WriteLine("Mainloop done", debugOnly: true); @@ -109,7 +109,7 @@ static partial class Program } - static void InitExistingChecker() + static void InitExistingCheckers() { if (Config.skipExisting) { @@ -123,7 +123,7 @@ static partial class Program if (!Directory.Exists(Config.musicDir)) Console.WriteLine("Error: Music directory does not exist"); else - musicDirExistingChecker = ExistingCheckerRegistry.GetChecker(Config.skipModeMusicDir, Config.outputFolder, cond, m3uEditor); + musicDirExistingChecker = ExistingCheckerRegistry.GetChecker(Config.skipModeMusicDir, Config.musicDir, cond, m3uEditor); } } } @@ -255,7 +255,7 @@ static partial class Program } else if (tle.source.Type == TrackType.Aggregate) { - tle.list[0] = await GetAggregateTracks(tle.source, responseData); + tle.list.Insert(0, await GetAggregateTracks(tle.source, responseData)); } else if (tle.source.Type == TrackType.AlbumAggregate) { @@ -271,7 +271,7 @@ static partial class Program if (Config.skipExisting && tle.needSkipExistingAfterSearch) { foreach (var tracks in tle.list) - notFound.AddRange(DoSkipExisting(tracks)); + existing.AddRange(DoSkipExisting(tracks)); } if (tle.gotoNextAfterSearch) @@ -412,7 +412,7 @@ static partial class Program Console.WriteLine("\n-----------------------------------------------\n"); } - if (Config.PrintTracks) + if (Config.PrintTracks || Config.PrintResults) { if (existing.Count > 0) { diff --git a/slsk-batchdl/SearchAndDownload.cs b/slsk-batchdl/SearchAndDownload.cs index d46072b..2e824ff 100644 --- a/slsk-batchdl/SearchAndDownload.cs +++ b/slsk-batchdl/SearchAndDownload.cs @@ -363,7 +363,7 @@ static partial class Program { int musicFileCount = val.Count(x => Utils.IsMusicFile(x.file.Filename)); - if (!countIsGood(musicFileCount)) + if (musicFileCount == 0 || !countIsGood(musicFileCount)) continue; var ls = new List(); @@ -433,6 +433,8 @@ static partial class Program string trackName = track.Title.Trim(); string albumName = track.Album.Trim(); + //var orderedResults = OrderedResults(results, track, false, false, false); + var fileResponses = results.Select(x => x.Value); var equivalentFiles = EquivalentFiles(track, fileResponses).ToList(); @@ -961,9 +963,11 @@ static partial class Program } - static Track InferTrack(string filename, Track defaultTrack) + static Track InferTrack(string filename, Track defaultTrack, TrackType type = TrackType.Normal) { var t = new Track(defaultTrack); + t.Type = type; + filename = Utils.GetFileNameWithoutExtSlsk(filename).Replace(" — ", " - ").Replace('_', ' ').Trim().RemoveConsecutiveWs(); var trackNumStart = new Regex(@"^(?:(?:[0-9][-\.])?\d{2,3}[. -]|\b\d\.\s|\b\d\s-\s)(?=.+\S)"); diff --git a/slsk-batchdl/Utils.cs b/slsk-batchdl/Utils.cs index 172388f..0b631f4 100644 --- a/slsk-batchdl/Utils.cs +++ b/slsk-batchdl/Utils.cs @@ -357,7 +357,7 @@ public static class Utils return s.Contains(other, StringComparison.OrdinalIgnoreCase); } - static readonly HashSet boundarySet = new("-|.\\/_—()[],:?!;@:*=+{}|'\"$^&`~%<>".ToCharArray()); + static readonly HashSet boundarySet = new("-|.\\/_—()[],:?!;@#:*=+{}|'\"$^&`~%<>".ToCharArray()); public static bool ContainsWithBoundary(this string str, string value, bool ignoreCase = false) { @@ -434,6 +434,38 @@ public static class Utils return false; } + public static bool ContainsInBracketsOptimized(this string str, string searchTerm, bool ignoreCase = false) + { + if (str.Length == 0 && searchTerm.Length > 0) + return false; + + var comp = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal; + int depth = 0; + int searchTermLen = searchTerm.Length; + + for (int i = 0; i < str.Length; i++) + { + char c = str[i]; + + if (c == '[' || c == '(') + { + depth++; + } + else if (c == ']' || c == ')') + { + depth--; + } + + if (depth > 0 && i + searchTermLen <= str.Length) + { + if (str.Substring(i, searchTermLen).Equals(searchTerm, comp)) + return true; + } + } + + return false; + } + public static bool RemoveRegexIfExist(this string s, string reg, out string res) { res = Regex.Replace(s, reg, string.Empty);