1
0
Fork 0
mirror of https://github.com/fiso64/slsk-batchdl.git synced 2024-12-22 22:42:41 +00:00
This commit is contained in:
fiso64 2024-06-20 19:48:50 +02:00
parent 1e7564dd7e
commit 93fb18f221
4 changed files with 100 additions and 38 deletions

View file

@ -122,6 +122,8 @@ Options:
--name-format <format> Name format for downloaded tracks, e.g "{artist} - {title}"
--fast-search Begin downloading as soon as a file satisfying the preferred
conditions is found. Higher chance to download wrong files.
--remove-from-source Remove downloaded tracks from source playlist or CSV file
(spotify and CSV only)
--m3u <option> Create an m3u8 playlist file
'none': Do not create a playlist file
'fails' (default): Write only failed downloads to the m3u
@ -129,7 +131,6 @@ Options:
--spotify-id <id> spotify client ID
--spotify-secret <secret> spotify client secret
--remove-from-playlist Remove downloaded tracks from playlist (spotify only)
--youtube-key <key> Youtube data API key
--get-deleted Attempt to retrieve titles of deleted videos from wayback

View file

@ -70,6 +70,7 @@ static class Program
static string spotifySecret = "";
static string ytKey = "";
static string csvPath = "";
static int csvColumnCount = -1;
static string username = "";
static string password = "";
static string artistCol = "";
@ -138,6 +139,7 @@ static class Program
static int listenPort = 50000;
static object consoleLock = new object();
static object csvLock = new object();
static bool skipUpdate = false;
static bool debugDisableDownload = false;
@ -154,7 +156,17 @@ static class Program
private static M3UEditor? m3uEditor;
private static CancellationTokenSource? mainLoopCts;
static string inputType = "";
static InputType inputType = InputType.None;
public enum InputType
{
None,
Spotify,
YouTube,
Bandcamp,
String,
CSV
};
static void PrintHelp()
{
@ -193,6 +205,8 @@ static class Program
"\n --name-format <format> Name format for downloaded tracks, e.g \"{artist} - {title}\"" +
"\n --fast-search Begin downloading as soon as a file satisfying the preferred" +
"\n conditions is found. Higher chance to download wrong files." +
"\n --remove-from-source Remove downloaded tracks from source playlist or CSV file " +
"\n (spotify and CSV only)" +
"\n --m3u <option> Create an m3u8 playlist file" +
"\n 'none': Do not create a playlist file" +
"\n 'fails' (default): Write only failed downloads to the m3u" +
@ -200,7 +214,6 @@ static class Program
"\n" +
"\n --spotify-id <id> spotify client ID" +
"\n --spotify-secret <secret> spotify client secret" +
"\n --remove-from-playlist Remove downloaded tracks from playlist (spotify only)" +
"\n" +
"\n --youtube-key <key> Youtube data API key" +
"\n --get-deleted Attempt to retrieve titles of deleted videos from wayback" +
@ -399,7 +412,16 @@ static class Program
break;
case "--it":
case "--input-type":
inputType = args[++i];
inputType = args[++i].ToLower().Trim() switch
{
"none" => InputType.None,
"csv" => InputType.CSV,
"youtube" => InputType.YouTube,
"spotify" => InputType.Spotify,
"bandcamp" => InputType.Bandcamp,
"string" => InputType.String,
_ => throw new ArgumentException($"Invalid input type '{args[i]}'"),
};
break;
case "-p":
case "--path":
@ -563,6 +585,8 @@ static class Program
skipNotFound = true;
break;
case "--rfp":
case "--rfs":
case "--remove-from-source":
case "--remove-from-playlist":
removeTracksFromSource = true;
break;
@ -925,7 +949,7 @@ static class Program
else
{
if (input == "")
input = args[i];
input = args[i].Trim();
else
throw new ArgumentException($"Invalid argument \'{args[i]}\'. Input is already set to \'{input}\'");
}
@ -933,8 +957,6 @@ static class Program
if (input == "")
throw new ArgumentException($"No input provided");
if (!(new string[] { "", "youtube", "spotify", "csv", "string", "bandcamp" }).Contains(inputType))
throw new ArgumentException($"Invalid input type '{inputType}'");
if (ytKey != "")
YouTube.apiKey = ytKey;
@ -944,22 +966,22 @@ static class Program
ignoreOn = Math.Min(ignoreOn, deprioritizeOn);
if (inputType == "youtube" || (inputType == "" && input.StartsWith("http") && input.Contains("youtu")))
if (inputType == InputType.YouTube || (inputType == InputType.None && input.StartsWith("http") && input.Contains("youtu")))
{
WriteLine("Youtube download", debugOnly: true);
await YoutubeInput();
}
else if (inputType == "spotify" || (inputType == "" && (input.StartsWith("http") && input.Contains("spotify")) || input == "spotify-likes"))
else if (inputType == InputType.Spotify || (inputType == InputType.None && (input.StartsWith("http") && input.Contains("spotify")) || input == "spotify-likes"))
{
WriteLine("Spotify download", debugOnly: true);
await SpotifyInput();
}
else if (inputType == "bandcamp" || (inputType == "" && input.StartsWith("http") && input.Contains("bandcamp")))
else if (inputType == InputType.Bandcamp || (inputType == InputType.None && input.StartsWith("http") && input.Contains("bandcamp")))
{
WriteLine("Bandcamp download", debugOnly: true);
await BandcampInput();
}
else if (inputType == "csv" || (inputType == "" && Path.GetExtension(input).Equals(".csv", StringComparison.OrdinalIgnoreCase)))
else if (inputType == InputType.CSV || (inputType == InputType.None && input.EndsWith(".csv", StringComparison.OrdinalIgnoreCase)))
{
WriteLine("CSV download", debugOnly: true);
await CsvInput();
@ -1021,7 +1043,7 @@ static class Program
int max = reverse ? int.MaxValue : maxTracks;
int off = reverse ? 0 : offset;
ytUrl = input;
inputType = "youtube";
inputType = InputType.YouTube;
string name;
List<Track>? deleted = null;
@ -1071,10 +1093,9 @@ static class Program
int off = reverse ? 0 : offset;
spotifyUrl = input;
inputType = "spotify";
inputType = InputType.Spotify;
string? playlistName;
bool usedDefaultId = false;
bool needLogin = spotifyUrl == "spotify-likes" || removeTracksFromSource;
List<Track> tracks;
@ -1129,16 +1150,20 @@ static class Program
}
catch (SpotifyAPI.Web.APIException)
{
if (!needLogin)
if (!needLogin && !spotifyClient.UsedDefaultCredentials)
{
await spotifyClient.Authorize(true, removeTracksFromSource);
(playlistName, playlistUri, tracks) = await spotifyClient.GetPlaylist(spotifyUrl, max, off);
}
else if (!needLogin)
{
Console.WriteLine("Spotify playlist not found. It may be set to private. Login? [Y/n]");
string answer = Console.ReadLine();
if (answer.ToLower() == "y")
if (Console.ReadLine()?.ToLower().Trim() == "y")
{
if (usedDefaultId)
readSpotifyCreds();
await spotifyClient.Authorize(true);
Console.WriteLine("Loading Spotify tracks");
readSpotifyCreds();
spotifyClient = new Spotify(spotifyId, spotifySecret);
await spotifyClient.Authorize(true, removeTracksFromSource);
Console.WriteLine("Loading Spotify playlist");
(playlistName, playlistUri, tracks) = await spotifyClient.GetPlaylist(spotifyUrl, max, off);
}
else
@ -1162,7 +1187,7 @@ static class Program
static async Task BandcampInput()
{
inputType = "bandcamp";
inputType = InputType.Bandcamp;
bool isAlbum = !input.Contains("/track/");
var web = new HtmlWeb();
@ -1212,7 +1237,7 @@ static class Program
int off = reverse ? 0 : offset;
csvPath = input;
inputType = "csv";
inputType = InputType.CSV;
if (!File.Exists(csvPath))
throw new FileNotFoundException("CSV file not found");
@ -1227,7 +1252,7 @@ static class Program
static async Task StringInput()
{
searchStr = input;
inputType = "string";
inputType = InputType.String;
var music = ParseTrackArg(searchStr, album);
bool isAlbum = false;
@ -1558,16 +1583,18 @@ static class Program
if (savedFilePath != "")
{
try
{
lock (trackLists) { tracks[index] = new Track(track) { TrackState = Track.State.Downloaded, DownloadPath = savedFilePath }; }
lock (trackLists) { tracks[index] = new Track(track) { TrackState = Track.State.Downloaded, DownloadPath = savedFilePath }; }
if (removeTracksFromSource && !string.IsNullOrEmpty(spotifyUrl))
spotifyClient.RemoveTrackFromPlaylist(playlistUri, track.URI);
}
catch (Exception ex)
if (removeTracksFromSource)
{
WriteLine($"\n{ex.Message}\n{ex.StackTrace}\n", ConsoleColor.DarkYellow, true);
try
{
await RemoveTrackFromSource(track);
}
catch (Exception ex)
{
WriteLine($"\n{ex.Message}\n{ex.StackTrace}\n", ConsoleColor.DarkYellow, true);
}
}
}
@ -1911,6 +1938,28 @@ static class Program
}
static async Task RemoveTrackFromSource(Track track)
{
if (inputType == InputType.Spotify && track.URI != "")
{
await spotifyClient.RemoveTrackFromPlaylist(playlistUri, track.URI);
}
else if (inputType == InputType.CSV && track.CsvRow != -1)
{
lock (csvLock)
{
string[] lines = File.ReadAllLines(csvPath, System.Text.Encoding.UTF8);
if (lines.Length > track.CsvRow)
{
lines[track.CsvRow] = new string(',', Math.Max(0, csvColumnCount - 1));
File.WriteAllLines(csvPath, lines, System.Text.Encoding.UTF8);
}
}
}
}
static async Task Login(bool random = false, int tries = 3)
{
string user = username, pass = password;
@ -3435,9 +3484,13 @@ static class Program
using var sr = new StreamReader(path, System.Text.Encoding.UTF8);
var parser = new SmallestCSV.SmallestCSVParser(sr);
int index = 0;
var header = parser.ReadNextRow();
while (header == null || header.Count == 0 || !header.Any(t => t.Trim() != ""))
{
index++;
header = parser.ReadNextRow();
}
string[] cols = { artistCol, albumCol, trackCol, lengthCol, descCol, ytIdCol, trackCountCol };
string[][] aliases = {
@ -3482,6 +3535,7 @@ static class Program
while (true)
{
index++;
var values = parser.ReadNextRow();
if (values == null)
break;
@ -3490,9 +3544,12 @@ static class Program
while (values.Count < foundCount)
values.Add("");
var desc = "";
if (csvColumnCount == -1)
csvColumnCount = values.Count;
var desc = "";
var track = new Track() { CsvRow = index };
var track = new Track();
if (artistIndex >= 0) track.Artist = values[artistIndex];
if (trackIndex >= 0) track.Title = values[trackIndex];
if (albumIndex >= 0) track.Album = values[albumIndex];
@ -4572,6 +4629,7 @@ public struct Track
public string FailureReason = "";
public string DownloadPath = "";
public string Other = "";
public int CsvRow = -1;
public State TrackState = State.Initial;
public SlDictionary? Downloads = null;
@ -4604,6 +4662,7 @@ public struct Track
Other = other.Other;
MinAlbumTrackCount = other.MinAlbumTrackCount;
MaxAlbumTrackCount = other.MaxAlbumTrackCount;
CsvRow = other.CsvRow;
}
public override readonly string ToString()

View file

@ -7,7 +7,7 @@ public class Spotify
private EmbedIOAuthServer _server;
private readonly string _clientId;
private readonly string _clientSecret;
private SpotifyClient _client;
private SpotifyClient? _client;
private bool loggedIn = false;
// default spotify credentials (base64-encoded to keep the bots away)
@ -15,7 +15,7 @@ public class Spotify
public const string encodedSpotifySecret = "Y2JlM2QxYTE5MzJkNDQ2MmFiOGUy3shTuf4Y2JhY2M3ZDdjYWU=";
public bool UsedDefaultCredentials { get; private set; }
public Spotify(string clientId, string clientSecret)
public Spotify(string clientId="", string clientSecret="")
{
_clientId = clientId;
_clientSecret = clientSecret;
@ -30,6 +30,8 @@ public class Spotify
public async Task Authorize(bool login = false, bool needModify = false)
{
_client = null;
if (!login)
{
var config = SpotifyClientConfig.CreateDefault();

View file

@ -24,8 +24,8 @@
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" />
<PackageReference Include="SmallestCSVParser" Version="1.1.1" />
<PackageReference Include="Soulseek" Version="6.4.1" />
<PackageReference Include="SpotifyAPI.Web" Version="7.0.2" />
<PackageReference Include="SpotifyAPI.Web.Auth" Version="7.0.2" />
<PackageReference Include="SpotifyAPI.Web" Version="7.1.1" />
<PackageReference Include="SpotifyAPI.Web.Auth" Version="7.1.1" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="YoutubeExplode" Version="6.3.16" />
</ItemGroup>