mirror of
https://github.com/fiso64/slsk-batchdl.git
synced 2024-12-22 22:42:41 +00:00
Add --remove-from-playlist
This commit is contained in:
parent
1cc392e4b2
commit
579e94d87e
5 changed files with 116 additions and 90 deletions
|
@ -6,7 +6,7 @@ A batch downloader for Soulseek using Soulseek.NET. Accepts CSV files, Spotify &
|
||||||
```
|
```
|
||||||
slsk-batchdl --csv test.csv --artist-col "Artist Name(s)" --track-col "Track Name" --length-col "Duration (ms)" --time-unit ms
|
slsk-batchdl --csv test.csv --artist-col "Artist Name(s)" --track-col "Track Name" --length-col "Duration (ms)" --time-unit ms
|
||||||
```
|
```
|
||||||
You can omit the column names provided they are named predictably (like in this example). Use `--print-tracks` before downloading to check if everything has been parsed correctly.
|
You can omit the column names if they are named predictably (like in this example). Use `--print-tracks` before downloading to check if everything has been parsed correctly.
|
||||||
|
|
||||||
- Download spotify likes while skipping existing songs, and create an m3u file:
|
- Download spotify likes while skipping existing songs, and create an m3u file:
|
||||||
```
|
```
|
||||||
|
@ -59,6 +59,7 @@ Options:
|
||||||
-n --number <maxtracks> Download at most n tracks of a playlist
|
-n --number <maxtracks> Download at most n tracks of a playlist
|
||||||
-o --offset <offset> Skip a specified number of tracks
|
-o --offset <offset> Skip a specified number of tracks
|
||||||
--reverse Download tracks in reverse order
|
--reverse Download tracks in reverse order
|
||||||
|
--remove-from-playlist Remove downloaded tracks from playlist (spotify only)
|
||||||
--name-format <format> Name format for downloaded tracks, e.g "{artist} - {title}"
|
--name-format <format> Name format for downloaded tracks, e.g "{artist} - {title}"
|
||||||
--m3u Create an m3u8 playlist file
|
--m3u Create an m3u8 playlist file
|
||||||
|
|
||||||
|
@ -131,3 +132,4 @@ Supports .conf files: Create a file named `slsk-batchdl.conf` in the same direct
|
||||||
### Notes:
|
### Notes:
|
||||||
- The CSV file must be saved with `,` as field delimiter and `"` as string delimiter, encoded with UTF8
|
- The CSV file must be saved with `,` as field delimiter and `"` as string delimiter, encoded with UTF8
|
||||||
- `--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. In my testing on Windows, the terminal app seems to be affected by this (unlike the old command prompt).
|
- `--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. In my testing on Windows, the terminal app seems to be affected by this (unlike the old command prompt).
|
||||||
|
- Why didn't I just use Python?
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
using Konsole;
|
using AngleSharp.Dom;
|
||||||
|
using Konsole;
|
||||||
using Soulseek;
|
using Soulseek;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Net.Sockets;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
using YoutubeExplode.Playlists;
|
||||||
|
|
||||||
static class Program
|
static class Program
|
||||||
{
|
{
|
||||||
|
@ -56,6 +59,7 @@ static class Program
|
||||||
static bool createM3u = false;
|
static bool createM3u = false;
|
||||||
static bool m3uOnly = false;
|
static bool m3uOnly = false;
|
||||||
static bool useTagsCheckExisting = false;
|
static bool useTagsCheckExisting = false;
|
||||||
|
static bool removeTracksFromSource = false;
|
||||||
static int maxTracks = int.MaxValue;
|
static int maxTracks = int.MaxValue;
|
||||||
static int offset = 0;
|
static int offset = 0;
|
||||||
|
|
||||||
|
@ -84,6 +88,8 @@ static class Program
|
||||||
|
|
||||||
static string confPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "slsk-batchdl.conf");
|
static string confPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "slsk-batchdl.conf");
|
||||||
|
|
||||||
|
static string playlistUri = "";
|
||||||
|
static Spotify? spotifyClient = null;
|
||||||
static string ytdlpFormat = "bestaudio/best";
|
static string ytdlpFormat = "bestaudio/best";
|
||||||
static int downloadMaxStaleTime = 50000;
|
static int downloadMaxStaleTime = 50000;
|
||||||
static int updateDelay = 100;
|
static int updateDelay = 100;
|
||||||
|
@ -140,6 +146,7 @@ static class Program
|
||||||
"\n -n --number <maxtracks> Download at most n tracks of a playlist" +
|
"\n -n --number <maxtracks> Download at most n tracks of a playlist" +
|
||||||
"\n -o --offset <offset> Skip a specified number of tracks" +
|
"\n -o --offset <offset> Skip a specified number of tracks" +
|
||||||
"\n --reverse Download tracks in reverse order" +
|
"\n --reverse Download tracks in reverse order" +
|
||||||
|
"\n --remove-from-playlist Remove downloaded tracks from playlist (spotify only)" +
|
||||||
"\n --name-format <format> Name format for downloaded tracks, e.g \"{artist} - {title}\"" +
|
"\n --name-format <format> Name format for downloaded tracks, e.g \"{artist} - {title}\"" +
|
||||||
"\n --m3u Create an m3u8 playlist file" +
|
"\n --m3u Create an m3u8 playlist file" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -210,7 +217,7 @@ static class Program
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Console.BufferHeight <= 50)
|
if (Console.BufferHeight <= 50)
|
||||||
WriteLine("You may be using the windows terminal app. Recommended to use command prompt to avoid printing issues.", ConsoleColor.DarkYellow);
|
WriteLine("Recommended to use the command prompt instead of terminal app to avoid printing issues.", ConsoleColor.DarkYellow);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
#endif
|
#endif
|
||||||
|
@ -346,6 +353,9 @@ static class Program
|
||||||
case "--skip-not-found":
|
case "--skip-not-found":
|
||||||
skipNotFound = true;
|
skipNotFound = true;
|
||||||
break;
|
break;
|
||||||
|
case "--remove-from-playlist":
|
||||||
|
removeTracksFromSource = true;
|
||||||
|
break;
|
||||||
case "--remove-ft":
|
case "--remove-ft":
|
||||||
removeFt = true;
|
removeFt = true;
|
||||||
break;
|
break;
|
||||||
|
@ -486,53 +496,67 @@ static class Program
|
||||||
|
|
||||||
int max = reverse ? int.MaxValue : maxTracks;
|
int max = reverse ? int.MaxValue : maxTracks;
|
||||||
int off = reverse ? 0 : offset;
|
int off = reverse ? 0 : offset;
|
||||||
|
|
||||||
if (spotifyUrl != "")
|
if (spotifyUrl != "")
|
||||||
{
|
{
|
||||||
|
string? playlistName;
|
||||||
bool usedDefaultId = false;
|
bool usedDefaultId = false;
|
||||||
|
bool login = spotifyUrl == "likes" || removeTracksFromSource;
|
||||||
|
|
||||||
|
void readSpotifyCreds()
|
||||||
|
{
|
||||||
|
Console.Write("Spotify client ID:");
|
||||||
|
spotifyId = Console.ReadLine();
|
||||||
|
Console.Write("Spotify client secret:");
|
||||||
|
spotifySecret = Console.ReadLine();
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
if (spotifyId == "" || spotifySecret == "")
|
if (spotifyId == "" || spotifySecret == "")
|
||||||
{
|
{
|
||||||
spotifyId = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encodedSpotifyId));
|
if (login)
|
||||||
spotifySecret = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encodedSpotifySecret));
|
readSpotifyCreds();
|
||||||
usedDefaultId = true;
|
else
|
||||||
|
{
|
||||||
|
spotifyId = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encodedSpotifyId));
|
||||||
|
spotifySecret = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(encodedSpotifySecret));
|
||||||
|
usedDefaultId = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
string? playlistName;
|
|
||||||
|
spotifyClient = new Spotify(spotifyId, spotifySecret);
|
||||||
|
await spotifyClient.Authorize(login, removeTracksFromSource);
|
||||||
|
|
||||||
if (spotifyUrl == "likes")
|
if (spotifyUrl == "likes")
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("Loading Spotify likes");
|
||||||
|
tracks = await spotifyClient.GetLikes(max, off);
|
||||||
playlistName = "Spotify Likes";
|
playlistName = "Spotify Likes";
|
||||||
if (usedDefaultId)
|
|
||||||
{
|
|
||||||
Console.Write("Spotify client ID:");
|
|
||||||
spotifyId = Console.ReadLine();
|
|
||||||
Console.Write("Spotify client secret:");
|
|
||||||
spotifySecret = Console.ReadLine();
|
|
||||||
Console.WriteLine();
|
|
||||||
}
|
|
||||||
tracks = await GetSpotifyLikes(spotifyId, spotifySecret, max, off);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(playlistName, tracks) = await GetSpotifyPlaylist(spotifyUrl, spotifyId, spotifySecret, false, max, off);
|
Console.WriteLine("Loading Spotify tracks");
|
||||||
|
(playlistName, playlistUri, tracks) = await spotifyClient.GetPlaylist(spotifyUrl, max, off);
|
||||||
}
|
}
|
||||||
catch (SpotifyAPI.Web.APIException)
|
catch (SpotifyAPI.Web.APIException)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Spotify playlist not found. It may be set to private. Login? [Y/n]");
|
if (!login)
|
||||||
string answer = Console.ReadLine();
|
|
||||||
if (answer.ToLower() == "y")
|
|
||||||
{
|
{
|
||||||
if (usedDefaultId)
|
Console.WriteLine("Spotify playlist not found. It may be set to private. Login? [Y/n]");
|
||||||
|
string answer = Console.ReadLine();
|
||||||
|
if (answer.ToLower() == "y")
|
||||||
{
|
{
|
||||||
Console.Write("Spotify client ID:");
|
if (usedDefaultId)
|
||||||
spotifyId = Console.ReadLine();
|
readSpotifyCreds();
|
||||||
Console.Write("Spotify client secret:");
|
await spotifyClient.Authorize(true);
|
||||||
spotifySecret = Console.ReadLine();
|
Console.WriteLine("Loading Spotify tracks");
|
||||||
Console.WriteLine();
|
(playlistName, playlistUri, tracks) = await spotifyClient.GetPlaylist(spotifyUrl, max, off);
|
||||||
}
|
}
|
||||||
(playlistName, tracks) = await GetSpotifyPlaylist(spotifyUrl, spotifyId, spotifySecret, true, max, off);
|
else return;
|
||||||
}
|
}
|
||||||
else
|
else throw;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (folderName == "")
|
if (folderName == "")
|
||||||
|
@ -561,7 +585,7 @@ static class Program
|
||||||
else if (tracksCsv != "")
|
else if (tracksCsv != "")
|
||||||
{
|
{
|
||||||
if (!System.IO.File.Exists(tracksCsv))
|
if (!System.IO.File.Exists(tracksCsv))
|
||||||
throw new Exception("csv file not found");
|
throw new Exception("CSV file not found");
|
||||||
|
|
||||||
tracks = await ParseCsvIntoTrackInfo(tracksCsv, artistCol, trackCol, lengthCol, albumCol, descCol, ytIdCol, timeUnit, ytParse);
|
tracks = await ParseCsvIntoTrackInfo(tracksCsv, artistCol, trackCol, lengthCol, albumCol, descCol, ytIdCol, timeUnit, ytParse);
|
||||||
tracks = tracks.Skip(off).Take(max).ToList();
|
tracks = tracks.Skip(off).Take(max).ToList();
|
||||||
|
@ -761,8 +785,14 @@ static class Program
|
||||||
if (savedFilePath != "")
|
if (savedFilePath != "")
|
||||||
{
|
{
|
||||||
Interlocked.Increment(ref successCount);
|
Interlocked.Increment(ref successCount);
|
||||||
m3uLines[tracksStart.IndexOf(track)] = Path.GetFileName(savedFilePath);
|
|
||||||
|
|
||||||
|
if (removeTracksFromSource)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(spotifyUrl))
|
||||||
|
spotifyClient.RemoveTrackFromPlaylist(playlistUri, track.URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
m3uLines[tracksStart.IndexOf(track)] = Path.GetFileName(savedFilePath);
|
||||||
if (createM3u)
|
if (createM3u)
|
||||||
{
|
{
|
||||||
using (var fileStream = new FileStream(m3uFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
|
using (var fileStream = new FileStream(m3uFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
|
||||||
|
@ -1259,11 +1289,11 @@ static class Program
|
||||||
|
|
||||||
static async Task<string> YtdlpSearchAndDownload(Track track, ProgressBar progress)
|
static async Task<string> YtdlpSearchAndDownload(Track track, ProgressBar progress)
|
||||||
{
|
{
|
||||||
if (track.YtID != "")
|
if (track.URI != "")
|
||||||
{
|
{
|
||||||
string videoTitle = (await YouTube.GetVideoInfo(track.YtID)).title;
|
string videoTitle = (await YouTube.GetVideoInfo(track.URI)).title;
|
||||||
string saveFilePathNoExt = GetSavePathNoExt(videoTitle, track);
|
string saveFilePathNoExt = GetSavePathNoExt(videoTitle, track);
|
||||||
await YtdlpDownload(track.YtID, saveFilePathNoExt, progress);
|
await YtdlpDownload(track.URI, saveFilePathNoExt, progress);
|
||||||
return saveFilePathNoExt;
|
return saveFilePathNoExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1625,33 +1655,6 @@ static class Program
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static async Task<(string?, List<Track>)> GetSpotifyPlaylist(string url, string id, string secret, bool login, int max=int.MaxValue, int offset=0)
|
|
||||||
{
|
|
||||||
var spotify = new Spotify(id, secret);
|
|
||||||
if (login)
|
|
||||||
{
|
|
||||||
await spotify.AuthorizeLogin();
|
|
||||||
await spotify.IsClientReady();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
await spotify.Authorize();
|
|
||||||
|
|
||||||
Console.WriteLine("Loading Spotify tracks");
|
|
||||||
(string? name, var res) = await spotify.GetPlaylist(url, max, offset);
|
|
||||||
return (name, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
static async Task<List<Track>> GetSpotifyLikes(string id, string secret, int max = int.MaxValue, int offset = 0)
|
|
||||||
{
|
|
||||||
var spotify = new Spotify(id, secret);
|
|
||||||
await spotify.AuthorizeLogin();
|
|
||||||
await spotify.IsClientReady();
|
|
||||||
|
|
||||||
Console.WriteLine("Loading Spotify tracks");
|
|
||||||
var res = await spotify.GetLikes(max, offset);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static async Task<List<Track>> ParseCsvIntoTrackInfo(string path, string? artistCol = "", string? trackCol = "",
|
static async Task<List<Track>> ParseCsvIntoTrackInfo(string path, string? artistCol = "", string? trackCol = "",
|
||||||
string? lengthCol = "", string? albumCol = "", string? descCol = "", string? ytIdCol = "", string timeUnit = "", bool ytParse = false)
|
string? lengthCol = "", string? albumCol = "", string? descCol = "", string? ytIdCol = "", string timeUnit = "", bool ytParse = false)
|
||||||
{
|
{
|
||||||
|
@ -1693,7 +1696,7 @@ static class Program
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(usingColumns))
|
if (!string.IsNullOrEmpty(usingColumns))
|
||||||
Console.WriteLine($"Using columns: {usingColumns.TrimEnd(' ', ',')}.");
|
Console.WriteLine($"Using inferred columns: {usingColumns.TrimEnd(' ', ',')}.");
|
||||||
|
|
||||||
if (cols[0] == "")
|
if (cols[0] == "")
|
||||||
WriteLine($"Warning: No artist column specified, results may be imprecise", ConsoleColor.DarkYellow);
|
WriteLine($"Warning: No artist column specified, results may be imprecise", ConsoleColor.DarkYellow);
|
||||||
|
@ -2218,7 +2221,7 @@ public struct Track
|
||||||
public string TrackTitle = "";
|
public string TrackTitle = "";
|
||||||
public string ArtistName = "";
|
public string ArtistName = "";
|
||||||
public string Album = "";
|
public string Album = "";
|
||||||
public string YtID = "";
|
public string URI = "";
|
||||||
public int Length = -1;
|
public int Length = -1;
|
||||||
public bool ArtistMaybeWrong = false;
|
public bool ArtistMaybeWrong = false;
|
||||||
|
|
||||||
|
|
|
@ -16,31 +16,42 @@ public class Spotify
|
||||||
_clientSecret = clientSecret;
|
_clientSecret = clientSecret;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Authorize()
|
public async Task Authorize(bool login = false, bool needModify = false)
|
||||||
{
|
{
|
||||||
var config = SpotifyClientConfig.CreateDefault();
|
if (!login)
|
||||||
|
|
||||||
var request = new ClientCredentialsRequest(_clientId, _clientSecret);
|
|
||||||
var response = await new OAuthClient(config).RequestToken(request);
|
|
||||||
|
|
||||||
_client = new SpotifyClient(config.WithToken(response.AccessToken));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AuthorizeLogin()
|
|
||||||
{
|
|
||||||
Swan.Logging.Logger.NoLogging();
|
|
||||||
_server = new EmbedIOAuthServer(new Uri("http://localhost:48721/callback"), 48721);
|
|
||||||
await _server.Start();
|
|
||||||
|
|
||||||
_server.AuthorizationCodeReceived += OnAuthorizationCodeReceived;
|
|
||||||
_server.ErrorReceived += OnErrorReceived;
|
|
||||||
|
|
||||||
var request = new LoginRequest(_server.BaseUri, _clientId, LoginRequest.ResponseType.Code)
|
|
||||||
{
|
{
|
||||||
Scope = new List<string> { Scopes.UserLibraryRead, Scopes.PlaylistReadPrivate }
|
var config = SpotifyClientConfig.CreateDefault();
|
||||||
};
|
|
||||||
|
|
||||||
BrowserUtil.Open(request.ToUri());
|
var request = new ClientCredentialsRequest(_clientId, _clientSecret);
|
||||||
|
var response = await new OAuthClient(config).RequestToken(request);
|
||||||
|
|
||||||
|
_client = new SpotifyClient(config.WithToken(response.AccessToken));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Swan.Logging.Logger.NoLogging();
|
||||||
|
_server = new EmbedIOAuthServer(new Uri("http://localhost:48721/callback"), 48721);
|
||||||
|
await _server.Start();
|
||||||
|
|
||||||
|
_server.AuthorizationCodeReceived += OnAuthorizationCodeReceived;
|
||||||
|
_server.ErrorReceived += OnErrorReceived;
|
||||||
|
|
||||||
|
var scope = new List<string> {
|
||||||
|
Scopes.UserLibraryRead, Scopes.PlaylistReadPrivate, Scopes.PlaylistReadCollaborative
|
||||||
|
};
|
||||||
|
|
||||||
|
if (needModify)
|
||||||
|
{
|
||||||
|
scope.Add(Scopes.PlaylistModifyPublic);
|
||||||
|
scope.Add(Scopes.PlaylistModifyPrivate);
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = new LoginRequest(_server.BaseUri, _clientId, LoginRequest.ResponseType.Code) { Scope = scope };
|
||||||
|
|
||||||
|
BrowserUtil.Open(request.ToUri());
|
||||||
|
|
||||||
|
await IsClientReady();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnAuthorizationCodeReceived(object sender, AuthorizationCodeResponse response)
|
private async Task OnAuthorizationCodeReceived(object sender, AuthorizationCodeResponse response)
|
||||||
|
@ -103,8 +114,16 @@ public class Spotify
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task RemoveTrackFromPlaylist(string playlistId, string trackUri)
|
||||||
|
{
|
||||||
|
var item = new PlaylistRemoveItemsRequest.Item { Uri = trackUri };
|
||||||
|
var pr = new PlaylistRemoveItemsRequest();
|
||||||
|
pr.Tracks = new List<PlaylistRemoveItemsRequest.Item>() { item };
|
||||||
|
try { await _client.Playlists.RemoveItems(playlistId, pr); }
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<(string?, List<Track>)> GetPlaylist(string url, int max = int.MaxValue, int offset = 0)
|
public async Task<(string?, string?, List<Track>)> GetPlaylist(string url, int max = int.MaxValue, int offset = 0)
|
||||||
{
|
{
|
||||||
var playlistId = GetPlaylistIdFromUrl(url);
|
var playlistId = GetPlaylistIdFromUrl(url);
|
||||||
var p = await _client.Playlists.Get(playlistId);
|
var p = await _client.Playlists.Get(playlistId);
|
||||||
|
@ -122,8 +141,10 @@ public class Spotify
|
||||||
string artist = artists[0];
|
string artist = artists[0];
|
||||||
string name = (string)track.Track.ReadProperty("name");
|
string name = (string)track.Track.ReadProperty("name");
|
||||||
string album = (string)track.Track.ReadProperty("album").ReadProperty("name");
|
string album = (string)track.Track.ReadProperty("album").ReadProperty("name");
|
||||||
|
string uri = (string)track.Track.ReadProperty("uri");
|
||||||
int duration = (int)track.Track.ReadProperty("durationMs");
|
int duration = (int)track.Track.ReadProperty("durationMs");
|
||||||
res.Add(new Track { Album = album, ArtistName = artist, TrackTitle = name, Length = duration / 1000 });
|
|
||||||
|
res.Add(new Track { Album = album, ArtistName = artist, TrackTitle = name, Length = duration / 1000, URI = uri });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tracks.Items.Count < limit || res.Count >= max)
|
if (tracks.Items.Count < limit || res.Count >= max)
|
||||||
|
@ -133,7 +154,7 @@ public class Spotify
|
||||||
limit = Math.Min(max - res.Count, 100);
|
limit = Math.Min(max - res.Count, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (p.Name, res);
|
return (p.Name, p.Id, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetPlaylistIdFromUrl(string url)
|
private string GetPlaylistIdFromUrl(string url)
|
||||||
|
|
|
@ -89,7 +89,7 @@ public static class YouTube
|
||||||
{
|
{
|
||||||
(string title, string uploader, int length, string desc) info = ("", "", -1, "");
|
(string title, string uploader, int length, string desc) info = ("", "", -1, "");
|
||||||
var track = new Track();
|
var track = new Track();
|
||||||
track.YtID = id;
|
track.URI = id;
|
||||||
|
|
||||||
title = title.Replace("–", "-");
|
title = title.Replace("–", "-");
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
<DefineConstants>$(DefineConstants)TRACE;</DefineConstants>
|
<DefineConstants>$(DefineConstants);TRACE</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
Loading…
Reference in a new issue