Initial commit

This commit is contained in:
2026-10-09 07:59:15 +02:00
commit a6efcd26d0
7 changed files with 745 additions and 0 deletions

97
Program.cs Normal file
View File

@@ -0,0 +1,97 @@
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();
}
}