1
0
Fork 0
mirror of https://github.com/fiso64/slsk-batchdl.git synced 2024-12-22 14:32:40 +00:00

fix spotify and csv retrieval

place files in subdirs for multi-album downloads if name-format is empty
This commit is contained in:
fiso64 2024-08-27 22:24:40 +02:00
parent 178dfe26b3
commit d5c42ad8b8
12 changed files with 106 additions and 51 deletions

View file

@ -311,6 +311,14 @@ will be ignored.
## Searching ## Searching
### Search Query
The search query is determined as follows:
- For album downloads: If the album field is non-empty, search for 'Artist Album'
Otherwise, search for 'Artist Title'
- For all other download types: If the title field is non-empty, search for 'Artist Title'
Otherwise, search for 'Artist Album'
### Soulseek's rate limits ### Soulseek's rate limits
The server will ban you for 30 minutes if too many searches are performed within a short 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 timespan. The program has a search limiter which can be adjusted with --searches-per-time
@ -492,13 +500,20 @@ sldl test.csv
``` ```
<br> <br>
Download spotify likes while skipping songs that already exist in the output folder: Download spotify likes:
``` ```
sldl spotify-likes --skip-existing sldl spotify-likes
``` ```
<br> <br>
Download from a youtube playlist with fallback to yt-dlp in case it is not found on soulseek, and retrieve deleted video titles from wayback machine: Download albums for every song in a spotify playlist:
```
sldl https://spotify/playlist/url --album --skip-existing
```
<br>
Retrieve deleted video names, then download from a youtube playlist with fallback to yt-dlp:
``` ```
sldl "https://www.youtube.com/playlist?list=PLI_eFW8NAFzYAXZ5DrU6E6mQ_XfhaLBUX" --get-deleted --yt-dlp sldl "https://www.youtube.com/playlist?list=PLI_eFW8NAFzYAXZ5DrU6E6mQ_XfhaLBUX" --get-deleted --yt-dlp
``` ```
@ -522,7 +537,7 @@ sldl "artist=MC MENTAL" --aggregate --skip-existing --music-dir "path/to/music"
``` ```
<br> <br>
Download all albums: Download all albums by an artist that are on soulseek:
``` ```
sldl "artist=MC MENTAL" --aggregate --album sldl "artist=MC MENTAL" --aggregate --album
``` ```

View file

@ -225,10 +225,16 @@ static class Config
if (DoNotDownload) if (DoNotDownload)
m3uOption = M3uOption.None; m3uOption = M3uOption.None;
else if (!hasConfiguredM3uMode && inputType == InputType.String) else if (!hasConfiguredM3uMode)
m3uOption = M3uOption.None; {
else if (!hasConfiguredM3uMode && Program.trackLists != null && !aggregate &&!Program.trackLists.Flattened(true, false, true).Skip(1).Any()) if (inputType == InputType.String)
m3uOption = M3uOption.None; m3uOption = M3uOption.None;
else if (!aggregate && !(skipExisting && (skipMode == SkipMode.M3u || skipMode == SkipMode.M3uCond))
&& Program.trackLists != null && !Program.trackLists.Flattened(true, false, true).Skip(1).Any())
{
m3uOption = M3uOption.None;
}
}
parentFolder = Utils.ExpandUser(parentFolder); parentFolder = Utils.ExpandUser(parentFolder);
m3uFilePath = Utils.ExpandUser(m3uFilePath); m3uFilePath = Utils.ExpandUser(m3uFilePath);

View file

@ -51,7 +51,10 @@ namespace Data
public string ToKey() public string ToKey()
{ {
return $"{Artist};{Album};{Title};{Length};{(int)Type}"; if (Type == TrackType.Album)
return $"{Artist};{Album};{(int)Type}";
else
return $"{Artist};{Album};{Title};{Length};{(int)Type}";
} }
public override string ToString() public override string ToString()
@ -103,7 +106,7 @@ namespace Data
public bool needSkipExistingAfterSearch = false; public bool needSkipExistingAfterSearch = false;
public bool gotoNextAfterSearch = false; public bool gotoNextAfterSearch = false;
public bool placeInSubdir = false; public bool placeInSubdir = false;
public string? subdirOverride; public bool useRemoteDirname = false;
public TrackListEntry() public TrackListEntry()
{ {
@ -138,12 +141,13 @@ namespace Data
} }
public TrackListEntry(List<List<Track>> list, Track source, bool needSearch, bool placeInSubdir, public TrackListEntry(List<List<Track>> list, Track source, bool needSearch, bool placeInSubdir,
bool canBeSkipped, bool needSkipExistingAfterSearch, bool gotoNextAfterSearch) bool useRemoteDirname, bool canBeSkipped, bool needSkipExistingAfterSearch, bool gotoNextAfterSearch)
{ {
this.list = list; this.list = list;
this.source = source; this.source = source;
this.needSourceSearch = needSearch; this.needSourceSearch = needSearch;
this.placeInSubdir = placeInSubdir; this.placeInSubdir = placeInSubdir;
this.useRemoteDirname = useRemoteDirname;
this.sourceCanBeSkipped = canBeSkipped; this.sourceCanBeSkipped = canBeSkipped;
this.needSkipExistingAfterSearch = needSkipExistingAfterSearch; this.needSkipExistingAfterSearch = needSkipExistingAfterSearch;
this.gotoNextAfterSearch = gotoNextAfterSearch; this.gotoNextAfterSearch = gotoNextAfterSearch;
@ -183,10 +187,9 @@ namespace Data
res.AddTrackToLast(enumerator.Current); res.AddTrackToLast(enumerator.Current);
} }
if (hasNext && enumerator.Current.Type != TrackType.Normal) if (hasNext)
res.AddEntry(new TrackListEntry(track)); res.AddEntry(new TrackListEntry(enumerator.Current));
else if (!hasNext) else break;
break;
} }
} }
@ -280,11 +283,26 @@ namespace Data
lists = newLists; lists = newLists;
} }
public void SetListEntryOptions()
{
// place downloads in subdirs if there is more than one special (album/aggregate) download
bool placeInSubdirs = Flattened(true, false, true).Skip(1).Any();
if (placeInSubdirs)
{
foreach(var tle in lists)
{
if (tle.source.Type != TrackType.Normal)
tle.placeInSubdir = true;
}
}
}
public IEnumerable<Track> Flattened(bool addSources, bool addSpecialSourceTracks, bool sourcesOnly = false) public IEnumerable<Track> Flattened(bool addSources, bool addSpecialSourceTracks, bool sourcesOnly = false)
{ {
foreach (var tle in lists) foreach (var tle in lists)
{ {
if ((addSources || sourcesOnly) && tle.source != null) if ((addSources || sourcesOnly) && tle.source != null && tle.source.Type != TrackType.Normal)
yield return tle.source; yield return tle.source;
if (!sourcesOnly && tle.list.Count > 0 && (tle.source.Type == TrackType.Normal || addSpecialSourceTracks)) if (!sourcesOnly && tle.list.Count > 0 && (tle.source.Type == TrackType.Normal || addSpecialSourceTracks))
{ {

View file

@ -25,6 +25,7 @@ namespace Extractors
if (isArtist) if (isArtist)
{ {
Console.WriteLine("Retrieving bandcamp artist discography..");
string artistUrl = Config.input.TrimEnd('/'); string artistUrl = Config.input.TrimEnd('/');
if (!artistUrl.EndsWith("/music")) if (!artistUrl.EndsWith("/music"))
@ -60,13 +61,13 @@ namespace Extractors
{ {
source = t, source = t,
placeInSubdir = true, placeInSubdir = true,
subdirOverride = t.ToString(true)
}; };
trackLists.AddEntry(tle); trackLists.AddEntry(tle);
} }
} }
else else
{ {
Console.WriteLine("Retrieving bandcamp item..");
var web = new HtmlWeb(); var web = new HtmlWeb();
var doc = await web.LoadFromWebAsync(Config.input); var doc = await web.LoadFromWebAsync(Config.input);

View file

@ -17,25 +17,22 @@ namespace Extractors
public async Task<TrackLists> GetTracks(int maxTracks, int offset, bool reverse) public async Task<TrackLists> GetTracks(int maxTracks, int offset, bool reverse)
{ {
int max = reverse ? int.MaxValue : maxTracks;
int off = reverse ? 0 : offset;
if (!File.Exists(Config.input)) if (!File.Exists(Config.input))
throw new FileNotFoundException("CSV file not found"); throw new FileNotFoundException("CSV file not found");
var tracks = await ParseCsvIntoTrackInfo(Config.input, Config.artistCol, Config.trackCol, Config.lengthCol, Config.albumCol, Config.descCol, Config.ytIdCol, Config.trackCountCol, Config.timeUnit, Config.ytParse); var tracks = await ParseCsvIntoTrackInfo(Config.input, Config.artistCol, Config.trackCol, Config.lengthCol,
Config.albumCol, Config.descCol, Config.ytIdCol, Config.trackCountCol, Config.timeUnit, Config.ytParse);
if (reverse) if (reverse)
tracks.Reverse(); tracks.Reverse();
var trackLists = TrackLists.FromFlattened(tracks.Skip(off).Take(max)); var trackLists = TrackLists.FromFlattened(tracks.Skip(offset).Take(maxTracks));
foreach (var tle in trackLists.lists) foreach (var tle in trackLists.lists)
{ {
if (tle.source.Type != TrackType.Normal) if (tle.source.Type != TrackType.Normal)
{ {
tle.placeInSubdir = true; tle.placeInSubdir = true;
tle.subdirOverride = tle.source.ToString(true);
} }
} }

View file

@ -110,6 +110,8 @@ namespace Extractors
Config.defaultFolderName = playlistName.ReplaceInvalidChars(Config.invalidReplaceStr); Config.defaultFolderName = playlistName.ReplaceInvalidChars(Config.invalidReplaceStr);
trackLists.AddEntry(tle);
if (reverse) if (reverse)
{ {
trackLists.Reverse(); trackLists.Reverse();

View file

@ -16,7 +16,7 @@ namespace Extractors
var trackLists = new TrackLists(); var trackLists = new TrackLists();
var music = ParseTrackArg(Config.input, Config.album); var music = ParseTrackArg(Config.input, Config.album);
if (music.Title.Length == 0 && music.Album.Length > 0) if (Config.album || (music.Title.Length == 0 && music.Album.Length > 0))
{ {
music.Type = TrackType.Album; music.Type = TrackType.Album;
trackLists.AddEntry(new TrackListEntry(music)); trackLists.AddEntry(new TrackListEntry(music));
@ -35,7 +35,7 @@ namespace Extractors
return trackLists; return trackLists;
} }
public Track ParseTrackArg(string input, bool isAlbum) static public Track ParseTrackArg(string input, bool isAlbum)
{ {
input = input.Trim(); input = input.Trim();
var track = new Track(); var track = new Track();

View file

@ -283,6 +283,14 @@ public static class Help
const string searchHelp = @" const string searchHelp = @"
Searching Searching
Search Query
The search query is determined as follows:
- For album downloads: If the album field is non-empty, search for 'Artist Album'
Otherwise, search for 'Artist Title'
- For all other download types: If the title field is non-empty, search for 'Artist Title'
Otherwise, search for 'Artist Album'
Soulseek's rate limits Soulseek's rate limits
The server will ban you for 30 minutes if too many searches are performed within a short 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 timespan. The program has a search limiter which can be adjusted with --searches-per-time

View file

@ -289,6 +289,7 @@ public class M3uEditor
public Track? PreviousRunResult(Track track) public Track? PreviousRunResult(Track track)
{ {
var key = track.ToKey();
previousRunData.TryGetValue(track.ToKey(), out var t); previousRunData.TryGetValue(track.ToKey(), out var t);
return t; return t;
} }

View file

@ -68,10 +68,12 @@ static partial class Program
WriteLine("Got tracks", debugOnly: true); WriteLine("Got tracks", debugOnly: true);
Config.PostProcessArgs();
trackLists.UpgradeListTypes(Config.aggregate, Config.album); trackLists.UpgradeListTypes(Config.aggregate, Config.album);
trackLists.SetListEntryOptions();
Config.PostProcessArgs();
m3uEditor = new M3uEditor(Config.m3uFilePath, trackLists, Config.m3uOption, Config.offset); m3uEditor = new M3uEditor(Config.m3uFilePath, trackLists, Config.m3uOption, Config.offset);
InitExistingChecker(); InitExistingChecker();
@ -262,7 +264,7 @@ static partial class Program
foreach (var item in res) foreach (var item in res)
{ {
var newSource = new Track(tle.source) { Type = TrackType.Album }; var newSource = new Track(tle.source) { Type = TrackType.Album };
trackLists.AddEntry(new TrackListEntry(item, newSource, false, true, true, false, false)); trackLists.AddEntry(new TrackListEntry(item, newSource, false, true, true, true, false, false));
} }
} }
@ -680,9 +682,9 @@ static partial class Program
soulseekFolderPathPrefix = GetCommonPathPrefix(tracks); soulseekFolderPathPrefix = GetCommonPathPrefix(tracks);
if (tle.placeInSubdir && Config.nameFormat.Length == 0 && (!downloadingImages || idx == 0)) if (tle.placeInSubdir && Config.nameFormat.Length == 0 && (idx == 0 || !downloadingImages))
{ {
string name = tle.subdirOverride ?? Utils.GetBaseNameSlsk(soulseekFolderPathPrefix); string name = tle.useRemoteDirname ? Utils.GetBaseNameSlsk(soulseekFolderPathPrefix) : tle.source.ToString(true);
Config.outputFolder = Path.Join(savedOutputFolder, name); Config.outputFolder = Path.Join(savedOutputFolder, name);
} }

View file

@ -919,11 +919,22 @@ static partial class Program
static string GetSearchString(Track track) static string GetSearchString(Track track)
{ {
if (track.Title.Length > 0) if (track.Type == TrackType.Album)
return (track.Artist + " " + track.Title).Trim(); {
else if (track.Album.Length > 0) if (track.Album.Length > 0)
return (track.Artist + " " + track.Album).Trim(); return (track.Artist + " " + track.Album).Trim();
return track.Artist.Trim(); if (track.Title.Length > 0)
return (track.Artist + " " + track.Title).Trim();
return track.Artist.Trim();
}
else
{
if (track.Title.Length > 0)
return (track.Artist + " " + track.Title).Trim();
else if (track.Album.Length > 0)
return (track.Artist + " " + track.Album).Trim();
return track.Artist.Trim();
}
} }

View file

@ -1,14 +1,8 @@
using Data; using Data;
using Enums; using Enums;
using ExistingCheckers; using ExistingCheckers;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static Test.Helpers; using static Test.Helpers;
@ -238,17 +232,17 @@ namespace Test
var albums = new List<Track>() var albums = new List<Track>()
{ {
new Track() { Album="Some Title" }, new Track() { Album="Some Title", Type = TrackType.Album },
new Track() { Album="Some, Title" }, new Track() { Album="Some, Title", Type = TrackType.Album },
new Track() { Title = "some title", Artist = "Some artist" }, new Track() { Title = "some title", Artist = "Some artist", Type = TrackType.Album },
new Track() { Album = "Title", Artist = "Artist", Length = 42 }, new Track() { Album = "Title", Artist = "Artist", Length = 42, Type = TrackType.Album },
new Track() { Title="Some, Title", Artist = "Some, Artist", Album = "Some, Album", Length = 42 }, new Track() { Title="Some, Title", Artist = "Some, Artist", Album = "Some, Album", Length = 42, Type = TrackType.Album },
new Track() { Artist = "Some, Artist = a", Album = "Some, Album", Length = 42 }, new Track() { Artist = "Some, Artist = a", Album = "Some, Album", Length = 42, Type = TrackType.Album },
new Track() { Album = "Foo Bar" }, new Track() { Album = "Foo Bar", Type = TrackType.Album },
new Track() { Album = "Bar", Artist = "Foo" }, new Track() { Album = "Bar", Artist = "Foo", Type = TrackType.Album },
new Track() { Album = "Title", Artist = "Artist", Length = 42 }, new Track() { Album = "Title", Artist = "Artist", Length = 42, Type = TrackType.Album },
new Track() { Title = "Title", Artist = "Artist", Length = 42 }, new Track() { Title = "Title", Artist = "Artist", Length = 42, Type = TrackType.Album },
}; };
var extractor = new Extractors.StringExtractor(); var extractor = new Extractors.StringExtractor();