mirror of
https://github.com/fiso64/slsk-batchdl.git
synced 2024-12-22 22:42:41 +00:00
c to cancel album download
This commit is contained in:
parent
4a34ec0cbd
commit
529f009c48
11 changed files with 216 additions and 80 deletions
33
README.md
33
README.md
|
@ -23,6 +23,7 @@ See the [usage examples](#examples-1).
|
|||
- [File conditions](#file-conditions)
|
||||
- [Name format](#name-format)
|
||||
- [Configuration](#configuration)
|
||||
- [Shortcuts \& interactive mode](#shortcuts--interactive-mode)
|
||||
- [Examples](#examples-1)
|
||||
- [Notes](#notes)
|
||||
- [Docker](#docker)
|
||||
|
@ -407,7 +408,7 @@ Variables enclosed in {} will be replaced by the corresponding file tag value.
|
|||
Name format supports subdirectories as well as conditional expressions like {tag1|tag2} - If
|
||||
tag1 is null, use tag2. String literals enclosed in parentheses are ignored in the null check.
|
||||
|
||||
### Examples:
|
||||
### Examples
|
||||
- `{artist} - {title}`
|
||||
Always name it 'Artist - Title'. Because some files on Soulseek are untagged, the
|
||||
following is generally preferred:
|
||||
|
@ -418,7 +419,7 @@ tag1 is null, use tag2. String literals enclosed in parentheses are ignored in t
|
|||
Sort files into artist/album folders if all tags are present, otherwise put them in
|
||||
the 'missing-tags' folder.
|
||||
|
||||
### Available variables:
|
||||
### Available variables
|
||||
```
|
||||
artist First artist (from the file tags)
|
||||
sartist Source artist (as on CSV/Spotify/YouTube/etc)
|
||||
|
@ -439,7 +440,7 @@ default-folder Default sldl folder name (usually the playlist n
|
|||
```
|
||||
|
||||
## Configuration
|
||||
### Config Location:
|
||||
### Config Location
|
||||
sldl will look for a file named sldl.conf in the following locations:
|
||||
```
|
||||
~/AppData/Roaming/sldl/sldl.conf
|
||||
|
@ -447,7 +448,7 @@ default-folder Default sldl folder name (usually the playlist n
|
|||
```
|
||||
as well as in the directory of the executable.
|
||||
|
||||
### Syntax:
|
||||
### Syntax
|
||||
Example config file:
|
||||
```
|
||||
username = your-username
|
||||
|
@ -458,7 +459,7 @@ fast-search = true
|
|||
Lines starting with hashtags (#) will be ignored. Tildes in paths are expanded as the user
|
||||
directory. The path variable `{bindir}` stores the directory of the sldl binary.
|
||||
|
||||
### Configuration profiles:
|
||||
### Configuration profiles
|
||||
Profiles are supported:
|
||||
```
|
||||
[lossless]
|
||||
|
@ -485,6 +486,28 @@ input-type ("youtube"|"csv"|"string"|"bandcamp"|"spotify")
|
|||
download-mode ("normal"|"aggregate"|"album"|"album-aggregate")
|
||||
interactive (bool)
|
||||
```
|
||||
|
||||
## Shortcuts & interactive mode
|
||||
|
||||
### Shortcuts
|
||||
To cancel a running album download, press `C`.
|
||||
|
||||
### Interactive mode
|
||||
Interactive mode for albums can be enabled with `-t`/`--interactive`. It enables users to choose the desired folder or download specific files from it.
|
||||
|
||||
Key bindings:
|
||||
```
|
||||
Up/p previous folder
|
||||
Down/n next folder
|
||||
Enter/d download selected folder
|
||||
q download folder and disable interactive mode
|
||||
r retrieve all files in the folder
|
||||
Esc/s skip current album
|
||||
|
||||
d:1,2,3 download specific files
|
||||
d:start:end download a range of files
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Download tracks from a csv file:
|
||||
|
|
|
@ -202,7 +202,7 @@ public class Config
|
|||
if (confPath == "none")
|
||||
return;
|
||||
|
||||
confPath = Utils.ExpandUser(args[idx + 1]);
|
||||
confPath = Utils.ExpandVariables(args[idx + 1]);
|
||||
if(File.Exists(Path.Join(AppDomain.CurrentDomain.BaseDirectory, confPath)))
|
||||
confPath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, confPath);
|
||||
}
|
||||
|
@ -249,12 +249,12 @@ public class Config
|
|||
|
||||
nameFormat = nameFormat.Trim();
|
||||
|
||||
confPath = Utils.GetFullPath(Utils.ExpandUser(confPath));
|
||||
parentDir = Utils.GetFullPath(Utils.ExpandUser(parentDir));
|
||||
m3uFilePath = Utils.GetFullPath(Utils.ExpandUser(m3uFilePath));
|
||||
indexFilePath = Utils.GetFullPath(Utils.ExpandUser(indexFilePath));
|
||||
skipMusicDir = Utils.GetFullPath(Utils.ExpandUser(skipMusicDir));
|
||||
failedAlbumPath = Utils.GetFullPath(Utils.ExpandUser(failedAlbumPath));
|
||||
confPath = Utils.GetFullPath(Utils.ExpandVariables(confPath));
|
||||
parentDir = Utils.GetFullPath(Utils.ExpandVariables(parentDir));
|
||||
m3uFilePath = Utils.GetFullPath(Utils.ExpandVariables(m3uFilePath));
|
||||
indexFilePath = Utils.GetFullPath(Utils.ExpandVariables(indexFilePath));
|
||||
skipMusicDir = Utils.GetFullPath(Utils.ExpandVariables(skipMusicDir));
|
||||
failedAlbumPath = Utils.GetFullPath(Utils.ExpandVariables(failedAlbumPath));
|
||||
|
||||
if (failedAlbumPath.Length == 0)
|
||||
failedAlbumPath = Path.Join(parentDir, "failed");
|
||||
|
|
|
@ -11,7 +11,7 @@ using SearchResponse = Soulseek.SearchResponse;
|
|||
|
||||
static class Download
|
||||
{
|
||||
public static async Task DownloadFile(SearchResponse response, Soulseek.File file, string filePath, Track track, ProgressBar progress, Config config, CancellationToken? ct = null, CancellationTokenSource? searchCts = null)
|
||||
public static async Task DownloadFile(SearchResponse response, Soulseek.File file, string filePath, Track track, ProgressBar progress, TrackListEntry tle, Config config, CancellationToken? ct = null, CancellationTokenSource? searchCts = null)
|
||||
{
|
||||
await Program.WaitForLogin(config);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
|
||||
|
@ -40,7 +40,7 @@ static class Download
|
|||
new CancellationTokenSource();
|
||||
|
||||
using var outputStream = new FileStream(filePath, FileMode.Create);
|
||||
var wrapper = new DownloadWrapper(origPath, response, file, track, downloadCts, progress);
|
||||
var wrapper = new DownloadWrapper(origPath, response, file, track, downloadCts, progress, tle);
|
||||
downloads.TryAdd(file.Filename, wrapper);
|
||||
|
||||
int maxRetries = 3;
|
||||
|
|
|
@ -8,6 +8,7 @@ namespace Enums
|
|||
OutOfDownloadRetries = 2,
|
||||
NoSuitableFileFound = 3,
|
||||
AllDownloadsFailed = 4,
|
||||
Other = 5,
|
||||
}
|
||||
|
||||
public enum TrackState
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Extractors
|
|||
|
||||
public async Task<TrackLists> GetTracks(string input, int maxTracks, int offset, bool reverse, Config config)
|
||||
{
|
||||
csvFilePath = Utils.ExpandUser(input);
|
||||
csvFilePath = Utils.ExpandVariables(input);
|
||||
|
||||
if (!File.Exists(csvFilePath))
|
||||
throw new FileNotFoundException($"CSV file '{csvFilePath}' not found");
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace Extractors
|
|||
|
||||
public async Task<TrackLists> GetTracks(string input, int maxTracks, int offset, bool reverse, Config config)
|
||||
{
|
||||
listFilePath = Utils.ExpandUser(input);
|
||||
listFilePath = Utils.ExpandVariables(input);
|
||||
|
||||
if (!File.Exists(listFilePath))
|
||||
throw new FileNotFoundException($"List file '{listFilePath}' not found");
|
||||
|
|
|
@ -183,7 +183,7 @@ public static class Help
|
|||
|
||||
Help
|
||||
-h, --help [option] [all|input|download-modes|search|name-format|
|
||||
file-conditions|config]
|
||||
file-conditions|config|shortcuts]
|
||||
|
||||
Notes
|
||||
Acronyms of two- and --three-word-flags are also accepted, e.g. --twf. If the option
|
||||
|
@ -473,6 +473,28 @@ public static class Help
|
|||
interactive (bool)
|
||||
";
|
||||
|
||||
const string shortcutsHelp = @"
|
||||
Shortcuts & interactive mode
|
||||
Shortcuts
|
||||
To cancel a running album download, press `C`.
|
||||
|
||||
Interactive mode
|
||||
Interactive mode for albums can be enabled with `-t`/`--interactive`. It enables users
|
||||
to choose the desired folder or download specific files from it.
|
||||
|
||||
Key bindings:
|
||||
|
||||
Up/p previous folder
|
||||
Down/n next folder
|
||||
Enter/d download selected folder
|
||||
q download folder and disable interactive mode
|
||||
r retrieve all files in the folder
|
||||
Esc/s skip current album
|
||||
|
||||
d:1,2,3 download specific files
|
||||
d:start:end download a range of files
|
||||
";
|
||||
|
||||
public static void PrintHelp(string? option = null)
|
||||
{
|
||||
string text = helpText;
|
||||
|
@ -485,6 +507,7 @@ public static class Help
|
|||
{ "file-conditions", fileConditionsHelp },
|
||||
{ "name-format", nameFormatHelp },
|
||||
{ "config", configHelp },
|
||||
{ "shortcuts", shortcutsHelp },
|
||||
};
|
||||
|
||||
if (option != null && dict.ContainsKey(option))
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace Models
|
|||
public SearchResponse response;
|
||||
public ProgressBar progress;
|
||||
public Track track;
|
||||
public TrackListEntry tle;
|
||||
public long bytesTransferred = 0;
|
||||
public bool stalled = false;
|
||||
public bool queued = false;
|
||||
|
@ -29,12 +30,13 @@ namespace Models
|
|||
bool updatedTextSuccess = false;
|
||||
readonly char[] bars = { '|', '/', '—', '\\' };
|
||||
|
||||
public DownloadWrapper(string savePath, SearchResponse response, Soulseek.File file, Track track, CancellationTokenSource cts, ProgressBar progress)
|
||||
public DownloadWrapper(string savePath, SearchResponse response, Soulseek.File file, Track track, CancellationTokenSource cts, ProgressBar progress, TrackListEntry tle)
|
||||
{
|
||||
this.savePath = savePath;
|
||||
this.response = response;
|
||||
this.file = file;
|
||||
this.cts = cts;
|
||||
this.tle = tle;
|
||||
this.track = track;
|
||||
this.progress = progress;
|
||||
this.displayText = Printing.DisplayString(track, file, response);
|
||||
|
|
|
@ -22,6 +22,8 @@ static partial class Program
|
|||
const int updateInterval = 100;
|
||||
private static bool initialized = false;
|
||||
public static bool skipUpdate = false;
|
||||
public static bool interceptKeys = false;
|
||||
public static event EventHandler<ConsoleKey>? keyPressed;
|
||||
|
||||
public static IExtractor extractor = null!;
|
||||
public static TrackLists trackLists = null!;
|
||||
|
@ -615,6 +617,15 @@ static partial class Program
|
|||
string? soulseekDir = null;
|
||||
int index = 0;
|
||||
|
||||
async Task runAlbumDownloads(List<Track> tracks, SemaphoreSlim semaphore, CancellationTokenSource cts)
|
||||
{
|
||||
var downloadTasks = tracks.Select(async track =>
|
||||
{
|
||||
await DownloadTask(config, tle, track, semaphore, organizer, cts, true, true, true);
|
||||
});
|
||||
await Task.WhenAll(downloadTasks);
|
||||
}
|
||||
|
||||
while (tle.list.Count > 0 && !config.albumArtOnly)
|
||||
{
|
||||
bool wasInteractive = config.interactiveMode;
|
||||
|
@ -641,12 +652,24 @@ static partial class Program
|
|||
PrintAlbum(tracks);
|
||||
}
|
||||
|
||||
var semaphore = new SemaphoreSlim(999); // Needs to be uncapped due to a bug that causes album downloads to fail after some time
|
||||
using var semaphore = new SemaphoreSlim(999); // Needs to be uncapped due to a bug that causes album downloads to fail after some time
|
||||
using var cts = new CancellationTokenSource();
|
||||
|
||||
bool userCancelled = false;
|
||||
void onKeyPressed(object? sender, ConsoleKey key)
|
||||
{
|
||||
if (key == ConsoleKey.C)
|
||||
{
|
||||
userCancelled = true;
|
||||
cts.Cancel();
|
||||
}
|
||||
}
|
||||
interceptKeys = true;
|
||||
keyPressed += onKeyPressed;
|
||||
|
||||
try
|
||||
{
|
||||
await RunAlbumDownloads(config, tle, organizer, tracks, semaphore, cts);
|
||||
await runAlbumDownloads(tracks, semaphore, cts);
|
||||
|
||||
if (!config.noBrowseFolder && retrieveCurrent && !retrievedFolders.Contains(soulseekDir))
|
||||
{
|
||||
|
@ -657,8 +680,8 @@ static partial class Program
|
|||
|
||||
if (newFilesFound > 0)
|
||||
{
|
||||
Console.WriteLine($"Found {newFilesFound} more files in the directory, downloading:");
|
||||
await RunAlbumDownloads(config, tle, organizer, tracks, semaphore, cts);
|
||||
Console.WriteLine($"Found {newFilesFound} more files, downloading:");
|
||||
await runAlbumDownloads(tracks, semaphore, cts);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -671,16 +694,52 @@ static partial class Program
|
|||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
OnAlbumFail(config, tracks);
|
||||
if (userCancelled)
|
||||
{
|
||||
Console.Write("\nDownload cancelled.");
|
||||
if (tracks.Any(t => t.State == TrackState.Downloaded && t.DownloadPath.Length > 0))
|
||||
{
|
||||
Console.WriteLine("Delete files? [Y/n] (default: album fail action): ");
|
||||
var res = Console.ReadLine().Trim().ToLower();
|
||||
if (res == "y")
|
||||
OnAlbumFail(tracks, true, config);
|
||||
else if (res == "" && !config.IgnoreAlbumFail)
|
||||
OnAlbumFail(tracks, config.DeleteAlbumOnFail, config);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!config.IgnoreAlbumFail)
|
||||
OnAlbumFail(tracks, config.DeleteAlbumOnFail, config);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
organizer.SetRemoteCommonDir(null);
|
||||
tle.list.RemoveAt(index);
|
||||
interceptKeys = false;
|
||||
keyPressed -= onKeyPressed;
|
||||
}
|
||||
}
|
||||
|
||||
if (succeeded)
|
||||
{
|
||||
await OnAlbumSuccess(config, tle, tracks);
|
||||
tle.source.State = TrackState.Downloaded;
|
||||
|
||||
var downloadedAudio = tracks.Where(t => !t.IsNotAudio && t.State == TrackState.Downloaded && t.DownloadPath.Length > 0);
|
||||
if (downloadedAudio.Any())
|
||||
{
|
||||
tle.source.DownloadPath = Utils.GreatestCommonDirectory(downloadedAudio.Select(t => t.DownloadPath));
|
||||
|
||||
if (config.removeTracksFromSource)
|
||||
{
|
||||
await extractor.RemoveTrackFromSource(tle.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (index != -1)
|
||||
{
|
||||
tle.source.State = TrackState.Failed;
|
||||
}
|
||||
|
||||
List<Track>? additionalImages = null;
|
||||
|
@ -704,40 +763,9 @@ static partial class Program
|
|||
}
|
||||
|
||||
|
||||
static async Task RunAlbumDownloads(Config config, TrackListEntry tle, FileManager organizer, List<Track> tracks, SemaphoreSlim semaphore, CancellationTokenSource cts)
|
||||
static void OnAlbumFail(List<Track>? tracks, bool deleteDownloaded, Config config)
|
||||
{
|
||||
var downloadTasks = tracks.Select(async track =>
|
||||
{
|
||||
await DownloadTask(config, tle, track, semaphore, organizer, cts, true, true, true);
|
||||
});
|
||||
await Task.WhenAll(downloadTasks);
|
||||
}
|
||||
|
||||
|
||||
static async Task OnAlbumSuccess(Config config, TrackListEntry tle, List<Track>? tracks)
|
||||
{
|
||||
if (tracks == null)
|
||||
return;
|
||||
|
||||
var downloadedAudio = tracks.Where(t => !t.IsNotAudio && t.State == TrackState.Downloaded && t.DownloadPath.Length > 0);
|
||||
|
||||
if (downloadedAudio.Any())
|
||||
{
|
||||
tle.source.State = TrackState.Downloaded;
|
||||
tle.source.DownloadPath = Utils.GreatestCommonDirectory(downloadedAudio.Select(t => t.DownloadPath));
|
||||
|
||||
if (config.removeTracksFromSource)
|
||||
{
|
||||
await extractor.RemoveTrackFromSource(tle.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void OnAlbumFail(Config config, List<Track>? tracks)
|
||||
{
|
||||
if (tracks == null || config.IgnoreAlbumFail)
|
||||
return;
|
||||
if (tracks == null) return;
|
||||
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
|
@ -745,7 +773,7 @@ static partial class Program
|
|||
{
|
||||
try
|
||||
{
|
||||
if (config.DeleteAlbumOnFail)
|
||||
if (deleteDownloaded || track.DownloadPath.EndsWith(".incomplete"))
|
||||
{
|
||||
File.Delete(track.DownloadPath);
|
||||
}
|
||||
|
@ -881,11 +909,25 @@ static partial class Program
|
|||
fileManager.SetRemoteCommonDir(Utils.GreatestCommonDirectorySlsk(tracks.Select(t => t.FirstDownload.Filename)));
|
||||
|
||||
bool allSucceeded = true;
|
||||
var semaphore = new SemaphoreSlim(1);
|
||||
using var semaphore = new SemaphoreSlim(1);
|
||||
using var cts = new CancellationTokenSource();
|
||||
|
||||
bool userCancelled = false;
|
||||
void onKeyPressed(object? sender, ConsoleKey key)
|
||||
{
|
||||
if (key == ConsoleKey.C)
|
||||
{
|
||||
userCancelled = true;
|
||||
cts.Cancel();
|
||||
}
|
||||
}
|
||||
interceptKeys = true;
|
||||
keyPressed += onKeyPressed;
|
||||
|
||||
try
|
||||
{
|
||||
foreach (var track in tracks)
|
||||
{
|
||||
using var cts = new CancellationTokenSource();
|
||||
await DownloadTask(config, tle, track, semaphore, fileManager, cts, false, false, false);
|
||||
|
||||
if (track.State == TrackState.Downloaded)
|
||||
|
@ -893,6 +935,29 @@ static partial class Program
|
|||
else
|
||||
allSucceeded = false;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
if (userCancelled)
|
||||
{
|
||||
Console.Write("\nDownload cancelled.");
|
||||
if (tracks.Any(t => t.State == TrackState.Downloaded && t.DownloadPath.Length > 0))
|
||||
{
|
||||
Console.WriteLine("Delete files? [Y/n]: ");
|
||||
if (Console.ReadLine().Trim().ToLower() == "y")
|
||||
OnAlbumFail(tracks, true, config);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
interceptKeys = false;
|
||||
keyPressed -= onKeyPressed;
|
||||
}
|
||||
|
||||
if (allSucceeded)
|
||||
break;
|
||||
|
@ -902,7 +967,7 @@ static partial class Program
|
|||
}
|
||||
|
||||
|
||||
static async Task DownloadTask(Config config, TrackListEntry? tle, Track track, SemaphoreSlim semaphore, FileManager organizer, CancellationTokenSource? cts, bool cancelOnFail, bool removeFromSource, bool organize)
|
||||
static async Task DownloadTask(Config config, TrackListEntry tle, Track track, SemaphoreSlim semaphore, FileManager organizer, CancellationTokenSource cts, bool cancelOnFail, bool removeFromSource, bool organize)
|
||||
{
|
||||
if (track.State != TrackState.Initial)
|
||||
return;
|
||||
|
@ -921,7 +986,7 @@ static partial class Program
|
|||
|
||||
try
|
||||
{
|
||||
(savedFilePath, chosenFile) = await Search.SearchAndDownload(track, organizer, config, cts);
|
||||
(savedFilePath, chosenFile) = await Search.SearchAndDownload(track, organizer, tle, config, cts);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -944,6 +1009,15 @@ static partial class Program
|
|||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
else if (ex is OperationCanceledException && cts.IsCancellationRequested)
|
||||
{
|
||||
lock (trackLists)
|
||||
{
|
||||
track.State = TrackState.Failed;
|
||||
track.FailureReason = FailureReason.Other;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
else
|
||||
{
|
||||
tries--;
|
||||
|
@ -956,6 +1030,12 @@ static partial class Program
|
|||
|
||||
if (tries == 0 && cancelOnFail)
|
||||
{
|
||||
lock (trackLists)
|
||||
{
|
||||
track.State = TrackState.Failed;
|
||||
track.FailureReason = FailureReason.Other;
|
||||
}
|
||||
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
|
@ -1027,8 +1107,6 @@ static partial class Program
|
|||
WriteLine($" [Up/p] | [Down/n] | [Enter] | [q] {retrieveAll1}| [Esc/s]", ConsoleColor.Green);
|
||||
WriteLine($" Prev | Next | Accept | Accept & Quit Interactive {retrieveAll2}| Skip", ConsoleColor.Green);
|
||||
Console.WriteLine();
|
||||
WriteLine($" d:1,2,3 or d:start:end to download individual files", ConsoleColor.Green);
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
writeHelp();
|
||||
|
@ -1123,7 +1201,7 @@ static partial class Program
|
|||
}
|
||||
|
||||
|
||||
static async Task Update(Config config)
|
||||
static async Task Update(Config startConfig)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
|
@ -1145,7 +1223,7 @@ static partial class Program
|
|||
{
|
||||
lock (val)
|
||||
{
|
||||
if ((DateTime.Now - val.UpdateLastChangeTime()).TotalMilliseconds > config.maxStaleTime)
|
||||
if ((DateTime.Now - val.UpdateLastChangeTime()).TotalMilliseconds > val.tle.config.maxStaleTime)
|
||||
{
|
||||
val.stalled = true;
|
||||
val.UpdateText();
|
||||
|
@ -1172,10 +1250,10 @@ static partial class Program
|
|||
&& !client.State.HasFlag(SoulseekClientStates.Connecting))
|
||||
{
|
||||
WriteLine($"\nDisconnected, logging in\n", ConsoleColor.DarkYellow, true);
|
||||
try { await Login(config, config.useRandomLogin); }
|
||||
try { await Login(startConfig, startConfig.useRandomLogin); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
string banMsg = config.useRandomLogin ? "" : " (possibly a 30-minute ban caused by frequent searches)";
|
||||
string banMsg = startConfig.useRandomLogin ? "" : " (possibly a 30-minute ban caused by frequent searches)";
|
||||
WriteLine($"{ex.Message}{banMsg}", ConsoleColor.DarkYellow, true);
|
||||
}
|
||||
}
|
||||
|
@ -1193,6 +1271,12 @@ static partial class Program
|
|||
{
|
||||
WriteLine($"\n{ex.Message}\n", ConsoleColor.DarkYellow, true);
|
||||
}
|
||||
|
||||
if (interceptKeys && Console.KeyAvailable)
|
||||
{
|
||||
var key = Console.ReadKey(intercept: true).Key;
|
||||
keyPressed?.Invoke(null, key);
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(updateInterval);
|
||||
|
|
|
@ -16,7 +16,7 @@ static class Search
|
|||
public static RateLimitedSemaphore? searchSemaphore;
|
||||
|
||||
// very messy function that does everything
|
||||
public static async Task<(string, SlFile?)> SearchAndDownload(Track track, FileManager organizer, Config config, CancellationTokenSource? cts = null)
|
||||
public static async Task<(string, SlFile?)> SearchAndDownload(Track track, FileManager organizer, TrackListEntry tle, Config config, CancellationTokenSource? cts = null)
|
||||
{
|
||||
if (config.DoNotDownload)
|
||||
throw new Exception();
|
||||
|
@ -58,7 +58,7 @@ static class Search
|
|||
saveFilePath = organizer.GetSavePath(f.Filename);
|
||||
fsUser = r.Username;
|
||||
chosenFile = f;
|
||||
downloadTask = Download.DownloadFile(r, f, saveFilePath, track, progress, config, cts?.Token, searchCts);
|
||||
downloadTask = Download.DownloadFile(r, f, saveFilePath, track, progress, tle, config, cts?.Token, searchCts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,12 +155,15 @@ static class Search
|
|||
try
|
||||
{
|
||||
downloading = 1;
|
||||
await Download.DownloadFile(response, file, saveFilePath, track, progress, config, cts?.Token);
|
||||
await Download.DownloadFile(response, file, saveFilePath, track, progress, tle, config, cts?.Token);
|
||||
userSuccessCounts.AddOrUpdate(response.Username, 1, (k, v) => v + 1);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is OperationCanceledException && cts != null && cts.IsCancellationRequested)
|
||||
throw;
|
||||
|
||||
Printing.WriteLineIf($"Error: Download Error: {e}", config.debugInfo, ConsoleColor.DarkYellow);
|
||||
|
||||
chosenFile = null;
|
||||
|
|
|
@ -101,7 +101,7 @@ public static class Utils
|
|||
return Path.GetDirectoryName(fname);
|
||||
}
|
||||
|
||||
public static string ExpandUser(string path)
|
||||
public static string ExpandVariables(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return path;
|
||||
|
|
Loading…
Reference in a new issue