mirror of
https://github.com/fiso64/slsk-batchdl.git
synced 2024-12-22 14:32:40 +00:00
commit
This commit is contained in:
parent
c5d11b995d
commit
8cf3016e6b
7 changed files with 79 additions and 39 deletions
10
README.md
10
README.md
|
@ -23,6 +23,7 @@ See the [examples](#examples-1).
|
|||
- [Configuration](#configuration)
|
||||
- [Examples](#examples-1)
|
||||
- [Notes](#notes)
|
||||
- [Docker](#docker)
|
||||
|
||||
|
||||
## Options
|
||||
|
@ -48,7 +49,7 @@ Usage: sldl <input> [OPTIONS]
|
|||
-o, --offset <offset> Skip a specified number of tracks
|
||||
-r, --reverse Download tracks in reverse order
|
||||
-c, --config <path> Set config file location. Set to 'none' to ignore config
|
||||
--profile <name> Configuration profile to use. See --help "config".
|
||||
--profile <names> Configuration profile(s) to use. See --help "config".
|
||||
--concurrent-downloads <num> Max concurrent downloads (default: 2)
|
||||
--m3u <option> Create an m3u8 playlist file in the output directory
|
||||
'none' (default for single inputs): Do not create
|
||||
|
@ -78,14 +79,15 @@ Usage: sldl <input> [OPTIONS]
|
|||
--debug Print extra debug info
|
||||
|
||||
--listen-port <port> Port for incoming connections (default: 49998)
|
||||
--on-complete <command> Run a specified command whenever a file is downloaded.
|
||||
--on-complete <command> Run a command whenever a file is downloaded.
|
||||
Available placeholders: {path} (local save path), {title},
|
||||
{artist},{album},{uri},{length},{failure-reason},{state}.
|
||||
Prepend a state number to only download in specific cases:
|
||||
1:, 2:, 3:, 4: for the Downloaded, Failed, Exists, and
|
||||
NotFoundLastTime states respectively.
|
||||
E.g: '1:<cmd>' will only run the command if the file is
|
||||
downloaded successfully.
|
||||
downloaded successfully. Prepend 's:' to use the system
|
||||
shell to execute the command.
|
||||
```
|
||||
```
|
||||
Searching
|
||||
|
@ -420,7 +422,7 @@ tag1 is null, use tag2. String literals enclosed in parentheses are ignored in t
|
|||
- "{artist( - )title|filename}"
|
||||
If artist and title are not null, name it 'Artist - Title', otherwise use the original
|
||||
filename.
|
||||
- "{artist(/)album(/)track(. )title|(missing-tags/)filename}"
|
||||
- "{albumartist(/)album(/)track(. )title|(missing-tags/)filename}"
|
||||
Sort files into artist/album folders if all tags are present, otherwise put them in
|
||||
the 'missing-tags' folder.
|
||||
|
||||
|
|
8
changelog.md
Normal file
8
changelog.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
- configuration profiles, auto-profiles
|
||||
- album aggregate download mode
|
||||
- automatically browse user shares during album downloads to guarantee that all files from the parent folder are downloaded
|
||||
- skip-existing is now much faster. Default mode is now m3u as it is more reliable. Changed how m3u skip-existing works (not backward compatible). Added `m3u-cond` skip mode. m3u modes can now skip album downloads.
|
||||
- can now download entire bandcamp artist discographies
|
||||
- added `--on-complete` to run a command whenever a file is downloaded
|
||||
- now also looks in the user config directories for `sldl.conf`
|
||||
- many bug fixes
|
|
@ -173,7 +173,7 @@ static class Config
|
|||
ApplyAutoProfiles();
|
||||
}
|
||||
|
||||
ApplyProfile(profile);
|
||||
ApplyProfiles(profile);
|
||||
|
||||
ProcessArgs(args);
|
||||
|
||||
|
@ -326,24 +326,27 @@ static class Config
|
|||
{
|
||||
//appliedProfiles.Clear();
|
||||
appliedProfiles.UnionWith(newProfiles);
|
||||
ApplyProfile(profile);
|
||||
ApplyProfiles(profile);
|
||||
ProcessArgs(arguments);
|
||||
PostProcessArgs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ApplyProfile(string name)
|
||||
static void ApplyProfiles(string names)
|
||||
{
|
||||
if (name.Length > 0 && name != "default")
|
||||
foreach (var name in names.Split(','))
|
||||
{
|
||||
if (profiles.ContainsKey(name))
|
||||
if (name.Length > 0 && name != "default")
|
||||
{
|
||||
ProcessArgs(profiles[name].args);
|
||||
appliedProfiles.Add(name);
|
||||
if (profiles.ContainsKey(name))
|
||||
{
|
||||
ProcessArgs(profiles[name].args);
|
||||
appliedProfiles.Add(name);
|
||||
}
|
||||
else
|
||||
Console.WriteLine($"Error: No profile '{name}' found in config");
|
||||
}
|
||||
else
|
||||
Console.WriteLine($"Error: No profile '{name}' found in config");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,7 +362,7 @@ static class Config
|
|||
{
|
||||
if (key == "default" || appliedProfiles.Contains(key))
|
||||
continue;
|
||||
if (key != profile && val.cond != null && ProfileConditionSatisfied(val.cond, tle))
|
||||
if (val.cond != null && ProfileConditionSatisfied(val.cond, tle))
|
||||
{
|
||||
Console.WriteLine($"Applying auto profile: {key}");
|
||||
ProcessArgs(val.args);
|
||||
|
|
|
@ -40,14 +40,14 @@ namespace Extractors
|
|||
|
||||
if (Config.input == "spotify-likes")
|
||||
{
|
||||
Console.WriteLine("Loading Spotify likes");
|
||||
Console.WriteLine("Loading Spotify likes..");
|
||||
var tracks = await spotifyClient.GetLikes(max, off);
|
||||
playlistName = "Spotify Likes";
|
||||
tle.list.Add(tracks);
|
||||
}
|
||||
else if (Config.input.Contains("/album/"))
|
||||
{
|
||||
Console.WriteLine("Loading Spotify album");
|
||||
Console.WriteLine("Loading Spotify album..");
|
||||
(var source, var tracks) = await spotifyClient.GetAlbum(Config.input);
|
||||
playlistName = source.ToString(noInfo: true);
|
||||
tle.source = source;
|
||||
|
@ -60,7 +60,7 @@ namespace Extractors
|
|||
}
|
||||
else if (Config.input.Contains("/artist/"))
|
||||
{
|
||||
Console.WriteLine("Loading spotify artist");
|
||||
Console.WriteLine("Loading spotify artist..");
|
||||
Console.WriteLine("Error: Spotify artist download currently not supported.");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
@ -203,12 +203,12 @@ namespace Extractors
|
|||
{
|
||||
if (_clientToken.Length != 0)
|
||||
{
|
||||
Console.WriteLine("Testing Spotify access with existing token...");
|
||||
//Console.WriteLine("Testing Spotify access with existing token...");
|
||||
var client = new SpotifyClient(_clientToken);
|
||||
try
|
||||
{
|
||||
var me = await client.UserProfile.Current();
|
||||
Console.WriteLine("Spotify access is good!");
|
||||
//Console.WriteLine("Spotify access is good!");
|
||||
_client = client;
|
||||
return true;
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ namespace Extractors
|
|||
{
|
||||
var oauthClient = new OAuthClient();
|
||||
var refreshResponse = await oauthClient.RequestToken(refreshRequest);
|
||||
Console.WriteLine($"We got a new refreshed access token from server: {refreshResponse.AccessToken}");
|
||||
//Console.WriteLine($"We got a new refreshed access token from server: {refreshResponse.AccessToken}");
|
||||
_clientToken = refreshResponse.AccessToken;
|
||||
_client = new SpotifyClient(_clientToken);
|
||||
return true;
|
||||
|
|
|
@ -27,7 +27,7 @@ public static class Help
|
|||
-o, --offset <offset> Skip a specified number of tracks
|
||||
-r, --reverse Download tracks in reverse order
|
||||
-c, --config <path> Set config file location. Set to 'none' to ignore config
|
||||
--profile <name> Configuration profile to use. See --help ""config"".
|
||||
--profile <names> Configuration profile(s) to use. See --help ""config"".
|
||||
--concurrent-downloads <num> Max concurrent downloads (default: 2)
|
||||
--m3u <option> Create an m3u8 playlist file in the output directory
|
||||
'none' (default for single inputs): Do not create
|
||||
|
@ -57,14 +57,15 @@ public static class Help
|
|||
--debug Print extra debug info
|
||||
|
||||
--listen-port <port> Port for incoming connections (default: 49998)
|
||||
--on-complete <command> Run a specified command whenever a file is downloaded.
|
||||
--on-complete <command> Run a command whenever a file is downloaded.
|
||||
Available placeholders: {path} (local save path), {title},
|
||||
{artist},{album},{uri},{length},{failure-reason},{state}.
|
||||
Prepend a state number to only download in specific cases:
|
||||
1:, 2:, 3:, 4: for the Downloaded, Failed, Exists, and
|
||||
NotFoundLastTime states respectively.
|
||||
E.g: '1:<cmd>' will only run the command if the file is
|
||||
downloaded successfully.
|
||||
downloaded successfully. Prepend 's:' to use the system
|
||||
shell to execute the command.
|
||||
|
||||
Searching
|
||||
--fast-search Begin downloading as soon as a file satisfying the preferred
|
||||
|
@ -393,7 +394,7 @@ public static class Help
|
|||
""{artist( - )title|filename}""
|
||||
If artist and title are not null, name it 'Artist - Title', otherwise use the original
|
||||
filename.
|
||||
""{artist(/)album(/)track(. )title|(missing-tags/)filename}""
|
||||
""{albumartist(/)album(/)track(. )title|(missing-tags/)filename}""
|
||||
Sort files into artist/album folders if all tags are present, otherwise put them in
|
||||
the 'missing-tags' folder.
|
||||
|
||||
|
|
|
@ -258,7 +258,6 @@ public static class Printing
|
|||
|
||||
WriteLine($"User : {userInfo}\nFolder: {parents}\nProps : {props}", ConsoleColor.White);
|
||||
PrintTracks(albumTracks.ToList(), pathsOnly: true, showAncestors: false, showUser: false);
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -432,7 +432,7 @@ static partial class Program
|
|||
bool succeeded = false;
|
||||
string? soulseekDir = null;
|
||||
|
||||
while (tle.list.Count > 0)
|
||||
while (tle.list.Count > 0 && !Config.albumArtOnly)
|
||||
{
|
||||
int index = 0;
|
||||
bool wasInteractive = Config.interactiveMode;
|
||||
|
@ -531,7 +531,7 @@ static partial class Program
|
|||
|
||||
int[]? sortedLengths = null;
|
||||
|
||||
if (chosenAlbum != null && chosenAlbum.Count(t => !t.IsNotAudio) > 0)
|
||||
if (chosenAlbum != null && chosenAlbum.Any(t => !t.IsNotAudio))
|
||||
sortedLengths = chosenAlbum.Where(t => !t.IsNotAudio).Select(t => t.Length).OrderBy(x => x).ToArray();
|
||||
|
||||
var albumArts = downloads
|
||||
|
@ -556,7 +556,9 @@ static partial class Program
|
|||
{
|
||||
mSize = chosenAlbum
|
||||
.Where(t => t.State == TrackState.Downloaded && Utils.IsImageFile(t.DownloadPath))
|
||||
.Max(t => t.FirstDownload.Size);
|
||||
.Select(t => t.FirstDownload.Size)
|
||||
.DefaultIfEmpty(0)
|
||||
.Max();
|
||||
}
|
||||
}
|
||||
else if (option == AlbumArtOption.Most)
|
||||
|
@ -772,6 +774,7 @@ static partial class Program
|
|||
}
|
||||
|
||||
PrintAlbum(tracks);
|
||||
Console.WriteLine();
|
||||
|
||||
string userInput = interactiveModeLoop().Trim();
|
||||
switch (userInput)
|
||||
|
@ -913,15 +916,30 @@ static partial class Program
|
|||
{
|
||||
if (onComplete.Length == 0)
|
||||
return;
|
||||
else if (onComplete.Length > 2 && onComplete[0].IsDigit() && onComplete[1] == ':')
|
||||
|
||||
bool useShellExecute = false;
|
||||
int count = 0;
|
||||
|
||||
while (onComplete.Length > 2 && count++ < 2)
|
||||
{
|
||||
if ((int)track.State != int.Parse(onComplete[0].ToString()))
|
||||
return;
|
||||
if (onComplete[0] == 's' && onComplete[1] == ':')
|
||||
{
|
||||
useShellExecute = true;
|
||||
}
|
||||
else if (onComplete[0].IsDigit() && onComplete[1] == ':')
|
||||
{
|
||||
if ((int)track.State != int.Parse(onComplete[0].ToString()))
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
onComplete = onComplete[2..];
|
||||
}
|
||||
|
||||
Process process = new Process();
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo();
|
||||
var process = new Process();
|
||||
var startInfo = new ProcessStartInfo();
|
||||
|
||||
onComplete = onComplete.Replace("{title}", track.Title)
|
||||
.Replace("{artist}", track.Artist)
|
||||
|
@ -957,16 +975,25 @@ static partial class Program
|
|||
startInfo.Arguments = parts.Length > 1 ? parts[1] : "";
|
||||
}
|
||||
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
startInfo.RedirectStandardError = true;
|
||||
startInfo.UseShellExecute = false;
|
||||
if (!useShellExecute)
|
||||
{
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
startInfo.RedirectStandardError = true;
|
||||
}
|
||||
|
||||
startInfo.UseShellExecute = useShellExecute;
|
||||
process.StartInfo = startInfo;
|
||||
|
||||
WriteLine($"on-complete: FileName={startInfo.FileName}, Arguments={startInfo.Arguments}", debugOnly: true);
|
||||
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
if (!useShellExecute)
|
||||
{
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue