mirror of
https://github.com/fiso64/slsk-batchdl.git
synced 2024-12-22 22:42:41 +00:00
stuff
This commit is contained in:
parent
3e688192ab
commit
134e8ce442
2 changed files with 122 additions and 108 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
lock (consoleLock)
|
||||||
|
{
|
||||||
Console.ForegroundColor = ConsoleColor.Blue;
|
Console.ForegroundColor = ConsoleColor.Blue;
|
||||||
Console.WriteLine($"\nSuccesses: {successCount}, fails: {failCount}, tracks left: {tracksRemaining}\n");
|
Console.WriteLine($"\nSuccesses: {successCount}, fails: {failCount}, tracks left: {tracksRemaining}\n");
|
||||||
Console.ResetColor();
|
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,21 +734,22 @@ 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);
|
||||||
|
lock (downloadingLocker)
|
||||||
|
{
|
||||||
if (!downloading)
|
if (!downloading)
|
||||||
{
|
{
|
||||||
var f = r.Files.First();
|
var f = r.Files.First();
|
||||||
|
@ -736,6 +760,8 @@ class Program
|
||||||
attemptedDownloadPref = true;
|
attemptedDownloadPref = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
Trace.TraceInformation($"Early download: {f.Filename}");
|
||||||
|
logFile?.Flush();
|
||||||
downloadTask = DownloadFile(r, f, saveFilePath, track, progress, cts);
|
downloadTask = DownloadFile(r, f, saveFilePath, track, progress, cts);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
@ -746,21 +772,23 @@ class Program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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();
|
Console.ResetColor();
|
||||||
progress.Refresh((int)((percentage ?? 0) * 100), $"{txt} {displayText}");
|
SafeRefresh(progress, (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[] { " ", "_", "-", ".", "(", ")" };
|
||||||
|
@ -1557,6 +1555,10 @@ class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteLineOutputFile(string line)
|
static void WriteLineOutputFile(string line)
|
||||||
|
{
|
||||||
|
if (!writeFails)
|
||||||
|
return;
|
||||||
|
lock (failsFileLock)
|
||||||
{
|
{
|
||||||
using (var fileStream = new FileStream(failsFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
|
using (var fileStream = new FileStream(failsFilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
|
||||||
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8))
|
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8))
|
||||||
|
@ -1564,14 +1566,19 @@ class Program
|
||||||
streamWriter.WriteLine(line);
|
streamWriter.WriteLine(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
static void WriteAllLinesOutputFile(string text)
|
static void WriteAllLinesOutputFile(string text)
|
||||||
|
{
|
||||||
|
if (!writeFails)
|
||||||
|
return;
|
||||||
|
lock (failsFileLock)
|
||||||
{
|
{
|
||||||
using (var fileStream = new FileStream(failsFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
using (var fileStream = new FileStream(failsFilePath, FileMode.Create, FileAccess.Write, FileShare.Read))
|
||||||
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8))
|
using (var streamWriter = new StreamWriter(fileStream, System.Text.Encoding.UTF8))
|
||||||
{
|
{
|
||||||
streamWriter.WriteLine(text);
|
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()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue