1
0
Fork 0
mirror of https://github.com/fiso64/slsk-batchdl.git synced 2024-12-22 06:22:41 +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
### 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
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
@ -492,13 +500,20 @@ sldl test.csv
```
<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>
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
```
@ -522,7 +537,7 @@ sldl "artist=MC MENTAL" --aggregate --skip-existing --music-dir "path/to/music"
```
<br>
Download all albums:
Download all albums by an artist that are on soulseek:
```
sldl "artist=MC MENTAL" --aggregate --album
```

View file

@ -225,10 +225,16 @@ static class Config
if (DoNotDownload)
m3uOption = M3uOption.None;
else if (!hasConfiguredM3uMode && inputType == InputType.String)
m3uOption = M3uOption.None;
else if (!hasConfiguredM3uMode && Program.trackLists != null && !aggregate &&!Program.trackLists.Flattened(true, false, true).Skip(1).Any())
m3uOption = M3uOption.None;
else if (!hasConfiguredM3uMode)
{
if (inputType == InputType.String)
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);
m3uFilePath = Utils.ExpandUser(m3uFilePath);

View file

@ -51,7 +51,10 @@ namespace Data
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()
@ -103,7 +106,7 @@ namespace Data
public bool needSkipExistingAfterSearch = false;
public bool gotoNextAfterSearch = false;
public bool placeInSubdir = false;
public string? subdirOverride;
public bool useRemoteDirname = false;
public TrackListEntry()
{
@ -138,12 +141,13 @@ namespace Data
}
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.source = source;
this.needSourceSearch = needSearch;
this.placeInSubdir = placeInSubdir;
this.useRemoteDirname = useRemoteDirname;
this.sourceCanBeSkipped = canBeSkipped;
this.needSkipExistingAfterSearch = needSkipExistingAfterSearch;
this.gotoNextAfterSearch = gotoNextAfterSearch;
@ -183,10 +187,9 @@ namespace Data
res.AddTrackToLast(enumerator.Current);
}
if (hasNext && enumerator.Current.Type != TrackType.Normal)
res.AddEntry(new TrackListEntry(track));
else if (!hasNext)
break;
if (hasNext)
res.AddEntry(new TrackListEntry(enumerator.Current));
else break;
}
}
@ -280,11 +283,26 @@ namespace Data
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)
{
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;
if (!sourcesOnly && tle.list.Count > 0 && (tle.source.Type == TrackType.Normal || addSpecialSourceTracks))
{

View file

@ -25,6 +25,7 @@ namespace Extractors
if (isArtist)
{
Console.WriteLine("Retrieving bandcamp artist discography..");
string artistUrl = Config.input.TrimEnd('/');
if (!artistUrl.EndsWith("/music"))
@ -60,13 +61,13 @@ namespace Extractors
{
source = t,
placeInSubdir = true,
subdirOverride = t.ToString(true)
};
trackLists.AddEntry(tle);
}
}
else
{
Console.WriteLine("Retrieving bandcamp item..");
var web = new HtmlWeb();
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)
{
int max = reverse ? int.MaxValue : maxTracks;
int off = reverse ? 0 : offset;
if (!File.Exists(Config.input))
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)
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)
{
if (tle.source.Type != TrackType.Normal)
{
tle.placeInSubdir = true;
tle.subdirOverride = tle.source.ToString(true);
}
}

View file

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

View file

@ -16,7 +16,7 @@ namespace Extractors
var trackLists = new TrackLists();
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;
trackLists.AddEntry(new TrackListEntry(music));
@ -35,7 +35,7 @@ namespace Extractors
return trackLists;
}
public Track ParseTrackArg(string input, bool isAlbum)
static public Track ParseTrackArg(string input, bool isAlbum)
{
input = input.Trim();
var track = new Track();

View file

@ -283,6 +283,14 @@ public static class Help
const string searchHelp = @"
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
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

View file

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

View file

@ -68,10 +68,12 @@ static partial class Program
WriteLine("Got tracks", debugOnly: true);
Config.PostProcessArgs();
trackLists.UpgradeListTypes(Config.aggregate, Config.album);
trackLists.SetListEntryOptions();
Config.PostProcessArgs();
m3uEditor = new M3uEditor(Config.m3uFilePath, trackLists, Config.m3uOption, Config.offset);
InitExistingChecker();
@ -262,7 +264,7 @@ static partial class Program
foreach (var item in res)
{
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);
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);
}

View file

@ -919,11 +919,22 @@ static partial class Program
static string GetSearchString(Track track)
{
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();
if (track.Type == TrackType.Album)
{
if (track.Album.Length > 0)
return (track.Artist + " " + track.Album).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 Enums;
using ExistingCheckers;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using static Test.Helpers;
@ -238,17 +232,17 @@ namespace Test
var albums = new List<Track>()
{
new Track() { Album="Some Title" },
new Track() { Album="Some, Title" },
new Track() { Title = "some title", Artist = "Some artist" },
new Track() { Album = "Title", Artist = "Artist", Length = 42 },
new Track() { Title="Some, Title", Artist = "Some, Artist", Album = "Some, Album", Length = 42 },
new Track() { Artist = "Some, Artist = a", Album = "Some, Album", Length = 42 },
new Track() { Album="Some Title", Type = TrackType.Album },
new Track() { Album="Some, Title", Type = TrackType.Album },
new Track() { Title = "some title", Artist = "Some artist", Type = TrackType.Album },
new Track() { Album = "Title", Artist = "Artist", Length = 42, Type = TrackType.Album },
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, Type = TrackType.Album },
new Track() { Album = "Foo Bar" },
new Track() { Album = "Bar", Artist = "Foo" },
new Track() { Album = "Title", Artist = "Artist", Length = 42 },
new Track() { Title = "Title", Artist = "Artist", Length = 42 },
new Track() { Album = "Foo Bar", Type = TrackType.Album },
new Track() { Album = "Bar", Artist = "Foo", Type = TrackType.Album },
new Track() { Album = "Title", Artist = "Artist", Length = 42, Type = TrackType.Album },
new Track() { Title = "Title", Artist = "Artist", Length = 42, Type = TrackType.Album },
};
var extractor = new Extractors.StringExtractor();