97 lines
2.5 KiB
C#
97 lines
2.5 KiB
C#
namespace SpotiGrabber;
|
|
using SpotifyAPI.Web;
|
|
using System.Diagnostics;
|
|
using YoutubeExplode;
|
|
using YoutubeExplode.Videos.Streams;
|
|
|
|
class Program
|
|
{
|
|
static SpotifyClient SpotiClient = null!;
|
|
static YoutubeClient YtClient = new();
|
|
|
|
static YouTubeTrackMatcher TrackMatcher = new(YtClient);
|
|
|
|
public static string? ExtractSpotifyId(string url)
|
|
{
|
|
var match = System.Text.RegularExpressions.Regex.Match(url, @"(?:open\.spotify\.com/|spotify:)(?:intl-[a-z]{2}/)?(?:track|playlist|album)[/:]([a-zA-Z0-9]+)",
|
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
|
return match.Success ? match.Groups[1].Value : null;
|
|
}
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
MainAsync(args).GetAwaiter().GetResult();
|
|
}
|
|
|
|
static async Task MainAsync(string[] args)
|
|
{
|
|
string token = File.ReadAllText("token.txt");
|
|
SpotiClient = new(token);
|
|
|
|
if (args.Length == 0)
|
|
{
|
|
Console.WriteLine("Please provide spotify link!");
|
|
return;
|
|
}
|
|
|
|
string spotiString = args[0];
|
|
string id = ExtractSpotifyId(spotiString)!;
|
|
|
|
if (spotiString.Contains("track"))
|
|
await ExportSingleTrack(id);
|
|
else if (spotiString.Contains("playlist"))
|
|
await ExportPlaylist(id);
|
|
else
|
|
Console.WriteLine("Link not supported!");
|
|
}
|
|
|
|
static async Task ExportSingleTrack(string id)
|
|
{
|
|
var track = await SpotiClient.Tracks.Get(id);
|
|
await InternalExportTrack(track, $"{track.Artists.First().Name} - {track.Name}");
|
|
}
|
|
|
|
static async Task ExportPlaylist(string id)
|
|
{
|
|
var playlist = await SpotiClient.Playlists.Get(id);
|
|
var trackPage = playlist.Items!;
|
|
|
|
Directory.CreateDirectory($"./{playlist.Name}");
|
|
|
|
using var semaphore = new SemaphoreSlim(5);
|
|
var tasks = new List<Task>();
|
|
int index = 0;
|
|
|
|
await foreach (var playlistTrack in SpotiClient.Paginate(trackPage))
|
|
{
|
|
if (playlistTrack.Item is not FullTrack track)
|
|
continue;
|
|
|
|
int currentIndex = index++;
|
|
|
|
await semaphore.WaitAsync();
|
|
|
|
tasks.Add(Task.Run(async () =>
|
|
{
|
|
try
|
|
{
|
|
await InternalExportTrack(track,$"./{playlist.Name}/[{currentIndex}] {track.Artists.First().Name} - {track.Name}");
|
|
}
|
|
finally
|
|
{
|
|
semaphore.Release();
|
|
}
|
|
}));
|
|
}
|
|
|
|
await Task.WhenAll(tasks);
|
|
}
|
|
|
|
static async Task InternalExportTrack(FullTrack track, string outputName)
|
|
{
|
|
var result = (await TrackMatcher.SearchAsync(track, 3)).First();
|
|
var manifests = await YtClient.Videos.Streams.GetManifestAsync(result.Video.Id);
|
|
var streamInfo = manifests.GetAudioOnlyStreams().GetWithHighestBitrate();
|
|
await Process.Start("ffmpeg.exe", $"-hide_banner -i \"{streamInfo.Url}\" -n \"{outputName}.mp3\"").WaitForExitAsync();
|
|
}
|
|
} |