mirror of
https://github.com/fiso64/slsk-batchdl.git
synced 2024-12-22 22:42:41 +00:00
commit
This commit is contained in:
parent
27ec0c30b3
commit
6c70442449
3 changed files with 52 additions and 29 deletions
|
@ -242,4 +242,4 @@ Create a file named `slsk-batchdl.conf` in the same directory as the executable
|
||||||
## Notes
|
## Notes
|
||||||
- For macOS builds you can use publish.sh to build the app. Download dotnet from https://dotnet.microsoft.com/en-us/download/dotnet/6.0, then run `chmod +x publish.sh && sh publish.sh`
|
- For macOS builds you can use publish.sh to build the app. Download dotnet from https://dotnet.microsoft.com/en-us/download/dotnet/6.0, then run `chmod +x publish.sh && sh publish.sh`
|
||||||
- `--display single` and especially `double` can cause the printed lines to be duplicated or overwritten on some configurations. Use `simple` if that's an issue.
|
- `--display single` and especially `double` can cause the printed lines to be duplicated or overwritten on some configurations. Use `simple` if that's an issue.
|
||||||
- The server will ban you for 30 minutes if too many searches are performed within a short timespan. The program has a search limiter which can be adjusted with `--searches-per-time` and `--searches-renew-time` (when limit is reached, the status of the downloads will be "Waiting"). By default it is configured to allow up to 34 searches every 220 seconds. These values were determined through experimentation as unfortunately I couldn't find any information regarding soulseek's rate limits, so they may be incorrect. You can also use `--random-login` to re-login with a random username and password automatically.
|
- The server will ban you for 30 minutes if too many searches are performed within a short timespan. The program has a search limiter which can be adjusted with `--searches-per-time` and `--searches-renew-time` (when limit is reached, the status of the downloads will be "Waiting"). By default it is configured to allow up to 34 searches every 220 seconds. These values were determined through experimentation as unfortunately I couldn't find any information regarding soulseek's rate limits, so they may be incorrect.
|
||||||
|
|
|
@ -559,6 +559,8 @@ static class Program
|
||||||
case "--max-stale-time":
|
case "--max-stale-time":
|
||||||
downloadMaxStaleTime = int.Parse(args[++i]);
|
downloadMaxStaleTime = int.Parse(args[++i]);
|
||||||
break;
|
break;
|
||||||
|
case "--processes":
|
||||||
|
case "--concurrent-processes":
|
||||||
case "--concurrent-downloads":
|
case "--concurrent-downloads":
|
||||||
maxConcurrentProcesses = int.Parse(args[++i]);
|
maxConcurrentProcesses = int.Parse(args[++i]);
|
||||||
break;
|
break;
|
||||||
|
@ -1167,7 +1169,7 @@ static class Program
|
||||||
if (tracks.Count > 0)
|
if (tracks.Count > 0)
|
||||||
{
|
{
|
||||||
bool showAll = type != TrackLists.ListType.Normal || debugPrintTracks;
|
bool showAll = type != TrackLists.ListType.Normal || debugPrintTracks;
|
||||||
PrintTracks(tracks, showAll ? int.MaxValue : 10, debugPrintTracksFull);
|
PrintTracks(tracks, showAll ? int.MaxValue : 10, debugPrintTracksFull, infoFirst: debugPrintTracks);
|
||||||
if (debugPrintTracksFull && (existing.Count > 0 || notFound.Count > 0))
|
if (debugPrintTracksFull && (existing.Count > 0 || notFound.Count > 0))
|
||||||
Console.WriteLine("\n-----------------------------------------------\n");
|
Console.WriteLine("\n-----------------------------------------------\n");
|
||||||
}
|
}
|
||||||
|
@ -1177,12 +1179,12 @@ static class Program
|
||||||
if (existing.Count > 0)
|
if (existing.Count > 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"\nThe following tracks already exist:");
|
Console.WriteLine($"\nThe following tracks already exist:");
|
||||||
PrintTracks(existing, fullInfo: debugPrintTracksFull);
|
PrintTracks(existing, fullInfo: debugPrintTracksFull, infoFirst: debugPrintTracks);
|
||||||
}
|
}
|
||||||
if (notFound.Count > 0)
|
if (notFound.Count > 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"\nThe following tracks were not found during the last run:");
|
Console.WriteLine($"\nThe following tracks were not found during the last run:");
|
||||||
PrintTracks(notFound, fullInfo: debugPrintTracksFull);
|
PrintTracks(notFound, fullInfo: debugPrintTracksFull, infoFirst: debugPrintTracks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1203,7 +1205,7 @@ static class Program
|
||||||
d.ToList().ForEach(x => existing.TryAdd(x.Key, x.Value));
|
d.ToList().ForEach(x => existing.TryAdd(x.Key, x.Value));
|
||||||
}
|
}
|
||||||
else if (musicDir != "" && !System.IO.Directory.Exists(musicDir))
|
else if (musicDir != "" && !System.IO.Directory.Exists(musicDir))
|
||||||
if (print) Console.WriteLine($"Path does not exist: {musicDir}");
|
if (print) Console.WriteLine($"Musid dir does not exist: {musicDir}");
|
||||||
|
|
||||||
return existing.Select(x => x.Key).ToList();
|
return existing.Select(x => x.Key).ToList();
|
||||||
}
|
}
|
||||||
|
@ -1233,7 +1235,8 @@ static class Program
|
||||||
{
|
{
|
||||||
SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrentProcesses);
|
SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrentProcesses);
|
||||||
|
|
||||||
var downloadTasks = tracks.Select(async (track, index) =>
|
var copy = new List<Track>(tracks);
|
||||||
|
var downloadTasks = copy.Select(async (track, index) =>
|
||||||
{
|
{
|
||||||
if (track.TrackState == Track.State.Exists || track.TrackState == Track.State.NotFoundLastTime)
|
if (track.TrackState == Track.State.Exists || track.TrackState == Track.State.NotFoundLastTime)
|
||||||
return;
|
return;
|
||||||
|
@ -1294,10 +1297,11 @@ static class Program
|
||||||
mainLoopCts = new CancellationTokenSource();
|
mainLoopCts = new CancellationTokenSource();
|
||||||
albumCommonPath = Utils.GreatestCommonPath(tracks.SelectMany(x => x.Downloads.Select(y => y.Value.Item2.Filename)), dirsep: '\\');
|
albumCommonPath = Utils.GreatestCommonPath(tracks.SelectMany(x => x.Downloads.Select(y => y.Value.Item2.Filename)), dirsep: '\\');
|
||||||
SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrentProcesses);
|
SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrentProcesses);
|
||||||
|
var copy = new List<Track>(tracks);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var downloadTasks = tracks.Select(async (track, index) =>
|
var downloadTasks = copy.Select(async (track, index) =>
|
||||||
{
|
{
|
||||||
if (track.TrackState == Track.State.Exists || track.TrackState == Track.State.NotFoundLastTime)
|
if (track.TrackState == Track.State.Exists || track.TrackState == Track.State.NotFoundLastTime)
|
||||||
return;
|
return;
|
||||||
|
@ -1627,7 +1631,7 @@ static class Program
|
||||||
{
|
{
|
||||||
foreach (var (response, file) in fileResponses) {
|
foreach (var (response, file) in fileResponses) {
|
||||||
Console.WriteLine(DisplayString(track, file, response,
|
Console.WriteLine(DisplayString(track, file, response,
|
||||||
(printResultsFull ? necessaryCond : null), (printResultsFull ? preferredCond : null), printResultsFull));
|
(printResultsFull ? necessaryCond : null), (printResultsFull ? preferredCond : null), printResultsFull, infoFirst: true));
|
||||||
}
|
}
|
||||||
WriteLine($"Total: {fileResponses.Count()}\n", ConsoleColor.Yellow);
|
WriteLine($"Total: {fileResponses.Count()}\n", ConsoleColor.Yellow);
|
||||||
return "";
|
return "";
|
||||||
|
@ -2200,7 +2204,9 @@ static class Program
|
||||||
filename = GetFileNameWithoutExtSlsk(filename).Replace(" — ", " - ").Replace("_", " ").RemoveConsecutiveWs().Trim();
|
filename = GetFileNameWithoutExtSlsk(filename).Replace(" — ", " - ").Replace("_", " ").RemoveConsecutiveWs().Trim();
|
||||||
|
|
||||||
var trackNumStart = new Regex(@"^(?:(?:[0-9][-\.])?\d{2,3}[. -]|\b\d\.\s|\b\d\s-\s)(?=.+\S)");
|
var trackNumStart = new Regex(@"^(?:(?:[0-9][-\.])?\d{2,3}[. -]|\b\d\.\s|\b\d\s-\s)(?=.+\S)");
|
||||||
|
//var trackNumMiddle = new Regex(@"\s+-\s+(\d{2,3})(?: -|\.|)\s+|\s+-(\d{2,3})-\s+");
|
||||||
var trackNumMiddle = new Regex(@"(?<= - )((\d-)?\d{2,3}|\d{2,3}\.?)\s+");
|
var trackNumMiddle = new Regex(@"(?<= - )((\d-)?\d{2,3}|\d{2,3}\.?)\s+");
|
||||||
|
var trackNumMiddleAlt = new Regex(@"\s+-(\d{2,3})-\s+");
|
||||||
|
|
||||||
if (trackNumStart.IsMatch(filename))
|
if (trackNumStart.IsMatch(filename))
|
||||||
{
|
{
|
||||||
|
@ -2210,10 +2216,14 @@ static class Program
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
filename = trackNumMiddle.Replace(filename, "<<tracknum>>", 1).Trim();
|
var reg = trackNumMiddle.IsMatch(filename) ? trackNumMiddle : (trackNumMiddleAlt.IsMatch(filename) ? trackNumMiddleAlt : null);
|
||||||
|
if (reg != null && !reg.IsMatch(defaultTrack.ToString(noInfo: true)))
|
||||||
|
{
|
||||||
|
filename = reg.Replace(filename, "<<tracknum>>", 1).Trim();
|
||||||
filename = Regex.Replace(filename, @"-\s*<<tracknum>>\s*-", "-");
|
filename = Regex.Replace(filename, @"-\s*<<tracknum>>\s*-", "-");
|
||||||
filename = filename.Replace("<<tracknum>>", "");
|
filename = filename.Replace("<<tracknum>>", "");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string aname = t.ArtistName.Trim();
|
string aname = t.ArtistName.Trim();
|
||||||
string tname = t.TrackTitle.Trim();
|
string tname = t.TrackTitle.Trim();
|
||||||
|
@ -2376,7 +2386,7 @@ static class Program
|
||||||
{
|
{
|
||||||
if (client.State.HasFlag(SoulseekClientStates.LoggedIn))
|
if (client.State.HasFlag(SoulseekClientStates.LoggedIn))
|
||||||
{
|
{
|
||||||
foreach (var (key, val) in searches)
|
foreach (var (key, val) in searches) // shouldn't this give "collection was modified" errors? whatever..
|
||||||
{
|
{
|
||||||
if (val == null)
|
if (val == null)
|
||||||
searches.TryRemove(key, out _);
|
searches.TryRemove(key, out _);
|
||||||
|
@ -3465,17 +3475,27 @@ static class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
static string DisplayString(Track t, Soulseek.File? file=null, SearchResponse? response=null, FileConditions? nec=null,
|
static string DisplayString(Track t, Soulseek.File? file=null, SearchResponse? response=null, FileConditions? nec=null,
|
||||||
FileConditions? pref=null, bool fullpath=false, string customPath="")
|
FileConditions? pref=null, bool fullpath=false, string customPath="", bool infoFirst=false)
|
||||||
{
|
{
|
||||||
if (file == null)
|
if (file == null)
|
||||||
return t.ToString();
|
return t.ToString();
|
||||||
|
|
||||||
string sampleRate = file.SampleRate.HasValue ? $"{file.SampleRate}Hz/" : "";
|
string sampleRate = file.SampleRate.HasValue ? $"{file.SampleRate}Hz" : "";
|
||||||
string bitRate = file.BitRate.HasValue ? $"{file.BitRate}kbps/" : "";
|
string bitRate = file.BitRate.HasValue ? $"{file.BitRate}kbps" : "";
|
||||||
string fileSize = $"{file.Size / (float)(1024 * 1024):F1}MB";
|
string fileSize = $"{file.Size / (float)(1024 * 1024):F1}MB";
|
||||||
string fname = fullpath ? "\\" + file.Filename : "\\..\\" + (customPath == "" ? GetFileNameSlsk(file.Filename) : customPath);
|
string fname = fullpath ? "\\" + file.Filename : "\\..\\" + (customPath == "" ? GetFileNameSlsk(file.Filename) : customPath);
|
||||||
string length = Utils.IsMusicFile(file.Filename) ? (file.Length ?? -1).ToString() + "s/" : "";
|
string length = Utils.IsMusicFile(file.Filename) ? (file.Length ?? -1).ToString() + "s" : "";
|
||||||
string displayText = $"{response?.Username ?? ""}{fname} [{length}{sampleRate}{bitRate}{fileSize}]";
|
string displayText;
|
||||||
|
if (!infoFirst)
|
||||||
|
{
|
||||||
|
string info = string.Join('/', new string[] { length, sampleRate+bitRate, fileSize }.Where(value => value!=""));
|
||||||
|
displayText = $"{response?.Username ?? ""}{fname} [{info}]";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string info = string.Join('/', new string[] { length.PadRight(4), (sampleRate+bitRate).PadRight(8), fileSize.PadLeft(6) });
|
||||||
|
displayText = $"[{info}] {response?.Username ?? ""}{fname}";
|
||||||
|
}
|
||||||
|
|
||||||
string necStr = nec != null ? $"nec:{nec.GetNotSatisfiedName(file, t, response)}, " : "";
|
string necStr = nec != null ? $"nec:{nec.GetNotSatisfiedName(file, t, response)}, " : "";
|
||||||
string prefStr = pref != null ? $"prf:{pref.GetNotSatisfiedName(file, t, response)}" : "";
|
string prefStr = pref != null ? $"prf:{pref.GetNotSatisfiedName(file, t, response)}" : "";
|
||||||
|
@ -3486,7 +3506,7 @@ static class Program
|
||||||
return displayText + cond;
|
return displayText + cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PrintTracks(List<Track> tracks, int number = int.MaxValue, bool fullInfo=false, bool pathsOnly=false, bool showAncestors=false)
|
static void PrintTracks(List<Track> tracks, int number = int.MaxValue, bool fullInfo=false, bool pathsOnly=false, bool showAncestors=false, bool infoFirst=false)
|
||||||
{
|
{
|
||||||
number = Math.Min(tracks.Count, number);
|
number = Math.Min(tracks.Count, number);
|
||||||
|
|
||||||
|
@ -3502,9 +3522,9 @@ static class Program
|
||||||
foreach (var x in tracks[i].Downloads)
|
foreach (var x in tracks[i].Downloads)
|
||||||
{
|
{
|
||||||
if (ancestor == "")
|
if (ancestor == "")
|
||||||
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1));
|
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, infoFirst: infoFirst));
|
||||||
else
|
else
|
||||||
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, customPath: x.Value.Item2.Filename.Replace(ancestor, "")));
|
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, customPath: x.Value.Item2.Filename.Replace(ancestor, ""), infoFirst: infoFirst));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3535,9 +3555,9 @@ static class Program
|
||||||
Console.WriteLine($" Shares: {tracks[i].Downloads.Count}");
|
Console.WriteLine($" Shares: {tracks[i].Downloads.Count}");
|
||||||
foreach (var x in tracks[i].Downloads) {
|
foreach (var x in tracks[i].Downloads) {
|
||||||
if (ancestor == "")
|
if (ancestor == "")
|
||||||
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1));
|
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, infoFirst: infoFirst));
|
||||||
else
|
else
|
||||||
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, customPath: x.Value.Item2.Filename.Replace(ancestor, "")));
|
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, customPath: x.Value.Item2.Filename.Replace(ancestor, ""), infoFirst: infoFirst));
|
||||||
}
|
}
|
||||||
if (tracks[i].Downloads?.Count > 0) Console.WriteLine();
|
if (tracks[i].Downloads?.Count > 0) Console.WriteLine();
|
||||||
}
|
}
|
||||||
|
@ -3548,9 +3568,9 @@ static class Program
|
||||||
Console.WriteLine($" Shares: {tracks[i].Downloads.Count}");
|
Console.WriteLine($" Shares: {tracks[i].Downloads.Count}");
|
||||||
foreach (var x in tracks[i].Downloads) {
|
foreach (var x in tracks[i].Downloads) {
|
||||||
if (ancestor == "")
|
if (ancestor == "")
|
||||||
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1));
|
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, infoFirst: infoFirst));
|
||||||
else
|
else
|
||||||
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, customPath: x.Value.Item2.Filename.Replace(ancestor, "")));
|
Console.WriteLine(" " + DisplayString(tracks[i], x.Value.Item2, x.Value.Item1, customPath: x.Value.Item2.Filename.Replace(ancestor, ""), infoFirst: infoFirst));
|
||||||
}
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
}
|
}
|
||||||
|
@ -3941,7 +3961,7 @@ public class M3UEditor
|
||||||
var value = lastBracketIndex != line.Length ? line.Substring(lastBracketIndex + 1).Trim().TrimEnd(']') : "";
|
var value = lastBracketIndex != line.Length ? line.Substring(lastBracketIndex + 1).Trim().TrimEnd(']') : "";
|
||||||
return new { Key = key, Value = value };
|
return new { Key = key, Value = value };
|
||||||
})
|
})
|
||||||
.ToDictionary(pair => pair.Key, pair => pair.Value);
|
.ToSafeDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
|
|
|
@ -28,11 +28,14 @@ public static class Utils
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Move(string sourceFilePath, string destinationFilePath)
|
public static void Move(string sourceFilePath, string destinationFilePath)
|
||||||
|
{
|
||||||
|
if (File.Exists(sourceFilePath))
|
||||||
{
|
{
|
||||||
if (File.Exists(destinationFilePath))
|
if (File.Exists(destinationFilePath))
|
||||||
File.Delete(destinationFilePath);
|
File.Delete(destinationFilePath);
|
||||||
File.Move(sourceFilePath, destinationFilePath);
|
File.Move(sourceFilePath, destinationFilePath);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static bool EqualsAny(this string input, string[] values, StringComparison comparison = StringComparison.Ordinal)
|
public static bool EqualsAny(this string input, string[] values, StringComparison comparison = StringComparison.Ordinal)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue