Make it more complete
This commit is contained in:
+296
-34
@@ -16,14 +16,40 @@ using System.Text;
|
||||
using YoutubeExplode.Search;
|
||||
using YouTubeMusicAPI.Client;
|
||||
using YouTubeMusicAPI.Models.Search;
|
||||
using YouTubeMusicAPI.Pagination;
|
||||
|
||||
|
||||
|
||||
namespace Tesses.YouTubeDownloader2025Companion
|
||||
{
|
||||
public class Program
|
||||
public static class Program
|
||||
{
|
||||
public static string GetName(this List<AlbumSongQuery.AlbumSongQueryMedia.AlbumSongQueryMediaTrack.AlbumSongQueryMediaTrackRecording.AlbumSongQueryMediaTrackRecordingArtists> artists, string songName)
|
||||
{
|
||||
string prefix = "";
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if(artists.Count > 0)
|
||||
{
|
||||
prefix = artists[0].JoinPhrase;
|
||||
builder.Append($"{artists[0].Artist.Name} - ");
|
||||
}
|
||||
|
||||
builder.Append(songName);
|
||||
|
||||
foreach(var aname in artists.Skip(1))
|
||||
{
|
||||
builder.Append(prefix);
|
||||
builder.Append(aname.Artist.Name);
|
||||
prefix = aname.JoinPhrase;
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
public static string VideoTitle {get;set;}="N/A";
|
||||
|
||||
public static string ChannelTitle {get;set;}="N/A";
|
||||
private const string VERSION = "1.0.0.0-dev";
|
||||
|
||||
public const long AdminFlag = 0b00000100;
|
||||
public static async Task InstallExtension()
|
||||
{
|
||||
var cfg = Config;
|
||||
@@ -124,7 +150,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
private static long LastRequestMinute = 0;
|
||||
private static long RequestsInThisMinute = 0;
|
||||
|
||||
private const long RequestsPerMinute = 10;
|
||||
private const long RequestsPerMinute = 15;
|
||||
|
||||
private static bool InRateLimit = false;
|
||||
|
||||
@@ -243,7 +269,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
{
|
||||
using (var db = new Database())
|
||||
{
|
||||
var res = await db.CreateSessionTokenAsync(val.Username);
|
||||
var res = await db.CreateSessionTokenAsync(val.Username, val.Flags);
|
||||
ctx.ResponseHeaders["Set-Cookie"] = [$"Session={res.Key}; SameSite=Lax; Expires={DateTimeOffset.FromUnixTimeSeconds(res.Expires).ToString("R")}; HttpOnly"];
|
||||
|
||||
await RedirectAsync(ctx, "/");
|
||||
@@ -251,6 +277,10 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
}
|
||||
}
|
||||
public static Database.Session? GetSession(this ServerContext ctx)
|
||||
{
|
||||
return ctx.Tag as Database.Session;
|
||||
}
|
||||
public static HttpActionAsync EnsureLoggedin(HttpActionAsync action)
|
||||
{
|
||||
return async (ctx) =>
|
||||
@@ -330,11 +360,13 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
};
|
||||
}
|
||||
public static IEnumerable<PageLink> DefaultLinks()
|
||||
public static IEnumerable<PageLink> DefaultLinks(int index)
|
||||
{
|
||||
yield return new PageLink("Home", "home", "/");
|
||||
yield return new PageLink("Search", "search", "/search");
|
||||
yield return new PageLink("Music", "music_note", "/music");
|
||||
yield return new PageLink("Home", "home", "/", index == 0);
|
||||
yield return new PageLink("Search", "search", "/search", index== 1);
|
||||
yield return new PageLink("Music", "music_note", "/music", index==2);
|
||||
yield return new PageLink("Settings","settings","/settings", index == 3);
|
||||
yield return new PageLink("Log","data_table","/log", index == 4);
|
||||
}
|
||||
private class LoginRequest
|
||||
{
|
||||
@@ -384,6 +416,13 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
Console.WriteLine(JsonConvert.SerializeObject(webhook));
|
||||
switch (webhook.Type)
|
||||
{
|
||||
case "VideoStarted":
|
||||
{
|
||||
VideoTitle = webhook.Video.Title;
|
||||
|
||||
ChannelTitle = webhook.Video.Channel;
|
||||
}
|
||||
break;
|
||||
case "VideoFinished":
|
||||
{
|
||||
|
||||
@@ -426,6 +465,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}, "POST");
|
||||
routeServer.Add("/log", EnsureLoggedin(async (ctx) => await ctx.SendTextAsync(await Pages.LogAsync(ctx))));
|
||||
routeServer.Add("/settings", EnsureLoggedin(SettingsAsync));
|
||||
routeServer.Add("/settings", EnsureLoggedin(SettingsPostAsync),"POST");
|
||||
routeServer.Add("/auth", AuthAsync, "POST");
|
||||
routeServer.Add("/login", LoginAsync);
|
||||
routeServer.Add("/rescan", EnsureLoggedin(async (ctx) =>
|
||||
@@ -447,8 +487,21 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}), "POST");
|
||||
routeServer.Add("/", EnsureLoggedin(async (ctx) =>
|
||||
{
|
||||
var session = ctx.GetSession();
|
||||
var cfg = Config ?? new AuthConfig();
|
||||
await ctx.SendTextAsync(Components.Shell("Home", (await File.ReadAllTextAsync("wwwroot/index.html")).Replace("@TYTD_URL@", HttpUtility.HtmlAttributeEncode($"{cfg.TYTDUrl.TrimEnd('/')}/")), DefaultLinks()));
|
||||
Song? song = null;
|
||||
List<Artist> artists=new List<Artist>();
|
||||
using(var db = new Database())
|
||||
{
|
||||
song = (await db.GetUnavailableSongsAsync(0,1)).FirstOrDefault();
|
||||
|
||||
if(song is not null)
|
||||
{
|
||||
artists.AddRange(await db.GetContributingArtistsAsync(song.SongId));
|
||||
}
|
||||
}
|
||||
|
||||
await ctx.SendTextAsync(Pages.Index(ctx,cfg, song,artists, session?.AccountName ?? ""));
|
||||
|
||||
}));
|
||||
routeServer.Add("/api/v1/login", async (ctx) =>
|
||||
@@ -470,11 +523,11 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
|
||||
using (var db = new Database())
|
||||
{
|
||||
var session = await db.CreateSessionTokenAsync(req.username, false);
|
||||
var session = await db.CreateSessionTokenAsync(req.username, myresp.flags, false);
|
||||
await ctx.SendJsonAsync(new
|
||||
{
|
||||
success = true,
|
||||
token = session.Key
|
||||
token = session.Key,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -487,6 +540,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
success = false
|
||||
});
|
||||
}, "POST");
|
||||
routeServer.Add("/return-to-setup",EnsureLoggedin(ReturnToSetup),"POST");
|
||||
routeServer.Add("/oobe", EnsureLoggedin(async (ctx) =>
|
||||
{
|
||||
await ctx.SendTextAsync(Components.Shell("Setup", await File.ReadAllTextAsync("wwwroot/pages/oobe.html"), []));
|
||||
@@ -527,12 +581,14 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
{
|
||||
if (sso_resp2.Success)
|
||||
{
|
||||
AuthConfig cfg = new AuthConfig();
|
||||
cfg.CompanionUrl = companion;
|
||||
cfg.TYTDUrl = tytd;
|
||||
cfg.Session = tytdLogin.Token;
|
||||
cfg.ServiceName = $"tytdcompanion-{service_name_unique}";
|
||||
cfg.SSOAppKey = sso_app_key;
|
||||
AuthConfig cfg = new AuthConfig
|
||||
{
|
||||
CompanionUrl = companion,
|
||||
TYTDUrl = tytd,
|
||||
Session = tytdLogin.Token,
|
||||
ServiceName = $"tytdcompanion-{service_name_unique}",
|
||||
SSOAppKey = sso_app_key
|
||||
};
|
||||
|
||||
File.WriteAllText(AuthConfigPath, JsonConvert.SerializeObject(cfg));
|
||||
lock (MyLockObject)
|
||||
@@ -579,9 +635,14 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
routeServer.Add("/search_more", EnsureLoggedin(SearchMore));
|
||||
routeServer.Add("/music", EnsureLoggedin(Music));
|
||||
routeServer.Add("/music/artists", EnsureLoggedin(Artists));
|
||||
routeServer.Add("/music/songs", EnsureLoggedin(Songs));
|
||||
routeServer.Add("/music/song", EnsureLoggedin(Song));
|
||||
routeServer.Add("/music/myartists", EnsureLoggedin(MyArtists));
|
||||
routeServer.Add("/music/fix-song", EnsureLoggedin(FixIncorrectGet));
|
||||
routeServer.Add("/music/fix-song", EnsureLoggedin(FixIncorrectPost), "POST");
|
||||
routeServer.Add("/fix-song-index", EnsureLoggedin(FixSongIndexGet));
|
||||
routeServer.Add("/fix-song-index", EnsureLoggedin(FixSongIndexPost), "POST");
|
||||
|
||||
|
||||
routeServer.Add("/music/artist", EnsureLoggedin(Artist));
|
||||
routeServer.Add("/music/myartist", EnsureLoggedin(MyArtist));
|
||||
@@ -601,6 +662,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
routeServer.Add("/api/v1/albums", EnsureLoggedin(ApiAlbums));
|
||||
routeServer.Add("/api/v1/album", EnsureLoggedin(ApiAlbum));
|
||||
routeServer.Add("/api/v1/songs", EnsureLoggedin(ApiSongs));
|
||||
routeServer.Add("/api/v1/unavailable", EnsureLoggedin(ApiUnavailable));
|
||||
routeServer.Add("/api/v1/song", EnsureLoggedin(ApiSong));
|
||||
routeServer.Add("/api/v1/album.zip", EnsureLoggedin(ApiAlbumZip));
|
||||
bool finished = false;
|
||||
@@ -618,7 +680,6 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(10000);
|
||||
Console.WriteLine("Wait expired");
|
||||
await InstallExtension();
|
||||
}).Wait(0);
|
||||
routeServer.StartServer(4255);
|
||||
@@ -626,6 +687,23 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
t.Wait();
|
||||
}
|
||||
|
||||
private static async Task ReturnToSetup(ServerContext ctx)
|
||||
{
|
||||
var session = ctx.GetSession();
|
||||
if(((session?.Flags ?? 0) & AdminFlag) != 0)
|
||||
{
|
||||
lock (MyLockObject)
|
||||
{
|
||||
Config = null;
|
||||
}
|
||||
await RedirectAsync(ctx,"/oobe");
|
||||
}
|
||||
else
|
||||
{
|
||||
await ctx.SendTextAsync(Components.Shell("Unauthorized","<h1>You are unauthorized to do that</h1>", DefaultLinks(3)));
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ApiAlbumZip(ServerContext ctx)
|
||||
{
|
||||
if (ctx.QueryParams.TryGetFirstGuid("albumId", out var albumId))
|
||||
@@ -689,6 +767,22 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
|
||||
}
|
||||
}
|
||||
private static async Task ApiUnavailable(ServerContext ctx)
|
||||
{
|
||||
|
||||
|
||||
if (!ctx.QueryParams.TryGetFirstInt64("offset", out var offset))
|
||||
offset = 0;
|
||||
if (!ctx.QueryParams.TryGetFirstInt64("count", out var count))
|
||||
count = 20;
|
||||
|
||||
using (var db = new Database())
|
||||
{
|
||||
List<object> songs = [.. await db.GetUnavailableSongsAsync(offset * count, count)];
|
||||
await ctx.SendJsonAsync(songs);
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task ApiSongs(ServerContext ctx)
|
||||
{
|
||||
if (!ctx.QueryParams.TryGetFirst("q", out var q))
|
||||
@@ -836,7 +930,34 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
private static async Task SettingsAsync(ServerContext ctx)
|
||||
{
|
||||
await ctx.SendTextAsync(Pages.Settings(ctx));
|
||||
var userInfo = ctx.GetSession();
|
||||
if(((userInfo?.Flags ?? 0) & AdminFlag) != 0)
|
||||
await ctx.SendTextAsync(Pages.Settings(ctx));
|
||||
else
|
||||
await ctx.SendTextAsync(Components.Shell("Unauthorized","<h1>You are unauthorized to enter settings</h1>",DefaultLinks(3)));
|
||||
}
|
||||
private static async Task SettingsPostAsync(ServerContext ctx)
|
||||
{
|
||||
ctx.ParseBody();
|
||||
var userInfo = ctx.GetSession();
|
||||
if(((userInfo?.Flags ?? 0) & AdminFlag) != 0)
|
||||
{
|
||||
var tytdthumbnail = ctx.QueryParams.GetFirstBoolean("tytdthumbnail");
|
||||
lock(MyLockObject)
|
||||
{
|
||||
var cfg = Config;
|
||||
if(cfg is not null)
|
||||
{
|
||||
cfg.ThumbnailsThroughTYTD = tytdthumbnail;
|
||||
|
||||
File.WriteAllText(AuthConfigPath, JsonConvert.SerializeObject(cfg));
|
||||
}
|
||||
}
|
||||
|
||||
await RedirectAsync(ctx,"/settings");
|
||||
}
|
||||
else
|
||||
await ctx.SendTextAsync(Components.Shell("Unauthorized","<h1>You are unauthorized to enter settings</h1>",DefaultLinks(3)));
|
||||
}
|
||||
|
||||
private static async Task AddAlbum(ServerContext ctx)
|
||||
@@ -878,6 +999,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static async Task<VideoId?> GetVideoIdFromSong(AlbumSongQuery.AlbumSongQueryMedia.AlbumSongQueryMediaTrack.AlbumSongQueryMediaTrackRecording recording, string albumName)
|
||||
{
|
||||
using (var db = new Database())
|
||||
@@ -886,19 +1008,9 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
if (vid.HasValue) return vid;
|
||||
}
|
||||
|
||||
string firstArtist = "";
|
||||
string name = "";
|
||||
foreach (var item in recording.ContributingArtists)
|
||||
{
|
||||
if (firstArtist.Length == 0) firstArtist = item.Artist.Name;
|
||||
name += item.Artist.Name;
|
||||
name += item.JoinPhrase;
|
||||
}
|
||||
|
||||
name += $" - \"{albumName}\" - \"{recording.Name}\"";
|
||||
|
||||
|
||||
await foreach (var item in YouTubeMusicClient.SearchAsync(name, YouTubeMusicAPI.Models.Search.SearchCategory.Songs))
|
||||
await foreach (var item in YouTubeMusicClient.SearchAsync(recording.ContributingArtists.GetName(recording.Name), YouTubeMusicAPI.Models.Search.SearchCategory.Songs))
|
||||
{
|
||||
if (item is SongSearchResult ssr)
|
||||
{
|
||||
@@ -1259,7 +1371,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public static async Task Music(ServerContext ctx)
|
||||
{
|
||||
await ctx.SendTextAsync(Components.Shell("Music", await File.ReadAllTextAsync("wwwroot/music.html"), DefaultLinks()));
|
||||
await ctx.SendTextAsync(Components.Shell("Music", await File.ReadAllTextAsync("wwwroot/music.html"), DefaultLinks(2)));
|
||||
}
|
||||
|
||||
public static async Task MyArtist(ServerContext ctx)
|
||||
@@ -1296,7 +1408,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
{
|
||||
foreach (var item in albumQuery.Albums)
|
||||
{
|
||||
links.Add(new AlbumLink(item.Name, item.Id.ToString(), $"/api/v1/album-thumbnail?id={HttpUtility.UrlEncode(item.Id.ToString())}"));
|
||||
links.Add(new AlbumLink(item.Name, item.Id.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1309,6 +1421,40 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
*/
|
||||
}
|
||||
public static async Task FixSongIndexPost(ServerContext ctx)
|
||||
{
|
||||
ctx.ParseBody();
|
||||
VideoId? myId;
|
||||
if (ctx.QueryParams.TryGetFirstGuid("songId", out var songId) && ctx.QueryParams.TryGetFirst("url", out var url) && (myId = VideoId.TryParse(url)).HasValue)
|
||||
{
|
||||
|
||||
var info = await YouTubeClient.Videos.GetAsync(myId.Value);
|
||||
using (var db = new Database())
|
||||
{
|
||||
VideoId? vid = await db.GetVideoIdAsync(songId.ToString());
|
||||
if (vid.HasValue)
|
||||
{
|
||||
string path = $"data/MusicFromTYTD/{vid.Value.Value}.bin";
|
||||
if (File.Exists(path)) File.Delete(path);
|
||||
}
|
||||
await db.UpdateVideoIdAsync(songId.ToString(), myId.Value.Value, info.Title);
|
||||
await foreach (var rel in db.GetMediaEntriesAsync(songId.ToString()))
|
||||
{
|
||||
string path = rel.ToString();
|
||||
if (File.Exists(path)) File.Delete(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AddVideo(myId.Value.Value, TYTDResolution.AudioOnly);
|
||||
//await ctx.SendTextAsync($"<p>{HttpUtility.HtmlEncode($"SongId: {songId}, AlbumId: {albumId}, Url: {url}")}</p>");
|
||||
|
||||
|
||||
await RedirectAsync(ctx, "/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static async Task FixIncorrectPost(ServerContext ctx)
|
||||
{
|
||||
ctx.ParseBody();
|
||||
@@ -1342,6 +1488,35 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
|
||||
}
|
||||
public static async Task FixSongIndexGet(ServerContext ctx)
|
||||
{
|
||||
if (ctx.QueryParams.TryGetFirstGuid("id", out var songId))
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
using (var db = new Database())
|
||||
{
|
||||
var song = await db.GetSongAsync(songId.ToString());
|
||||
if (song is not null)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var artist in await db.GetContributingArtistsAsync(songId.ToString()))
|
||||
{
|
||||
if (i == 0)
|
||||
builder.Append($"{artist.ArtistName} - {song.SongName}");
|
||||
else if (i == 1)
|
||||
builder.Append($" feat {artist.ArtistName}");
|
||||
else
|
||||
builder.Append($", {artist.ArtistName}");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
await ctx.SendTextAsync(Pages.FixSongIndex(songId.ToString(), builder.ToString()));
|
||||
}
|
||||
}
|
||||
public static async Task FixIncorrectGet(ServerContext ctx)
|
||||
{
|
||||
if (ctx.QueryParams.TryGetFirstGuid("songId", out var songId) && ctx.QueryParams.TryGetFirstGuid("albumId", out var albumId))
|
||||
@@ -1397,7 +1572,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
html = Pages.MySong(song, album, video, cfg.TYTDUrl, File.Exists(file.ToString()), trk.Track, TimeSpan.FromSeconds(trk.Length / 1000), artists.ToList());
|
||||
}
|
||||
else
|
||||
html = Components.Shell("Song not found", "<h1>Song not found</h1>", DefaultLinks());
|
||||
html = Components.Shell("Song not found", "<h1>Song not found</h1>", DefaultLinks(2));
|
||||
}
|
||||
|
||||
await ctx.SendTextAsync(html);
|
||||
@@ -1405,22 +1580,41 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
else
|
||||
{
|
||||
List<MediaEntry> entries = new List<MediaEntry>();
|
||||
List<Artist> artists=new List<Artist>();
|
||||
Song? song = null;
|
||||
using (var db = new Database())
|
||||
{
|
||||
|
||||
await foreach (var item in db.GetMediaEntriesAsync(songId.ToString()))
|
||||
{
|
||||
entries.Add(item);
|
||||
}
|
||||
|
||||
if(entries.Count > 1)
|
||||
{
|
||||
song = await db.GetSongAsync(songId.ToString());
|
||||
foreach(var item in await db.GetContributingArtistsAsync(songId.ToString()))
|
||||
{
|
||||
artists.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entries.Count == 0)
|
||||
{
|
||||
ctx.SendText(Components.Shell("Song not found", "<h1>Song not found</h1>", DefaultLinks()));
|
||||
await ctx.SendTextAsync(Components.Shell("Song not found", "<h1>Song not found</h1>", DefaultLinks(2)));
|
||||
}
|
||||
else if (entries.Count == 1)
|
||||
{
|
||||
await RedirectAsync(ctx, $"/music/mysong?songId={HttpUtility.UrlEncode(songId.ToString())}&albumId={HttpUtility.UrlEncode(entries[0].AlbumId)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(song is not null)
|
||||
await ctx.SendTextAsync(Pages.MySongAlbums(entries, song, artists));
|
||||
else
|
||||
await ctx.SendTextAsync(Components.Shell("Song not found", "<h1>Song not found</h1>", DefaultLinks(2)));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1506,6 +1700,74 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
|
||||
await ctx.SendTextAsync(Pages.MyAlbumListing(q, links, page));
|
||||
}
|
||||
public static async Task Song(ServerContext ctx)
|
||||
{
|
||||
if(ctx.QueryParams.TryGetFirstGuid("id",out var id))
|
||||
{
|
||||
|
||||
var songResp = await MusicBrainzRequestAsync<MusicBrainzSongInfo>($"/ws/2/recording/{HttpUtility.UrlEncode(id.ToString())}/?fmt=json&inc=artist-credits+releases");
|
||||
if(songResp is not null)
|
||||
{
|
||||
|
||||
|
||||
|
||||
List<AlbumLink> links = new List<AlbumLink>();
|
||||
|
||||
foreach(var item in songResp.Albums)
|
||||
{
|
||||
links.Add(new AlbumLink(item.Name, item.Id.ToString()));
|
||||
}
|
||||
await ctx.SendTextAsync(Pages.SongAlbums(songResp.ContributingArtists.GetName(songResp.Name),links));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
public static async Task Songs(ServerContext ctx)
|
||||
{
|
||||
List<ArtistLink> links = new List<ArtistLink>();
|
||||
string q = "";
|
||||
int page = 1;
|
||||
List<SongLink> songs = new List<SongLink>();
|
||||
if (ctx.QueryParams.TryGetFirst("q", out q))
|
||||
{
|
||||
|
||||
if (!ctx.QueryParams.TryGetFirstInt32("page", out page))
|
||||
page = 1;
|
||||
|
||||
var songQuery = await MusicBrainzRequestAsync<SongQuery>($"/ws/2/recording?query={HttpUtility.UrlEncode(q)}&fmt=json&offset={(page - 1) * 25}&limit=25");
|
||||
if (songQuery is not null)
|
||||
{
|
||||
|
||||
|
||||
foreach(var item in songQuery.Recordings)
|
||||
{
|
||||
string prefix = "";
|
||||
StringBuilder builder=new StringBuilder();
|
||||
if(item.ContributingArtists.Count > 0)
|
||||
{
|
||||
prefix = item.ContributingArtists[0].JoinPhrase;
|
||||
builder.Append($"{item.ContributingArtists[0].Artist.Name} - ");
|
||||
}
|
||||
|
||||
builder.Append(item.Name);
|
||||
|
||||
foreach(var aname in item.ContributingArtists.Skip(1))
|
||||
{
|
||||
builder.Append(prefix);
|
||||
builder.Append(aname.Artist.Name);
|
||||
prefix = aname.JoinPhrase;
|
||||
}
|
||||
|
||||
songs.Add(new SongLink(builder.ToString(),item.Id.ToString()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
await ctx.SendTextAsync(Pages.SongListing(q,songs,page));
|
||||
}
|
||||
|
||||
public static async Task Artists(ServerContext ctx)
|
||||
{
|
||||
|
||||
@@ -1645,7 +1907,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
{
|
||||
foreach (var item in albumQuery.Albums)
|
||||
{
|
||||
links.Add(new AlbumLink(item.Name, item.Id.ToString(), $"/api/v1/album-thumbnail?id={HttpUtility.UrlEncode(item.Id.ToString())}"));
|
||||
links.Add(new AlbumLink(item.Name, item.Id.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user