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(); 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(); } }