mirror of
https://github.com/fiso64/slsk-batchdl.git
synced 2024-12-22 14:32:40 +00:00
parallel album searching
This commit is contained in:
parent
85571115cf
commit
111442b40c
10 changed files with 274 additions and 126 deletions
|
@ -198,6 +198,7 @@ Usage: sldl <input> [OPTIONS]
|
||||||
the directory fails to download. Set to 'delete' to delete
|
the directory fails to download. Set to 'delete' to delete
|
||||||
the files instead. Set to 'disable' keep it where it is.
|
the files instead. Set to 'disable' keep it where it is.
|
||||||
Default: {configured output dir}/failed
|
Default: {configured output dir}/failed
|
||||||
|
--album-parallel-search Run album searches in parallel
|
||||||
```
|
```
|
||||||
#### Aggregate Download Options
|
#### Aggregate Download Options
|
||||||
```
|
```
|
||||||
|
@ -357,7 +358,8 @@ The search query is determined as follows:
|
||||||
- --concurrent-downloads - set it to 4 or more
|
- --concurrent-downloads - set it to 4 or more
|
||||||
- --max-stale-time is set to 50 seconds by default, so it will wait a long time before giving
|
- --max-stale-time is set to 50 seconds by default, so it will wait a long time before giving
|
||||||
up on a file
|
up on a file
|
||||||
- --searches-per-time - increase at the risk of bans.
|
- --searches-per-time - increase at the risk of bans
|
||||||
|
- --album-parallel-search - enables parallel searching for album entries
|
||||||
|
|
||||||
## File conditions
|
## File conditions
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,10 @@ using System.Text.RegularExpressions;
|
||||||
|
|
||||||
public class Config
|
public class Config
|
||||||
{
|
{
|
||||||
public FileConditions necessaryCond = new();
|
public FileConditions necessaryCond = new()
|
||||||
|
{
|
||||||
|
Formats = new string[] { ".mp3", ".flac", ".ogg", ".m4a", ".opus", ".wav", ".aac", ".alac" },
|
||||||
|
};
|
||||||
|
|
||||||
public FileConditions preferredCond = new()
|
public FileConditions preferredCond = new()
|
||||||
{
|
{
|
||||||
|
@ -79,6 +82,7 @@ public class Config
|
||||||
public bool writePlaylist = false;
|
public bool writePlaylist = false;
|
||||||
public bool skipExisting = true;
|
public bool skipExisting = true;
|
||||||
public bool writeIndex = true;
|
public bool writeIndex = true;
|
||||||
|
public bool parallelAlbumSearch = false;
|
||||||
public int downrankOn = -1;
|
public int downrankOn = -1;
|
||||||
public int ignoreOn = -2;
|
public int ignoreOn = -2;
|
||||||
public int minAlbumTrackCount = -1;
|
public int minAlbumTrackCount = -1;
|
||||||
|
@ -96,6 +100,7 @@ public class Config
|
||||||
public int searchesPerTime = 34;
|
public int searchesPerTime = 34;
|
||||||
public int searchRenewTime = 220;
|
public int searchRenewTime = 220;
|
||||||
public int aggregateLengthTol = 3;
|
public int aggregateLengthTol = 3;
|
||||||
|
public int parallelAlbumSearchProcesses = 5;
|
||||||
public double fastSearchMinUpSpeed = 1.0;
|
public double fastSearchMinUpSpeed = 1.0;
|
||||||
public Track regexToReplace = new();
|
public Track regexToReplace = new();
|
||||||
public Track regexReplaceBy = new();
|
public Track regexReplaceBy = new();
|
||||||
|
@ -1257,6 +1262,14 @@ public class Config
|
||||||
case "--aggregate-length-tol":
|
case "--aggregate-length-tol":
|
||||||
aggregateLengthTol = int.Parse(args[++i]);
|
aggregateLengthTol = int.Parse(args[++i]);
|
||||||
break;
|
break;
|
||||||
|
case "--aps":
|
||||||
|
case "--album-parallel-search":
|
||||||
|
setFlag(ref parallelAlbumSearch, ref i);
|
||||||
|
break;
|
||||||
|
case "--apsc":
|
||||||
|
case "--album-parallel-search-count":
|
||||||
|
parallelAlbumSearchProcesses = int.Parse(args[++i]);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException($"Unknown argument: {args[i]}");
|
throw new ArgumentException($"Unknown argument: {args[i]}");
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace Extractors
|
||||||
else throw;
|
else throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
tle.defaultFolderName = playlistName;
|
tle.defaultFolderName = playlistName.ReplaceInvalidChars(' ').Trim();
|
||||||
tle.enablesIndexByDefault = true;
|
tle.enablesIndexByDefault = true;
|
||||||
tle.list.Add(tracks);
|
tle.list.Add(tracks);
|
||||||
}
|
}
|
||||||
|
|
|
@ -607,7 +607,7 @@ namespace Extractors
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<List<(int length, string id, string title)>> YtdlpSearch(Track track)
|
public static async Task<List<(int length, string id, string title)>> YtdlpSearch(Track track, bool printCommand = false)
|
||||||
{
|
{
|
||||||
Process process = new Process();
|
Process process = new Process();
|
||||||
ProcessStartInfo startInfo = new ProcessStartInfo();
|
ProcessStartInfo startInfo = new ProcessStartInfo();
|
||||||
|
@ -623,6 +623,8 @@ namespace Extractors
|
||||||
process.OutputDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
process.OutputDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
||||||
process.ErrorDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
process.ErrorDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
||||||
|
|
||||||
|
Printing.WriteLineIf($"{startInfo.FileName} {startInfo.Arguments}", printCommand);
|
||||||
|
|
||||||
process.Start();
|
process.Start();
|
||||||
|
|
||||||
List<(int, string, string)> results = new List<(int, string, string)>();
|
List<(int, string, string)> results = new List<(int, string, string)>();
|
||||||
|
@ -644,7 +646,7 @@ namespace Extractors
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<string> YtdlpDownload(string id, string savePathNoExt, string ytdlpArgument = "")
|
public static async Task<string> YtdlpDownload(string id, string savePathNoExt, string ytdlpArgument = "", bool printCommand = false)
|
||||||
{
|
{
|
||||||
Process process = new Process();
|
Process process = new Process();
|
||||||
ProcessStartInfo startInfo = new ProcessStartInfo();
|
ProcessStartInfo startInfo = new ProcessStartInfo();
|
||||||
|
@ -666,6 +668,8 @@ namespace Extractors
|
||||||
//process.OutputDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
//process.OutputDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
||||||
//process.ErrorDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
//process.ErrorDataReceived += (sender, e) => { Console.WriteLine(e.Data); };
|
||||||
|
|
||||||
|
Printing.WriteLineIf($"{startInfo.FileName} {startInfo.Arguments}", printCommand);
|
||||||
|
|
||||||
process.Start();
|
process.Start();
|
||||||
process.WaitForExit();
|
process.WaitForExit();
|
||||||
|
|
||||||
|
|
|
@ -117,7 +117,14 @@ public class FileManager
|
||||||
if (track.DownloadPath.Length == 0)
|
if (track.DownloadPath.Length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
string newFilePath = Path.Join(parent, Path.GetFileName(track.DownloadPath));
|
string? part = null;
|
||||||
|
|
||||||
|
if (remoteCommonDir != null && Utils.IsInDirectory(Utils.GetDirectoryNameSlsk(track.FirstDownload.Filename), remoteCommonDir, true))
|
||||||
|
{
|
||||||
|
part = Utils.GetFileNameSlsk(Utils.GetDirectoryNameSlsk(track.FirstDownload.Filename));
|
||||||
|
}
|
||||||
|
|
||||||
|
string newFilePath = Path.Join(parent, part, Path.GetFileName(track.DownloadPath));
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
// --invalid-replace-str, --cond, --pref
|
// --invalid-replace-str, --cond, --pref
|
||||||
// --fast-search-delay, --fast-search-min-up-speed
|
// --fast-search-delay, --fast-search-min-up-speed
|
||||||
// --min-album-track-count, --max-album-track-count, --extract-max-track-count, --extract-min-track-count
|
// --min-album-track-count, --max-album-track-count, --extract-max-track-count, --extract-min-track-count
|
||||||
// --skip-mode-music-dir, --skip-mode-output-dir
|
// --skip-mode-music-dir, --skip-mode-output-dir, --album-parallel-search-count
|
||||||
|
|
||||||
public static class Help
|
public static class Help
|
||||||
{
|
{
|
||||||
|
@ -168,6 +168,7 @@ public static class Help
|
||||||
the directory fails to download. Set to 'delete' to delete
|
the directory fails to download. Set to 'delete' to delete
|
||||||
the files instead. Set to the empty string """" to disable.
|
the files instead. Set to the empty string """" to disable.
|
||||||
Default: {configured output dir}/failed
|
Default: {configured output dir}/failed
|
||||||
|
--album-parallel-search Run album searches in parallel
|
||||||
|
|
||||||
Aggregate Download
|
Aggregate Download
|
||||||
-g, --aggregate Aggregate download mode: Find and download all distinct
|
-g, --aggregate Aggregate download mode: Find and download all distinct
|
||||||
|
@ -333,7 +334,8 @@ public static class Help
|
||||||
--concurrent-downloads - set it to 4 or more
|
--concurrent-downloads - set it to 4 or more
|
||||||
--max-stale-time is set to 50 seconds by default, so it will wait a long time before giving
|
--max-stale-time is set to 50 seconds by default, so it will wait a long time before giving
|
||||||
up on a file
|
up on a file
|
||||||
--searches-per-time - increase at the risk of bans.
|
--searches-per-time - increase at the risk of bans
|
||||||
|
--album-parallel-search - enables parallel searching for album entries
|
||||||
";
|
";
|
||||||
|
|
||||||
const string fileConditionsHelp = @"
|
const string fileConditionsHelp = @"
|
||||||
|
|
|
@ -22,6 +22,8 @@ namespace Models
|
||||||
public FileSkipper? outputDirSkipper = null;
|
public FileSkipper? outputDirSkipper = null;
|
||||||
public FileSkipper? musicDirSkipper = null;
|
public FileSkipper? musicDirSkipper = null;
|
||||||
|
|
||||||
|
public bool CanParallelSearch => source.Type == TrackType.Album || source.Type == TrackType.Aggregate;
|
||||||
|
|
||||||
public TrackListEntry(TrackType trackType)
|
public TrackListEntry(TrackType trackType)
|
||||||
{
|
{
|
||||||
list = new List<List<Track>>();
|
list = new List<List<Track>>();
|
||||||
|
|
|
@ -15,6 +15,7 @@ using static Printing;
|
||||||
using Directory = System.IO.Directory;
|
using Directory = System.IO.Directory;
|
||||||
using File = System.IO.File;
|
using File = System.IO.File;
|
||||||
using SlFile = Soulseek.File;
|
using SlFile = Soulseek.File;
|
||||||
|
using Konsole;
|
||||||
|
|
||||||
static partial class Program
|
static partial class Program
|
||||||
{
|
{
|
||||||
|
@ -54,9 +55,7 @@ static partial class Program
|
||||||
trackLists.UpgradeListTypes(config.aggregate, config.album);
|
trackLists.UpgradeListTypes(config.aggregate, config.album);
|
||||||
trackLists.SetListEntryOptions();
|
trackLists.SetListEntryOptions();
|
||||||
|
|
||||||
InitConfigs(config);
|
await MainLoop(config);
|
||||||
|
|
||||||
await MainLoop();
|
|
||||||
|
|
||||||
WriteLineIf("Mainloop done", config.debugInfo);
|
WriteLineIf("Mainloop done", config.debugInfo);
|
||||||
}
|
}
|
||||||
|
@ -105,18 +104,13 @@ static partial class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void InitConfigs(Config defaultConfig)
|
static void InitEditors(TrackListEntry tle, Config config)
|
||||||
{
|
|
||||||
if (trackLists.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
void initEditors(TrackListEntry tle, Config config)
|
|
||||||
{
|
{
|
||||||
tle.playlistEditor = new M3uEditor(trackLists, config.writePlaylist ? M3uOption.Playlist : M3uOption.None, config.offset);
|
tle.playlistEditor = new M3uEditor(trackLists, config.writePlaylist ? M3uOption.Playlist : M3uOption.None, config.offset);
|
||||||
tle.indexEditor = new M3uEditor(trackLists, config.writeIndex ? M3uOption.Index : M3uOption.None);
|
tle.indexEditor = new M3uEditor(trackLists, config.writeIndex ? M3uOption.Index : M3uOption.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initFileSkippers(TrackListEntry tle, Config config)
|
static void InitFileSkippers(TrackListEntry tle, Config config)
|
||||||
{
|
{
|
||||||
if (config.skipExisting)
|
if (config.skipExisting)
|
||||||
{
|
{
|
||||||
|
@ -143,25 +137,30 @@ static partial class Program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var tle in trackLists.lists)
|
static void InitConfigs(Config defaultConfig)
|
||||||
{
|
{
|
||||||
tle.config = defaultConfig.Copy();
|
//if (trackLists.Count == 0)
|
||||||
tle.config.UpdateProfiles(tle);
|
// return;
|
||||||
|
|
||||||
if (tle.extractorCond != null)
|
//foreach (var tle in trackLists.lists)
|
||||||
{
|
//{
|
||||||
tle.config.necessaryCond = tle.config.necessaryCond.With(tle.extractorCond);
|
// tle.config = defaultConfig.Copy();
|
||||||
tle.extractorCond = null;
|
// tle.config.UpdateProfiles(tle);
|
||||||
}
|
|
||||||
if (tle.extractorPrefCond != null)
|
|
||||||
{
|
|
||||||
tle.config.preferredCond = tle.config.preferredCond.With(tle.extractorPrefCond);
|
|
||||||
tle.extractorPrefCond = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
initEditors(tle, tle.config);
|
// if (tle.extractorCond != null)
|
||||||
initFileSkippers(tle, tle.config);
|
// {
|
||||||
}
|
// tle.config.necessaryCond = tle.config.necessaryCond.With(tle.extractorCond);
|
||||||
|
// tle.extractorCond = null;
|
||||||
|
// }
|
||||||
|
// if (tle.extractorPrefCond != null)
|
||||||
|
// {
|
||||||
|
// tle.config.preferredCond = tle.config.preferredCond.With(tle.extractorPrefCond);
|
||||||
|
// tle.extractorPrefCond = null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// initEditors(tle, tle.config);
|
||||||
|
// initFileSkippers(tle, tle.config);
|
||||||
|
//}
|
||||||
|
|
||||||
//defaultConfig.UpdateProfiles(trackLists[0]);
|
//defaultConfig.UpdateProfiles(trackLists[0]);
|
||||||
//trackLists[0].config = defaultConfig;
|
//trackLists[0].config = defaultConfig;
|
||||||
|
@ -266,40 +265,66 @@ static partial class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void PrepareListEntry(Config config, TrackListEntry tle, bool isFirstEntry)
|
static void PrepareListEntry(Config prevConfig, TrackListEntry tle)
|
||||||
{
|
{
|
||||||
|
tle.config = prevConfig.Copy();
|
||||||
|
tle.config.UpdateProfiles(tle);
|
||||||
|
|
||||||
|
if (tle.extractorCond != null)
|
||||||
|
{
|
||||||
|
tle.config.necessaryCond = tle.config.necessaryCond.With(tle.extractorCond);
|
||||||
|
tle.extractorCond = null;
|
||||||
|
}
|
||||||
|
if (tle.extractorPrefCond != null)
|
||||||
|
{
|
||||||
|
tle.config.preferredCond = tle.config.preferredCond.With(tle.extractorPrefCond);
|
||||||
|
tle.extractorPrefCond = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitEditors(tle, tle.config);
|
||||||
|
InitFileSkippers(tle, tle.config);
|
||||||
|
|
||||||
string m3uPath, indexPath;
|
string m3uPath, indexPath;
|
||||||
|
|
||||||
if (config.m3uFilePath.Length > 0)
|
if (tle.config.m3uFilePath.Length > 0)
|
||||||
m3uPath = config.m3uFilePath;
|
m3uPath = tle.config.m3uFilePath;
|
||||||
else
|
else
|
||||||
m3uPath = Path.Join(config.parentDir, tle.defaultFolderName, "_playlist.m3u8");
|
m3uPath = Path.Join(tle.config.parentDir, tle.defaultFolderName, "_playlist.m3u8");
|
||||||
|
|
||||||
if (config.indexFilePath.Length > 0)
|
if (tle.config.indexFilePath.Length > 0)
|
||||||
indexPath = config.indexFilePath;
|
indexPath = tle.config.indexFilePath;
|
||||||
else
|
else
|
||||||
indexPath = Path.Join(config.parentDir, tle.defaultFolderName, "_index.sldl");
|
indexPath = Path.Join(tle.config.parentDir, tle.defaultFolderName, "_index.sldl");
|
||||||
|
|
||||||
if (config.writePlaylist)
|
if (tle.config.writePlaylist)
|
||||||
tle.playlistEditor?.SetPathAndLoad(m3uPath);
|
tle.playlistEditor?.SetPathAndLoad(m3uPath);
|
||||||
if (config.writeIndex)
|
if (tle.config.writeIndex)
|
||||||
tle.indexEditor?.SetPathAndLoad(indexPath);
|
tle.indexEditor?.SetPathAndLoad(indexPath);
|
||||||
|
|
||||||
PreprocessTracks(config, tle);
|
PreprocessTracks(tle.config, tle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static async Task MainLoop()
|
static async Task MainLoop(Config defaultConfig)
|
||||||
{
|
{
|
||||||
|
if (trackLists.Count == 0) return;
|
||||||
|
|
||||||
|
PrepareListEntry(defaultConfig, trackLists[0]);
|
||||||
|
var firstConfig = trackLists.lists[0].config;
|
||||||
|
|
||||||
|
bool enableParallelSearch = firstConfig.parallelAlbumSearch && !firstConfig.PrintResults && !firstConfig.PrintTracks && trackLists.lists.Any(x => x.CanParallelSearch);
|
||||||
|
var parallelSearches = new List<(TrackListEntry tle, Task<(bool, ResponseData)> task)>();
|
||||||
|
var parallelSearchSemaphore = new SemaphoreSlim(firstConfig.parallelAlbumSearchProcesses);
|
||||||
|
|
||||||
for (int i = 0; i < trackLists.lists.Count; i++)
|
for (int i = 0; i < trackLists.lists.Count; i++)
|
||||||
{
|
{
|
||||||
Console.WriteLine();
|
if (!enableParallelSearch) Console.WriteLine();
|
||||||
|
|
||||||
|
if (i > 0) PrepareListEntry(trackLists[i-1].config, trackLists[i]);
|
||||||
|
|
||||||
var tle = trackLists[i];
|
var tle = trackLists[i];
|
||||||
var config = tle.config;
|
var config = tle.config;
|
||||||
|
|
||||||
PrepareListEntry(config, tle, isFirstEntry: i == 0);
|
|
||||||
|
|
||||||
var existing = new List<Track>();
|
var existing = new List<Track>();
|
||||||
var notFound = new List<Track>();
|
var notFound = new List<Track>();
|
||||||
|
|
||||||
|
@ -361,7 +386,14 @@ static partial class Program
|
||||||
{
|
{
|
||||||
await InitClientAndUpdateIfNeeded(config);
|
await InitClientAndUpdateIfNeeded(config);
|
||||||
|
|
||||||
Console.WriteLine($"{tle.source.Type} download: {tle.source.ToString(true)}, searching..");
|
ProgressBar? progress = null;
|
||||||
|
|
||||||
|
async Task<(bool, ResponseData)> sourceSearch()
|
||||||
|
{
|
||||||
|
await parallelSearchSemaphore.WaitAsync();
|
||||||
|
|
||||||
|
progress = enableParallelSearch ? Printing.GetProgressBar(config) : null;
|
||||||
|
Printing.RefreshOrPrint(progress, 0, $"{tle.source.Type} download: {tle.source.ToString(true)}, searching..", print: true);
|
||||||
|
|
||||||
bool foundSomething = false;
|
bool foundSomething = false;
|
||||||
var responseData = new ResponseData();
|
var responseData = new ResponseData();
|
||||||
|
@ -391,11 +423,26 @@ static partial class Program
|
||||||
foundSomething = res.Count > 0;
|
foundSomething = res.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tle.needSourceSearch = false;
|
||||||
|
|
||||||
if (!foundSomething)
|
if (!foundSomething)
|
||||||
{
|
{
|
||||||
var lockedFiles = responseData.lockedFilesCount > 0 ? $" (Found {responseData.lockedFilesCount} locked files)" : "";
|
var lockedFiles = responseData.lockedFilesCount > 0 ? $" (Found {responseData.lockedFilesCount} locked files)" : "";
|
||||||
Console.WriteLine($"No results.{lockedFiles}");
|
var str = progress != null ? $"{tle.source}: " : "";
|
||||||
|
Printing.RefreshOrPrint(progress, 0, $"{str}No results.{lockedFiles}", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
parallelSearchSemaphore.Release();
|
||||||
|
|
||||||
|
return (foundSomething, responseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enableParallelSearch || !tle.CanParallelSearch)
|
||||||
|
{
|
||||||
|
(bool foundSomething, ResponseData responseData) = await sourceSearch();
|
||||||
|
|
||||||
|
if (!foundSomething)
|
||||||
|
{
|
||||||
if (!config.PrintResults)
|
if (!config.PrintResults)
|
||||||
{
|
{
|
||||||
tle.source.State = TrackState.Failed;
|
tle.source.State = TrackState.Failed;
|
||||||
|
@ -417,6 +464,12 @@ static partial class Program
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parallelSearches.Add((tle, sourceSearch()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (config.PrintResults)
|
if (config.PrintResults)
|
||||||
{
|
{
|
||||||
|
@ -424,6 +477,29 @@ static partial class Program
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parallelSearches.Count > 0 && !tle.CanParallelSearch)
|
||||||
|
{
|
||||||
|
await parallelDownloads();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enableParallelSearch || !tle.CanParallelSearch)
|
||||||
|
{
|
||||||
|
await download(tle, config, notFound, existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parallelSearches.Count > 0)
|
||||||
|
{
|
||||||
|
await parallelDownloads();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!trackLists[^1].config.DoNotDownload && (trackLists.lists.Count > 0 || trackLists.Flattened(false, false).Skip(1).Any()))
|
||||||
|
{
|
||||||
|
PrintComplete(trackLists);
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task download(TrackListEntry tle, Config config, List<Track>? notFound, List<Track>? existing)
|
||||||
|
{
|
||||||
tle.indexEditor?.Update();
|
tle.indexEditor?.Update();
|
||||||
tle.playlistEditor?.Update();
|
tle.playlistEditor?.Update();
|
||||||
|
|
||||||
|
@ -432,9 +508,9 @@ static partial class Program
|
||||||
PrintTracksTbd(tle.list[0].Where(t => t.State == TrackState.Initial).ToList(), existing, notFound, tle.source.Type, config);
|
PrintTracksTbd(tle.list[0].Where(t => t.State == TrackState.Initial).ToList(), existing, notFound, tle.source.Type, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notFound.Count + existing.Count >= tle.list.Sum(x => x.Count))
|
if (notFound != null && existing != null && notFound.Count + existing.Count >= tle.list.Sum(x => x.Count))
|
||||||
{
|
{
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await InitClientAndUpdateIfNeeded(config);
|
await InitClientAndUpdateIfNeeded(config);
|
||||||
|
@ -453,9 +529,38 @@ static partial class Program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!trackLists[^1].config.DoNotDownload && (trackLists.lists.Count > 0 || trackLists.Flattened(false, false).Skip(1).Any()))
|
async Task parallelDownloads()
|
||||||
{
|
{
|
||||||
PrintComplete(trackLists);
|
await Task.WhenAll(parallelSearches.Select(x => x.task));
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
foreach (var (tle, task) in parallelSearches)
|
||||||
|
{
|
||||||
|
(bool foundSomething, var responseData) = task.Result;
|
||||||
|
|
||||||
|
if (foundSomething)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Downloading: {tle.source}");
|
||||||
|
await download(tle, tle.config, null, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!tle.config.PrintResults)
|
||||||
|
{
|
||||||
|
tle.source.State = TrackState.Failed;
|
||||||
|
tle.source.FailureReason = FailureReason.NoSuitableFileFound;
|
||||||
|
tle.indexEditor?.Update();
|
||||||
|
}
|
||||||
|
if (tle.config.skipExisting && tle.needSkipExistingAfterSearch)
|
||||||
|
{
|
||||||
|
foreach (var tracks in tle.list)
|
||||||
|
DoSkipExisting(tle, tle.config, tracks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parallelSearches.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,6 +854,11 @@ static partial class Program
|
||||||
Console.WriteLine("No images found");
|
Console.WriteLine("No images found");
|
||||||
return downloadedImages;
|
return downloadedImages;
|
||||||
}
|
}
|
||||||
|
else if (!albumArts.Skip(1).Any() && albumArts.First().All(y => y.State != TrackState.Initial))
|
||||||
|
{
|
||||||
|
Console.WriteLine("No additional images found");
|
||||||
|
return downloadedImages;
|
||||||
|
}
|
||||||
|
|
||||||
if (option == AlbumArtOption.Largest)
|
if (option == AlbumArtOption.Largest)
|
||||||
{
|
{
|
||||||
|
|
|
@ -102,7 +102,7 @@ static class Search
|
||||||
},
|
},
|
||||||
fileFilter: (file) =>
|
fileFilter: (file) =>
|
||||||
{
|
{
|
||||||
return Utils.IsMusicFile(file.Filename) && necCond.FileSatisfies(file, track, null);
|
return necCond.FileSatisfies(file, track, null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ static class Search
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Printing.RefreshOrPrint(progress, 0, $"yt-dlp search: {track}", true);
|
Printing.RefreshOrPrint(progress, 0, $"yt-dlp search: {track}", true);
|
||||||
var ytResults = await Extractors.YouTube.YtdlpSearch(track);
|
var ytResults = await Extractors.YouTube.YtdlpSearch(track, printCommand: config.debugInfo);
|
||||||
|
|
||||||
if (ytResults.Count > 0)
|
if (ytResults.Count > 0)
|
||||||
{
|
{
|
||||||
|
@ -224,8 +224,8 @@ static class Search
|
||||||
{
|
{
|
||||||
string saveFilePathNoExt = organizer.GetSavePathNoExt(title);
|
string saveFilePathNoExt = organizer.GetSavePathNoExt(title);
|
||||||
downloading = 1;
|
downloading = 1;
|
||||||
Printing.RefreshOrPrint(progress, 0, $"yt-dlp download: {track}", true);
|
Printing.RefreshOrPrint(progress, 0, $"yt-dlp download: {track}, filename: {saveFilePathNoExt}", true);
|
||||||
saveFilePath = await Extractors.YouTube.YtdlpDownload(id, saveFilePathNoExt, config.ytdlpArgument);
|
saveFilePath = await Extractors.YouTube.YtdlpDownload(id, saveFilePathNoExt, config.ytdlpArgument, printCommand: config.debugInfo);
|
||||||
Printing.RefreshOrPrint(progress, 100, $"Succeded: yt-dlp completed download for {track}", true);
|
Printing.RefreshOrPrint(progress, 100, $"Succeded: yt-dlp completed download for {track}", true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -418,7 +418,7 @@ static class Search
|
||||||
},
|
},
|
||||||
fileFilter: (file) =>
|
fileFilter: (file) =>
|
||||||
{
|
{
|
||||||
return Utils.IsMusicFile(file.Filename) && nec.FileSatisfies(file, track, null);
|
return nec.FileSatisfies(file, track, null);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
void handler(SlResponse r)
|
void handler(SlResponse r)
|
||||||
|
@ -678,6 +678,7 @@ static class Search
|
||||||
bool albumMode = false)
|
bool albumMode = false)
|
||||||
{
|
{
|
||||||
bool useBracketCheck = true;
|
bool useBracketCheck = true;
|
||||||
|
|
||||||
if (albumMode)
|
if (albumMode)
|
||||||
{
|
{
|
||||||
useBracketCheck = false;
|
useBracketCheck = false;
|
||||||
|
@ -881,7 +882,7 @@ static class Search
|
||||||
},
|
},
|
||||||
fileFilter: (file) =>
|
fileFilter: (file) =>
|
||||||
{
|
{
|
||||||
return Utils.IsMusicFile(file.Filename) && (necCond.FileSatisfies(file, track, null) || config.PrintResultsFull);
|
return (necCond.FileSatisfies(file, track, null) || config.PrintResultsFull);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -634,6 +634,13 @@ public static class Utils
|
||||||
return path.Replace('\\', '/').TrimEnd('/').Trim();
|
return path.Replace('\\', '/').TrimEnd('/').Trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsInDirectory(string path, string dir, bool strict)
|
||||||
|
{
|
||||||
|
path = NormalizedPath(path);
|
||||||
|
dir = NormalizedPath(dir);
|
||||||
|
return strict ? path.StartsWith(dir + '/') : path.StartsWith(dir);
|
||||||
|
}
|
||||||
|
|
||||||
public static bool SequenceEqualUpToPermutation<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
|
public static bool SequenceEqualUpToPermutation<T>(this IEnumerable<T> list1, IEnumerable<T> list2)
|
||||||
{
|
{
|
||||||
var cnt = new Dictionary<T, int>();
|
var cnt = new Dictionary<T, int>();
|
||||||
|
|
Loading…
Reference in a new issue