1
0
Fork 0
mirror of https://github.com/fiso64/slsk-batchdl.git synced 2024-12-22 14:32:40 +00:00
This commit is contained in:
fiso64 2023-06-01 22:27:12 +02:00
parent 3e688192ab
commit 134e8ce442
2 changed files with 122 additions and 108 deletions

2
.gitignore vendored
View file

@ -362,3 +362,5 @@ MigrationBackup/
# Fody - auto-generated XML schema # Fody - auto-generated XML schema
FodyWeavers.xsd FodyWeavers.xsd
/slsk-batchdl/Properties/launchSettings.json /slsk-batchdl/Properties/launchSettings.json
/slsk-batchdl/Test.cs
/Class1.cs

View file

@ -3,14 +3,13 @@ using System.Text.RegularExpressions;
using Soulseek; using Soulseek;
using Konsole; using Konsole;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
using System.Xml.Linq; using System.Collections.Concurrent;
using TagLib.Matroska;
class Program class Program
{ {
static SoulseekClient client = new SoulseekClient(); static SoulseekClient client = new SoulseekClient();
static Dictionary<Track, SearchInfo> searches = new Dictionary<Track, SearchInfo>(); static ConcurrentDictionary<Track, SearchInfo> searches = new ConcurrentDictionary<Track, SearchInfo>();
static Dictionary<string, DownloadWrapper> downloads = new Dictionary<string, DownloadWrapper>(); static ConcurrentDictionary<string, DownloadWrapper> downloads = new ConcurrentDictionary<string, DownloadWrapper>();
static List<Track> tracks = new List<Track>(); static List<Track> tracks = new List<Track>();
static string outputFolder = ""; static string outputFolder = "";
static string failsFilePath = ""; static string failsFilePath = "";
@ -18,11 +17,17 @@ class Program
static string musicDir = ""; static string musicDir = "";
static string ytdlpFormat = ""; static string ytdlpFormat = "";
static int downloadMaxStaleTime = 0; static int downloadMaxStaleTime = 0;
static int updateDelay = 300; static int updateDelay = 200;
static int slowUpdateDelay = 5000; static int slowUpdateDelay = 2000;
static bool slowConsoleOutput = false; static bool slowConsoleOutput = false;
private static object consoleLock = new object(); static string logLocation = "";
static StreamWriter? logFile = null;
static TextWriterTraceListener? textListener = null;
static object failsFileLock = new object();
static object consoleLock = new object();
static bool writeFails = true;
static DateTime lastUpdate; static DateTime lastUpdate;
static bool skipUpdate = false; static bool skipUpdate = false;
@ -65,7 +70,6 @@ class Program
"\n channel names; attempt to parse." + "\n channel names; attempt to parse." +
"\n" + "\n" +
"\n -s --single <str> Search & download a specific track" + "\n -s --single <str> Search & download a specific track" +
"\n -a --album <str> Does nothing" +
"\n" + "\n" +
"\n --pref-format <format> Preferred file format (default: mp3)" + "\n --pref-format <format> Preferred file format (default: mp3)" +
"\n --pref-length-tol <tol> Preferred length tolerance (default: 3)" + "\n --pref-length-tol <tol> Preferred length tolerance (default: 3)" +
@ -120,10 +124,23 @@ class Program
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
if (!string.IsNullOrEmpty(logLocation))
{
logFile = new StreamWriter(System.IO.Path.Combine(logLocation, "log.txt"), append: false);
textListener = new TextWriterTraceListener(logFile);
Trace.Listeners.Add(textListener);
}
AppDomain.CurrentDomain.UnhandledException += (sender, e) => { AppDomain.CurrentDomain.UnhandledException += (sender, e) => {
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"{e.ExceptionObject}"); Console.WriteLine($"{e.ExceptionObject}");
Console.ResetColor(); Console.ResetColor();
Trace.TraceError($"{e.ExceptionObject}");
if (logFile != null)
{
logFile.Flush();
logFile.Close();
}
}; };
Console.ResetColor(); Console.ResetColor();
@ -461,6 +478,7 @@ class Program
else if (singleName != "") else if (singleName != "")
{ {
tracks.Add(new Track { TrackTitle=singleName, onlyTrackTitle=true }); tracks.Add(new Track { TrackTitle=singleName, onlyTrackTitle=true });
writeFails = false;
} }
else else
throw new Exception("Nothing url, csv or name provided to download."); throw new Exception("Nothing url, csv or name provided to download.");
@ -673,9 +691,12 @@ class Program
{ {
skipUpdate = true; skipUpdate = true;
await Task.Delay(50); await Task.Delay(50);
Console.ForegroundColor = ConsoleColor.Blue; lock (consoleLock)
Console.WriteLine($"\nSuccesses: {successCount}, fails: {failCount}, tracks left: {tracksRemaining}\n"); {
Console.ResetColor(); Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"\nSuccesses: {successCount}, fails: {failCount}, tracks left: {tracksRemaining}\n");
Console.ResetColor();
}
await Task.Delay(50); await Task.Delay(50);
skipUpdate = false; skipUpdate = false;
} }
@ -684,7 +705,7 @@ class Program
await Task.WhenAll(downloadTasks); await Task.WhenAll(downloadTasks);
if (tracks.Count > 1) if (tracks.Count > 1)
Console.WriteLine($"\n\nDownloaded {tracks.Count - tracksRemaining} of {tracks.Count} tracks"); Console.WriteLine($"\n\nDownloaded {tracks.Count - failCount} of {tracks.Count} tracks");
if (System.IO.File.Exists(failsFilePath)) if (System.IO.File.Exists(failsFilePath))
Console.WriteLine($"Failed:\n{System.IO.File.ReadAllText(failsFilePath)}"); Console.WriteLine($"Failed:\n{System.IO.File.ReadAllText(failsFilePath)}");
} }
@ -694,6 +715,8 @@ class Program
{ {
var title = !track.onlyTrackTitle ? $"{track.ArtistName} - {track.TrackTitle}" : $"{track.TrackTitle}"; var title = !track.onlyTrackTitle ? $"{track.ArtistName} - {track.TrackTitle}" : $"{track.TrackTitle}";
var saveFilePath = ""; var saveFilePath = "";
Trace.TraceInformation($"Searching for {title}");
logFile?.Flush();
var searchQuery = SearchQuery.FromText($"{title}"); var searchQuery = SearchQuery.FromText($"{title}");
var searchOptions = new SearchOptions var searchOptions = new SearchOptions
@ -711,56 +734,61 @@ class Program
bool attemptedDownloadPref = false; bool attemptedDownloadPref = false;
Task? downloadTask = null; Task? downloadTask = null;
object downloadingLocker = new object();
bool downloading = false; bool downloading = false;
var responses = new List<SearchResponse>(); var responses = new ConcurrentDictionary<string, SearchResponse>();
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
Console.ResetColor(); Console.ResetColor();
ProgressBar progress = new ProgressBar(PbStyle.DoubleLine, 100); ProgressBar progress = new ProgressBar(PbStyle.DoubleLine, 100);
lock (consoleLock) { SafeRefresh(progress, 0, $"Searching: {title}");
progress.Refresh(0, $"Searching: {title}");
}
Action<SearchResponse> responseHandler = (r) => Action<SearchResponse> responseHandler = (r) =>
{ {
if (r.Files.Count > 0) if (r.Files.Count > 0)
{ {
responses.Add(r); responses.TryAdd(r.Files.First().Filename, r);
if (!downloading) lock (downloadingLocker)
{ {
var f = r.Files.First(); if (!downloading)
if (preferredCond.FileSatisfies(f, track) && r.HasFreeUploadSlot && r.UploadSpeed / 1000000 >= 1)
{ {
downloading = true; var f = r.Files.First();
saveFilePath = GetSavePath(f, track); if (preferredCond.FileSatisfies(f, track) && r.HasFreeUploadSlot && r.UploadSpeed / 1000000 >= 1)
attemptedDownloadPref = true;
try
{ {
downloadTask = DownloadFile(r, f, saveFilePath, track, progress, cts); downloading = true;
} saveFilePath = GetSavePath(f, track);
catch attemptedDownloadPref = true;
{ try
saveFilePath = ""; {
downloading = false; Trace.TraceInformation($"Early download: {f.Filename}");
logFile?.Flush();
downloadTask = DownloadFile(r, f, saveFilePath, track, progress, cts);
}
catch
{
saveFilePath = "";
downloading = false;
}
} }
} }
} }
} }
}; };
lock (searches) { lock (searches)
searches[track] = new SearchInfo(searchQuery, responses, searchOptions, progress); searches[track] = new SearchInfo(searchQuery, responses, searchOptions, progress);
}
try try
{ {
await WaitForInternetConnection(); await WaitForInternetConnection();
var searchTasks = new List<Task>(); var searchTasks = new List<Task>();
Trace.TraceInformation("Search pos 1");
searchTasks.Add(client.SearchAsync(searchQuery, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(searchQuery, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler));
if (noDiacrSearch && title.RemoveDiacriticsIfExist(out string newSearch)) if (noDiacrSearch && title.RemoveDiacriticsIfExist(out string newSearch))
{ {
var searchQuery2 = SearchQuery.FromText(newSearch); var searchQuery2 = SearchQuery.FromText(newSearch);
Trace.TraceInformation("Search pos 2");
searchTasks.Add(client.SearchAsync(searchQuery2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(searchQuery2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler));
} }
@ -789,15 +817,14 @@ class Program
var searchQuery1 = SearchQuery.FromText($"{track.Album} {track.TrackTitle}"); var searchQuery1 = SearchQuery.FromText($"{track.Album} {track.TrackTitle}");
var searchQuery2 = SearchQuery.FromText($"{track.ArtistName} {track.Album}"); var searchQuery2 = SearchQuery.FromText($"{track.ArtistName} {track.Album}");
lock (consoleLock) SafeRefresh(progress, 0, $"Searching (album name): {title}");
{
progress.Refresh(0, $"Searching (album name): {title}");
}
try try
{ {
await WaitForInternetConnection(); await WaitForInternetConnection();
var searchTasks = new List<Task>(); var searchTasks = new List<Task>();
Trace.TraceInformation("Search pos 3, 4");
searchTasks.Add(client.SearchAsync(searchQuery1, options: searchOptions1, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(searchQuery1, options: searchOptions1, cancellationToken: cts.Token, responseHandler: responseHandler));
searchTasks.Add(client.SearchAsync(searchQuery2, options: searchOptions2, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(searchQuery2, options: searchOptions2, cancellationToken: cts.Token, responseHandler: responseHandler));
@ -805,11 +832,13 @@ class Program
{ {
if (searchQuery1.SearchText.RemoveDiacriticsIfExist(out string newSearch1)) if (searchQuery1.SearchText.RemoveDiacriticsIfExist(out string newSearch1))
{ {
Trace.TraceInformation("Search pos 5");
var searchQuery1_2 = SearchQuery.FromText(newSearch1); var searchQuery1_2 = SearchQuery.FromText(newSearch1);
searchTasks.Add(client.SearchAsync(searchQuery1_2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(searchQuery1_2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler));
} }
if (searchQuery2.SearchText.RemoveDiacriticsIfExist(out string newSearch2)) if (searchQuery2.SearchText.RemoveDiacriticsIfExist(out string newSearch2))
{ {
Trace.TraceInformation("Search pos 6");
var searchQuery2_2 = SearchQuery.FromText(newSearch2); var searchQuery2_2 = SearchQuery.FromText(newSearch2);
searchTasks.Add(client.SearchAsync(searchQuery2_2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(searchQuery2_2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler));
} }
@ -828,20 +857,21 @@ class Program
minimumPeerUploadSpeed: 1, searchTimeout: 8000, minimumPeerUploadSpeed: 1, searchTimeout: 8000,
fileFilter: (file) => { return IsMusicFile(file.Filename) && necessaryCond.FileSatisfies(file, track); } fileFilter: (file) => { return IsMusicFile(file.Filename) && necessaryCond.FileSatisfies(file, track); }
); );
lock (consoleLock)
{ SafeRefresh(progress, 0, $"Searching (no channel name): {searchText}");
progress.Refresh(0, $"Searching (no channel name): {searchText}");
}
try try
{ {
await WaitForInternetConnection(); await WaitForInternetConnection();
var searchTasks = new List<Task>(); var searchTasks = new List<Task>();
Trace.TraceInformation("Search pos 7");
searchTasks.Add(client.SearchAsync(SearchQuery.FromText(searchText), options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(SearchQuery.FromText(searchText), options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler));
if (noDiacrSearch && title.RemoveDiacriticsIfExist(out string newSearch)) if (noDiacrSearch && title.RemoveDiacriticsIfExist(out string newSearch))
{ {
var searchQuery2 = SearchQuery.FromText(newSearch); var searchQuery2 = SearchQuery.FromText(newSearch);
Trace.TraceInformation("Search pos 8");
searchTasks.Add(client.SearchAsync(searchQuery2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler)); searchTasks.Add(client.SearchAsync(searchQuery2, options: searchOptions, cancellationToken: cts.Token, responseHandler: responseHandler));
} }
@ -850,9 +880,11 @@ class Program
catch (OperationCanceledException ex) { } catch (OperationCanceledException ex) { }
} }
lock (searches) { searches.Remove(track); } searches.TryRemove(track, out _);
cts.Dispose(); cts.Dispose();
lock (downloadingLocker) { }
bool notFound = false; bool notFound = false;
if (!downloading && responses.Count == 0 && !useYtdlp) if (!downloading && responses.Count == 0 && !useYtdlp)
{ {
@ -874,7 +906,7 @@ class Program
if (!downloading && responses.Count > 0) if (!downloading && responses.Count > 0)
{ {
var fileResponses = responses var fileResponses = responses
.SelectMany(response => response.Files.Select(file => (response, file))) .SelectMany(kvp => kvp.Value.Files.Select(file => (response: kvp.Value, file)))
.OrderByDescending(x => preferredCond.LengthToleranceSatisfies(x.file, track.Length)) .OrderByDescending(x => preferredCond.LengthToleranceSatisfies(x.file, track.Length))
.ThenByDescending(x => preferredCond.BitrateSatisfies(x.file)) .ThenByDescending(x => preferredCond.BitrateSatisfies(x.file))
.ThenByDescending(x => preferredCond.FileSatisfies(x.file, track)) .ThenByDescending(x => preferredCond.FileSatisfies(x.file, track))
@ -888,10 +920,7 @@ class Program
bool pref = preferredCond.FileSatisfies(x.file, track); bool pref = preferredCond.FileSatisfies(x.file, track);
if (skipIfPrefFailed && attemptedDownloadPref && !pref) if (skipIfPrefFailed && attemptedDownloadPref && !pref)
{ {
lock (consoleLock) SafeRefresh(progress, 0, $"Pref. version of the file exists, but couldn't be downloaded: {track}, skipping");
{
progress.Refresh(0, $"Pref. version of the file exists, but couldn't be downloaded: {track}, skipping");
}
var failedDownloadInfo = $"{track} [Pref. version of the file exists, but couldn't be downloaded]"; var failedDownloadInfo = $"{track} [Pref. version of the file exists, but couldn't be downloaded]";
WriteLineOutputFile(failedDownloadInfo); WriteLineOutputFile(failedDownloadInfo);
return ""; return "";
@ -912,10 +941,7 @@ class Program
downloading = false; downloading = false;
if (--maxRetriesPerFile <= 0) if (--maxRetriesPerFile <= 0)
{ {
lock (consoleLock) SafeRefresh(progress, 0, $"Out of download retries: {track}, skipping");
{
progress.Refresh(0, $"Out of download retries: {track}, skipping");
}
var failedDownloadInfo = $"{track} [Out of download retries]"; var failedDownloadInfo = $"{track} [Out of download retries]";
WriteLineOutputFile(failedDownloadInfo); WriteLineOutputFile(failedDownloadInfo);
return ""; return "";
@ -928,10 +954,7 @@ class Program
{ {
notFound = false; notFound = false;
try { try {
lock (consoleLock) SafeRefresh(progress, 0, $"Not found, searching with yt-dlp: {track}");
{
progress.Refresh(0, $"Not found, searching with yt-dlp: {track}");
}
downloading = true; downloading = true;
string fname = GetSaveName(track); string fname = GetSaveName(track);
await YtdlpSearchAndDownload(track, necessaryCond, Path.Combine(outputFolder, fname), progress); await YtdlpSearchAndDownload(track, necessaryCond, Path.Combine(outputFolder, fname), progress);
@ -940,10 +963,7 @@ class Program
{ {
if (IsMusicFile(file)) if (IsMusicFile(file))
{ {
lock (consoleLock) SafeRefresh(progress, 100, $"yt-dlp: Completed download for {track}");
{
progress.Refresh(100, $"yt-dlp: Completed download for {track}");
}
saveFilePath = file; saveFilePath = file;
break; break;
} }
@ -956,10 +976,7 @@ class Program
downloading = false; downloading = false;
if (e.Message.Contains("No matching files found")) if (e.Message.Contains("No matching files found"))
notFound = true; notFound = true;
lock (consoleLock) SafeRefresh(progress, 0, $"{e.Message}");
{
progress.Refresh(0, $"{e.Message}");
}
} }
} }
@ -967,19 +984,13 @@ class Program
{ {
if (notFound) if (notFound)
{ {
lock (consoleLock) SafeRefresh(progress, 0, $"Not found: {track}, skipping");
{
progress.Refresh(0, $"Not found: {track}, skipping");
}
var failedDownloadInfo = $"{track} [No suitable file found]"; var failedDownloadInfo = $"{track} [No suitable file found]";
WriteLineOutputFile(failedDownloadInfo); WriteLineOutputFile(failedDownloadInfo);
} }
else else
{ {
lock (consoleLock) SafeRefresh(progress, 0, $"Failed to download: {track}, skipping");
{
progress.Refresh(0, $"Failed to download: {track}, skipping");
}
var failedDownloadInfo = $"{track} [All downloads failed]"; var failedDownloadInfo = $"{track} [All downloads failed]";
WriteLineOutputFile(failedDownloadInfo); WriteLineOutputFile(failedDownloadInfo);
} }
@ -991,6 +1002,8 @@ class Program
static async Task DownloadFile(SearchResponse response, Soulseek.File file, string filePath, Track track, ProgressBar progress, CancellationTokenSource? searchCts = null) static async Task DownloadFile(SearchResponse response, Soulseek.File file, string filePath, Track track, ProgressBar progress, CancellationTokenSource? searchCts = null)
{ {
Trace.TraceInformation($"Downloading {file.Filename}");
logFile.Flush();
System.IO.Directory.CreateDirectory(Path.GetDirectoryName(filePath)); System.IO.Directory.CreateDirectory(Path.GetDirectoryName(filePath));
bool transferSet = false; bool transferSet = false;
@ -1010,7 +1023,8 @@ class Program
using (var cts = new CancellationTokenSource()) using (var cts = new CancellationTokenSource())
using (var outputStream = new FileStream(filePath, FileMode.Create)) using (var outputStream = new FileStream(filePath, FileMode.Create))
{ {
lock (downloads) { downloads[file.Filename] = new DownloadWrapper(filePath, response, file, track, cts, progress); } lock (downloads)
downloads[file.Filename] = new DownloadWrapper(filePath, response, file, track, cts, progress);
try try
{ {
@ -1020,7 +1034,7 @@ class Program
catch (Exception e) catch (Exception e)
{ {
downloads[file.Filename].UpdateText(); downloads[file.Filename].UpdateText();
lock (downloads) { downloads.Remove(file.Filename); } downloads.TryRemove(file.Filename, out _);
try try
{ {
if (System.IO.File.Exists(filePath)) if (System.IO.File.Exists(filePath))
@ -1035,7 +1049,7 @@ class Program
catch { } catch { }
downloads[file.Filename].success = true; downloads[file.Filename].success = true;
downloads[file.Filename].UpdateText(); downloads[file.Filename].UpdateText();
lock (downloads) { downloads.Remove(file.Filename); } downloads.TryRemove(file.Filename, out _);
} }
static int totalCalls = 0; static int totalCalls = 0;
@ -1056,7 +1070,7 @@ class Program
foreach (var (key, val) in searches) foreach (var (key, val) in searches)
{ {
if (val == null) if (val == null)
lock (searches) { searches.Remove(key); } searches.TryRemove(key, out _);
} }
foreach (var (key, val) in downloads) foreach (var (key, val) in downloads)
@ -1066,23 +1080,18 @@ class Program
val.UpdateText(); val.UpdateText();
if (val.success) if (val.success)
lock (downloads) downloads.TryRemove(key, out _);
{
downloads.Remove(key);
}
else if ((DateTime.Now - val.UpdateLastChangeTime()).TotalMilliseconds > downloadMaxStaleTime) else if ((DateTime.Now - val.UpdateLastChangeTime()).TotalMilliseconds > downloadMaxStaleTime)
{ {
try { val.cts.Cancel(); } try { val.cts.Cancel(); }
catch { } catch { }
val.stalled = true; val.stalled = true;
val.UpdateText(); val.UpdateText();
lock (downloads) { downloads.Remove(key); } downloads.TryRemove(key, out _);
} }
} }
else else
{ downloads.TryRemove(key, out _);
lock (downloads) { downloads.Remove(key); }
}
} }
} }
@ -1123,10 +1132,7 @@ class Program
startInfo.FileName = "yt-dlp"; startInfo.FileName = "yt-dlp";
string search = $"{track.ArtistName} - {track.TrackTitle}"; string search = $"{track.ArtistName} - {track.TrackTitle}";
startInfo.Arguments = $"\"ytsearch3:{search}\" --print \"%(duration>%H:%M:%S)s ¦¦ %(id)s ¦¦ %(title)s\""; startInfo.Arguments = $"\"ytsearch3:{search}\" --print \"%(duration>%H:%M:%S)s ¦¦ %(id)s ¦¦ %(title)s\"";
lock (consoleLock) SafeRefresh(progress, 0, $"{startInfo.FileName} {startInfo.Arguments}");
{
progress.Refresh(0, $"{startInfo.FileName} {startInfo.Arguments}");
}
startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true; startInfo.RedirectStandardError = true;
@ -1178,10 +1184,7 @@ class Program
startInfo.FileName = "yt-dlp"; startInfo.FileName = "yt-dlp";
startInfo.Arguments = $"\"{id}\" -f {ytdlpFormat} -ci -o \"{savePathNoExt}.%(ext)s\" -x"; startInfo.Arguments = $"\"{id}\" -f {ytdlpFormat} -ci -o \"{savePathNoExt}.%(ext)s\" -x";
lock (consoleLock) SafeRefresh(progress, 0, $"yt-dlp \"{id}\" -f {ytdlpFormat} -ci -o \"{Path.GetFileNameWithoutExtension(savePathNoExt + ".m")}.%(ext)s\" -x");
{
progress.Refresh(0, $"yt-dlp \"{id}\" -f {ytdlpFormat} -ci -o \"{Path.GetFileNameWithoutExtension(savePathNoExt + ".m")}.%(ext)s\" -x");
}
startInfo.RedirectStandardOutput = true; startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true; startInfo.RedirectStandardError = true;
@ -1230,10 +1233,7 @@ class Program
$"[{file.Length}s{sampleRate}{bitRate}/{fileSize}]"; $"[{file.Length}s{sampleRate}{bitRate}/{fileSize}]";
this.progress = progress; this.progress = progress;
lock (consoleLock) SafeRefresh(progress, 0, displayText);
{
progress.Refresh(0, displayText);
}
} }
public string UpdateText() public string UpdateText()
@ -1267,11 +1267,8 @@ class Program
string txt = $"{bar}{state}:".PadRight(14, ' '); string txt = $"{bar}{state}:".PadRight(14, ' ');
lock (consoleLock) Console.ResetColor();
{ SafeRefresh(progress, (int)((percentage ?? 0) * 100), $"{txt} {displayText}");
Console.ResetColor();
progress.Refresh((int)((percentage ?? 0) * 100), $"{txt} {displayText}");
}
return progress.Line1 + "\n" + progress.Line2; return progress.Line1 + "\n" + progress.Line2;
} }
@ -1291,10 +1288,10 @@ class Program
{ {
public SearchQuery query; public SearchQuery query;
public SearchOptions searchOptions; public SearchOptions searchOptions;
public List<SearchResponse> responses; public ConcurrentDictionary<string, SearchResponse> responses;
public ProgressBar progress; public ProgressBar progress;
public SearchInfo(SearchQuery query, List<SearchResponse> responses, SearchOptions searchOptions, ProgressBar progress) public SearchInfo(SearchQuery query, ConcurrentDictionary<string, SearchResponse> responses, SearchOptions searchOptions, ProgressBar progress)
{ {
this.query = query; this.query = query;
this.responses = responses; this.responses = responses;
@ -1507,6 +1504,7 @@ class Program
var extension = Path.GetExtension(fileName).ToLower(); var extension = Path.GetExtension(fileName).ToLower();
return musicExtensions.Contains(extension); return musicExtensions.Contains(extension);
} }
static bool TrackExistsInCollection(Track track, FileConditions conditions, IEnumerable<string> collection, out string? foundPath) static bool TrackExistsInCollection(Track track, FileConditions conditions, IEnumerable<string> collection, out string? foundPath)
{ {
string[] ignore = new string[] { " ", "_", "-", ".", "(", ")" }; string[] ignore = new string[] { " ", "_", "-", ".", "(", ")" };
@ -1558,20 +1556,29 @@ class Program
static void WriteLineOutputFile(string line) static void WriteLineOutputFile(string line)
{ {
using (var fileStream = new FileStream(failsFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)) if (!writeFails)
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8)) return;
lock (failsFileLock)
{ {
streamWriter.WriteLine(line); using (var fileStream = new FileStream(failsFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8))
{
streamWriter.WriteLine(line);
}
} }
} }
static void WriteAllLinesOutputFile(string text) static void WriteAllLinesOutputFile(string text)
{ {
using (var fileStream = new FileStream(failsFilePath, FileMode.Create, FileAccess.Write, FileShare.Read)) if (!writeFails)
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8)) return;
lock (failsFileLock)
{ {
streamWriter.WriteLine(text); using (var fileStream = new FileStream(failsFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8))
{
streamWriter.WriteLine(text);
}
} }
} }
static string[] ParseCommand(string cmd) static string[] ParseCommand(string cmd)
{ {
@ -1603,6 +1610,11 @@ class Program
Console.WriteLine($"Track count: {tracks.Count}"); Console.WriteLine($"Track count: {tracks.Count}");
} }
static void SafeRefresh(ProgressBar progress, int current, string item)
{
lock (consoleLock)
progress.Refresh(current, item);
}
public static async Task WaitForInternetConnection() public static async Task WaitForInternetConnection()
{ {