Add more apis
This commit is contained in:
+50
-26
@@ -34,7 +34,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task<IEnumerable<LogEntry>> GetLogEntriesAsync(int offset = 0, int count = 20)
|
||||
{
|
||||
return await conn.QueryAsync<LogEntry>("SELECT * FROM tytd_events ORDER BY Time DESC LIMIT @Count OFFSET @Offset",new DynamicParameters(new{
|
||||
return await conn.QueryAsync<LogEntry>("SELECT * FROM tytd_events ORDER BY Time DESC LIMIT @Count OFFSET @Offset", new DynamicParameters(new
|
||||
{
|
||||
Offset = offset,
|
||||
Count = count
|
||||
}));
|
||||
@@ -86,11 +87,11 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
yield return ent;
|
||||
}
|
||||
}
|
||||
public async Task<Session> CreateSessionTokenAsync(string username)
|
||||
public async Task<Session> CreateSessionTokenAsync(string username, bool expires = true)
|
||||
{
|
||||
Session session = new Session();
|
||||
session.AccountName = username;
|
||||
session.Expires = Program.NowEpoch + Expires;
|
||||
session.Expires = expires ? Program.NowEpoch + Expires : 0;
|
||||
session.Key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
||||
|
||||
await conn.ExecuteAsync("INSERT INTO sessions (AccountName,Key,Expires) VALUES (@AccountName,@Key,@Expires);", session);
|
||||
@@ -117,28 +118,36 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task AddVideoIdAsync(string songId, VideoId id, string title)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
await conn.ExecuteAsync($"INSERT INTO song_videos (SongId, VideoId, VideoTitle) VALUES (@SongId,@VideoId,@VideoTitle);", new DynamicParameters(new { SongId = songId, VideoId = id.Value, VideoTitle = title }));
|
||||
}catch(Exception){}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task UpdateVideoIdAsync(string songId, VideoId id, string title)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
|
||||
await conn.ExecuteAsync($"INSERT OR REPLACE INTO song_videos (SongId, VideoId, VideoTitle) VALUES (@SongId,@VideoId,@VideoTitle);", new DynamicParameters(new { SongId = songId, VideoId = id.Value, VideoTitle = title }));
|
||||
}catch(Exception){}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task AddAlbumArtistAsync(string albumId, string artistId)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
await conn.ExecuteAsync($"INSERT INTO album_artists (AlbumId, ArtistId) VALUES (@AlbumId,@ArtistId);", new DynamicParameters(new { AlbumId = albumId, ArtistId = artistId }));
|
||||
}catch(Exception){}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task AddContributingArtistAsync(string songId, string artistId)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
await conn.ExecuteAsync($"INSERT INTO song_artists (SongId, ArtistId) VALUES (@SongId,@ArtistId);", new DynamicParameters(new { SongId = songId, ArtistId = artistId }));
|
||||
}catch(Exception){}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task<SongVideo?> GetSongVideoFromVideoIdAsync(VideoId id)
|
||||
{
|
||||
@@ -150,27 +159,35 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task AddArtistAsync(Artist artist)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
await conn.ExecuteAsync("INSERT INTO artists (ArtistId,ArtistName,ArtistDisambiguation) VALUES (@ArtistId,@ArtistName,@ArtistDisambiguation);", artist);
|
||||
}catch(Exception) {}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task AddAlbumAsync(Album album)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
await conn.ExecuteAsync("INSERT INTO albums (AlbumId,AlbumName,AlbumReleaseDate) VALUES (@AlbumId,@AlbumName,@AlbumReleaseDate);", album);
|
||||
}catch(Exception) {}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task AddSongAsync(Song song)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
await conn.ExecuteAsync("INSERT INTO songs (SongId,SongName) VALUES (@SongId,@SongName);", song);
|
||||
}catch(Exception) {}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task AddTrackAsync(AlbumTrack track)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
await conn.ExecuteAsync("INSERT INTO album_tracks (AlbumId,SongId,Track,Length) VALUES (@AlbumId,@SongId,@Track,@Length);", track);
|
||||
}catch(Exception) {}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
public async Task<Song?> GetSongAsync(string id)
|
||||
{
|
||||
@@ -183,7 +200,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task<IEnumerable<Artist>> GetArtistsAsync(string q = "", long offset = 0, long count = 20)
|
||||
{
|
||||
return await conn.QueryAsync<Artist>("SELECT * FROM artists WHERE ArtistName LIKE @Query LIMIT @Count OFFSET @Offset;",new DynamicParameters(new{
|
||||
return await conn.QueryAsync<Artist>("SELECT * FROM artists WHERE ArtistName LIKE @Query LIMIT @Count OFFSET @Offset;", new DynamicParameters(new
|
||||
{
|
||||
Query = $"%{q}%",
|
||||
Offset = offset,
|
||||
Count = count
|
||||
@@ -191,7 +209,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task<IEnumerable<Song>> GetSongsAsync(string q = "", long offset = 0, long count = 20)
|
||||
{
|
||||
return await conn.QueryAsync<Song>("SELECT * FROM songs WHERE SongName LIKE @Query LIMIT @Count OFFSET @Offset;",new DynamicParameters(new{
|
||||
return await conn.QueryAsync<Song>("SELECT * FROM songs WHERE SongName LIKE @Query LIMIT @Count OFFSET @Offset;", new DynamicParameters(new
|
||||
{
|
||||
Query = $"%{q}%",
|
||||
Offset = offset,
|
||||
Count = count
|
||||
@@ -199,7 +218,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task<IEnumerable<Album>> GetAlbumsFromArtistAsync(string artistId, string q = "", long offset = 0, long count = 20)
|
||||
{
|
||||
return await conn.QueryAsync<Album>("SELECT * FROM albums al INNER JOIN album_artists ar ON al.AlbumId = ar.AlbumId WHERE AlbumName LIKE @Query AND ar.ArtistId = @ArtistId ORDER BY al.AlbumReleaseDate, al.Id ASC LIMIT @Count OFFSET @Offset;",new DynamicParameters(new{
|
||||
return await conn.QueryAsync<Album>("SELECT * FROM albums al INNER JOIN album_artists ar ON al.AlbumId = ar.AlbumId WHERE AlbumName LIKE @Query AND ar.ArtistId = @ArtistId ORDER BY al.AlbumReleaseDate, al.Id ASC LIMIT @Count OFFSET @Offset;", new DynamicParameters(new
|
||||
{
|
||||
Query = $"%{q}%",
|
||||
Offset = offset,
|
||||
Count = count,
|
||||
@@ -208,7 +228,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task<IEnumerable<Album>> GetAlbumsAsync(string q = "", long offset = 0, long count = 20)
|
||||
{
|
||||
return await conn.QueryAsync<Album>("SELECT * FROM albums WHERE AlbumName LIKE @Query ORDER BY AlbumReleaseDate, Id ASC LIMIT @Count OFFSET @Offset;",new DynamicParameters(new{
|
||||
return await conn.QueryAsync<Album>("SELECT * FROM albums WHERE AlbumName LIKE @Query ORDER BY AlbumReleaseDate, Id ASC LIMIT @Count OFFSET @Offset;", new DynamicParameters(new
|
||||
{
|
||||
Query = $"%{q}%",
|
||||
Offset = offset,
|
||||
Count = count
|
||||
@@ -234,9 +255,11 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public async Task<AlbumTrack?> GetTrackAsync(string songId, string albumId)
|
||||
{
|
||||
return (await conn.QueryAsync<AlbumTrack>("SELECT * FROM album_tracks WHERE AlbumId = @AlbumId AND SongId = @SongId", new DynamicParameters(new{
|
||||
return (await conn.QueryAsync<AlbumTrack>("SELECT * FROM album_tracks WHERE AlbumId = @AlbumId AND SongId = @SongId", new DynamicParameters(new
|
||||
{
|
||||
SongId = songId,
|
||||
AlbumId = albumId}))).FirstOrDefault();
|
||||
AlbumId = albumId
|
||||
}))).FirstOrDefault();
|
||||
}
|
||||
public async Task<IEnumerable<AlbumSongTrack>> GetTracksAsync(string albumId)
|
||||
{
|
||||
@@ -281,7 +304,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
public async Task AddEventAsync(long epoch, Webhook webhook)
|
||||
{
|
||||
//conn.Execute("CREATE TABLE IF NOT EXISTS tytd_events (Id INTEGER PRIMARY KEY AUTOINCREMENT, Time INTEGER, Type TEXT, VideoTitle TEXT, VideoId TEXT, ChannelTitle TEXT, ChannelId TEXT);");
|
||||
await conn.ExecuteAsync("INSERT INTO tytd_events (Time,Type,VideoTitle,VideoId,ChannelTitle,ChannelId) VALUES (@Time,@Type,@VideoTitle,@VideoId,@ChannelTitle,@ChannelId);", new DynamicParameters(new {
|
||||
await conn.ExecuteAsync("INSERT INTO tytd_events (Time,Type,VideoTitle,VideoId,ChannelTitle,ChannelId) VALUES (@Time,@Type,@VideoTitle,@VideoId,@ChannelTitle,@ChannelId);", new DynamicParameters(new
|
||||
{
|
||||
Time = epoch,
|
||||
Type = webhook.Type,
|
||||
VideoTitle = webhook.Video.Title,
|
||||
|
||||
+236
-33
@@ -82,7 +82,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
public static void AddVideo(VideoId id, TYTDResolution res = TYTDResolution.LowVideo)
|
||||
{
|
||||
|
||||
EnqueueTask(async()=>{
|
||||
EnqueueTask(async () =>
|
||||
{
|
||||
var cfg = Config ?? new AuthConfig();
|
||||
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Post, $"{cfg.TYTDUrl.TrimEnd('/')}/api/v1/add");
|
||||
|
||||
@@ -203,6 +204,13 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public static async Task AuthRedirectAsync(ServerContext ctx)
|
||||
{
|
||||
if (ctx.UrlPath.StartsWith("/api/v1/"))
|
||||
{
|
||||
ctx.StatusCode = 401;
|
||||
await ctx.WriteHeadersAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
string tmpToken = RandomNumberGenerator.GetHexString(24);
|
||||
@@ -245,7 +253,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
public static HttpActionAsync EnsureLoggedin(HttpActionAsync action)
|
||||
{
|
||||
return async(ctx)=>{
|
||||
return async (ctx) =>
|
||||
{
|
||||
AuthConfig? cfg;
|
||||
lock (MyLockObject)
|
||||
{
|
||||
@@ -284,6 +293,27 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
await AuthRedirectAsync(ctx);
|
||||
return;
|
||||
}
|
||||
else if (ctx.RequestHeaders.TryGetFirst("Authorization", out var auth))
|
||||
{
|
||||
var authParts = auth.Split(" ", 2);
|
||||
if (authParts.Length == 2 && authParts[0] == "Bearer")
|
||||
{
|
||||
using (var db = new Database())
|
||||
{
|
||||
var session = await db.GetSessionAsync(ctx, authParts[1]);
|
||||
if (session is not null)
|
||||
{
|
||||
ctx.Tag = session;
|
||||
await action(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx.StatusCode = 401;
|
||||
await ctx.WriteHeadersAsync();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
await AuthRedirectAsync(ctx);
|
||||
@@ -306,6 +336,16 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
yield return new PageLink("Search", "search", "/search");
|
||||
yield return new PageLink("Music", "music_note", "/music");
|
||||
}
|
||||
private class LoginRequest
|
||||
{
|
||||
public string username { get; set; } = "";
|
||||
public string password { get; set; } = "";
|
||||
}
|
||||
private class AuthResponse
|
||||
{
|
||||
public bool success { get; set; } = false;
|
||||
public long flags { get; set; } = 0;
|
||||
}
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
Directory.CreateDirectory("data/Config");
|
||||
@@ -323,7 +363,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
|
||||
RouteServer routeServer = new RouteServer(new StaticServer("wwwroot"));
|
||||
routeServer.Add("/api/v1/webhook",async(ctx)=>{
|
||||
routeServer.Add("/api/v1/webhook", async (ctx) =>
|
||||
{
|
||||
string auth = "";
|
||||
ctx.RequestHeaders.TryGetFirst("Authorization", out auth);
|
||||
|
||||
@@ -332,7 +373,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
|
||||
if (string.IsNullOrWhiteSpace(cfg.Session) || (authHdr.Length == 2 && authHdr[0] == "TYTD" && cfg.SSOAppKey == authHdr[1]))
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
var webhook = await ctx.ReadJsonAsync<Webhook>();
|
||||
if (webhook is not null)
|
||||
{
|
||||
@@ -346,7 +388,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
{
|
||||
|
||||
var vid = VideoId.TryParse(webhook.Video.VideoId);
|
||||
EnqueueTask(async()=>{
|
||||
EnqueueTask(async () =>
|
||||
{
|
||||
if (vid.HasValue)
|
||||
await ProcessSongAsync(vid.Value);
|
||||
});
|
||||
@@ -354,7 +397,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
break;
|
||||
case "Started":
|
||||
{
|
||||
EnqueueTask(async()=>{
|
||||
EnqueueTask(async () =>
|
||||
{
|
||||
using (var db = new Database())
|
||||
{
|
||||
foreach (var item in await db.GetSongVideosAsync())
|
||||
@@ -371,7 +415,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
|
||||
}
|
||||
} catch(Exception ex)
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
@@ -383,9 +428,11 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
routeServer.Add("/settings", EnsureLoggedin(SettingsAsync));
|
||||
routeServer.Add("/auth", AuthAsync, "POST");
|
||||
routeServer.Add("/login", LoginAsync);
|
||||
routeServer.Add("/rescan",EnsureLoggedin(async(ctx)=>{
|
||||
routeServer.Add("/rescan", EnsureLoggedin(async (ctx) =>
|
||||
{
|
||||
ctx.ParseBody();
|
||||
EnqueueTask(async()=>{
|
||||
EnqueueTask(async () =>
|
||||
{
|
||||
using (var db = new Database())
|
||||
{
|
||||
foreach (var item in await db.GetSongVideosAsync())
|
||||
@@ -398,19 +445,59 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
});
|
||||
await ctx.WithStatusCode(System.Net.HttpStatusCode.ExpectationFailed).WriteHeadersAsync();
|
||||
}), "POST");
|
||||
routeServer.Add("/",EnsureLoggedin(async(ctx)=>{
|
||||
routeServer.Add("/", EnsureLoggedin(async (ctx) =>
|
||||
{
|
||||
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()));
|
||||
|
||||
}));
|
||||
routeServer.Add("/oobe",EnsureLoggedin(async(ctx)=>{
|
||||
routeServer.Add("/api/v1/login", async (ctx) =>
|
||||
{
|
||||
ctx.StatusCode = 200;
|
||||
var cfg = Program.Config ?? new AuthConfig();
|
||||
var req = await ctx.ReadJsonAsync<LoginRequest>();
|
||||
if (req is not null)
|
||||
{
|
||||
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Post, $"{cfg.TYTDUrl.TrimEnd('/')}/api/v1/auth");
|
||||
msg.Content = new StringContent(JsonConvert.SerializeObject(req), Encoding.UTF8, "application/json");
|
||||
using var resp = await Program.MyHttpClient.SendAsync(msg);
|
||||
if (resp.IsSuccessStatusCode)
|
||||
{
|
||||
var myresp = await resp.Content.ReadFromJsonAsync<AuthResponse>();
|
||||
if (myresp is not null && myresp.success)
|
||||
{
|
||||
//we succeeded
|
||||
|
||||
using (var db = new Database())
|
||||
{
|
||||
var session = await db.CreateSessionTokenAsync(req.username, false);
|
||||
await ctx.SendJsonAsync(new
|
||||
{
|
||||
success = true,
|
||||
token = session.Key
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await ctx.SendJsonAsync(new
|
||||
{
|
||||
success = false
|
||||
});
|
||||
}, "POST");
|
||||
routeServer.Add("/oobe", EnsureLoggedin(async (ctx) =>
|
||||
{
|
||||
await ctx.SendTextAsync(Components.Shell("Setup", await File.ReadAllTextAsync("wwwroot/pages/oobe.html"), []));
|
||||
}));
|
||||
routeServer.Add("/oobe",EnsureLoggedin(async(ctx)=>{
|
||||
routeServer.Add("/oobe", EnsureLoggedin(async (ctx) =>
|
||||
{
|
||||
ctx.ParseBody();
|
||||
if (ctx.QueryParams.TryGetFirst("companion", out var companion) && ctx.QueryParams.TryGetFirst("tytd", out var tytd) && ctx.QueryParams.TryGetFirst("user", out var user) && ctx.QueryParams.TryGetFirst("pass", out var pass))
|
||||
{
|
||||
using var resp = await MyHttpClient.PostAsync($"{tytd.TrimEnd('/')}/api/v1/login",JsonContent.Create(new {
|
||||
using var resp = await MyHttpClient.PostAsync($"{tytd.TrimEnd('/')}/api/v1/login", JsonContent.Create(new
|
||||
{
|
||||
username = user,
|
||||
password = pass
|
||||
}));
|
||||
@@ -423,7 +510,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
{
|
||||
var service_name_unique = RandomNumberGenerator.GetHexString(24);
|
||||
var sso_app_key = RandomNumberGenerator.GetHexString(64);
|
||||
var register_req = JsonContent.Create(new{
|
||||
var register_req = JsonContent.Create(new
|
||||
{
|
||||
service_name = $"tytdcompanion-{service_name_unique}",
|
||||
sso_app_key,
|
||||
service_pretty_name = "TYTD Companion",
|
||||
@@ -454,7 +542,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
await InstallExtension();
|
||||
await RedirectAsync(ctx, "/");
|
||||
return;
|
||||
} else if(sso_resp2.Type != "db")
|
||||
}
|
||||
else if (sso_resp2.Type != "db")
|
||||
{
|
||||
throw new Exception($"Error of {sso_resp2.Type}, {sso_resp2.Reason}");
|
||||
}
|
||||
@@ -468,7 +557,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}), "POST");
|
||||
routeServer.Add("/search", EnsureLoggedin(Search));
|
||||
routeServer.Add("/search_dialog", EnsureLoggedin(SearchDialog));
|
||||
routeServer.Add("/search_button",EnsureLoggedin(async(ctx)=>{
|
||||
routeServer.Add("/search_button", EnsureLoggedin(async (ctx) =>
|
||||
{
|
||||
|
||||
ctx.ParseBody();
|
||||
if (ctx.QueryParams.TryGetFirst("type", out var type))
|
||||
@@ -502,6 +592,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
routeServer.Add("/music/mysongs", EnsureLoggedin(MySongs));
|
||||
routeServer.Add("/music/mysong", EnsureLoggedin(MySong));
|
||||
routeServer.Add("/music/album/add", EnsureLoggedin(AddAlbum), "POST");
|
||||
routeServer.Add("/api/v1/album", EnsureLoggedin(ApiAddAlbum), "POST");
|
||||
routeServer.Add("/api/v1/video-thumbnail", EnsureLoggedin(VideoThumbnail));
|
||||
routeServer.Add("/api/v1/artist-thumbnail", EnsureLoggedin(ArtistThumbnail));
|
||||
routeServer.Add("/api/v1/album-thumbnail", EnsureLoggedin(AlbumThumbnail));
|
||||
@@ -509,20 +600,23 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
routeServer.Add("/api/v1/artist", EnsureLoggedin(ApiArtist));
|
||||
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/song", EnsureLoggedin(ApiSong));
|
||||
routeServer.Add("/api/v1/album.zip", EnsureLoggedin(ApiAlbumZip));
|
||||
bool finished = false;
|
||||
|
||||
RegisterEventable(SearchResultHandler);
|
||||
RegisterEventable(tasks);
|
||||
Task t = Task.Factory.StartNew(async()=>{
|
||||
Task t = Task.Factory.StartNew(async () =>
|
||||
{
|
||||
while (!finished)
|
||||
{
|
||||
foreach (var item in eventables)
|
||||
await item.EventAsync();
|
||||
}
|
||||
});
|
||||
Task.Run(async()=>{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(10000);
|
||||
Console.WriteLine("Wait expired");
|
||||
await InstallExtension();
|
||||
@@ -585,10 +679,51 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
count = 20;
|
||||
|
||||
using (var db = new Database())
|
||||
await ctx.WithMimeType("application/json").SendJsonAsync((await db.GetAlbumsFromArtistAsync(artistId.ToString(),q,offset,count)).ToList());
|
||||
await ctx.WithMimeType("application/json").SendJsonAsync(
|
||||
new
|
||||
{
|
||||
Albums = (await db.GetAlbumsFromArtistAsync(artistId.ToString(), q, offset, count)).ToList(),
|
||||
ArtistName = (await db.GetArtistAsync(artistId.ToString()))?.ArtistName ?? ""
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
private static async Task ApiSongs(ServerContext ctx)
|
||||
{
|
||||
if (!ctx.QueryParams.TryGetFirst("q", out var q))
|
||||
q = "";
|
||||
|
||||
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 = new List<object>();
|
||||
foreach (var item in await db.GetSongsAsync(q, offset * count, count))
|
||||
{
|
||||
List<object> entries = new List<object>();
|
||||
await foreach (var entry in db.GetMediaEntriesAsync(item.SongId))
|
||||
{
|
||||
entries.Add(new
|
||||
{
|
||||
Entry = entry,
|
||||
RelativePathOnServer = entry.ToString(),
|
||||
Exists = File.Exists(entry.ToString())
|
||||
});
|
||||
}
|
||||
|
||||
songs.Add(new
|
||||
{
|
||||
Song = item,
|
||||
MediaEntries = entries
|
||||
});
|
||||
}
|
||||
await ctx.SendJsonAsync(songs);
|
||||
}
|
||||
}
|
||||
private static async Task ApiAlbums(ServerContext ctx)
|
||||
{
|
||||
if (!ctx.QueryParams.TryGetFirst("q", out var q))
|
||||
@@ -600,38 +735,62 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
count = 20;
|
||||
|
||||
using (var db = new Database())
|
||||
await ctx.WithMimeType("application/json").SendJsonAsync((await db.GetAlbumsAsync(q,offset,count)).ToList());
|
||||
await ctx.WithMimeType("application/json").SendJsonAsync((await db.GetAlbumsAsync(q, offset * 20, count)).ToList());
|
||||
}
|
||||
private static async Task ApiAlbum(ServerContext ctx)
|
||||
{
|
||||
if (ctx.QueryParams.TryGetFirstGuid("albumId", out var albumId))
|
||||
using (var db = new Database())
|
||||
{
|
||||
bool hasAny = false;
|
||||
List<object> tracks = new List<object>();
|
||||
foreach (var item in await db.GetTracksAsync(albumId.ToString()))
|
||||
{
|
||||
var artists = await db.GetContributingArtistsAsync(item.SongId);
|
||||
tracks.Add(new {
|
||||
var me = await db.GetMediaEntryAsync(albumId.ToString(), item.SongId);
|
||||
string status = "Unavailable";
|
||||
if (me is not null)
|
||||
{
|
||||
bool hasSong = File.Exists(me.ToString());
|
||||
if (hasSong)
|
||||
{
|
||||
status = "Downloaded";
|
||||
}
|
||||
else if ((await db.GetSongVideoFromSongIdAsync(item.SongId)) is not null)
|
||||
{
|
||||
status = "Downloading";
|
||||
}
|
||||
if (hasSong) hasAny = true;
|
||||
}
|
||||
tracks.Add(new
|
||||
{
|
||||
item.SongId,
|
||||
item.SongName,
|
||||
item.Track,
|
||||
ContributingArtists = artists.ToList()
|
||||
item.Length,
|
||||
LengthString = TimeSpan.FromSeconds(item.Length / 1000).ToString("m\\:ss"),
|
||||
ContributingArtists = artists.ToList(),
|
||||
Status = status
|
||||
});
|
||||
}
|
||||
|
||||
var album = await db.GetAlbumAsync(albumId.ToString());
|
||||
var albumArtists = await db.GetAlbumArtistsAsync(albumId.ToString());
|
||||
await ctx.WithMimeType("application/json").SendJsonAsync(new {
|
||||
await ctx.WithMimeType("application/json").SendJsonAsync(new
|
||||
{
|
||||
Album = album,
|
||||
AlbumArtists = albumArtists,
|
||||
Tracks = tracks
|
||||
Tracks = tracks,
|
||||
HasAny = hasAny
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
private static async Task ApiSong(ServerContext ctx)
|
||||
{
|
||||
if(ctx.QueryParams.TryGetFirstGuid("albumId",out var albumId) && ctx.QueryParams.TryGetFirstGuid("songId",out var songId))
|
||||
if (ctx.QueryParams.TryGetFirstGuid("songId", out var songId))
|
||||
{
|
||||
if (ctx.QueryParams.TryGetFirstGuid("albumId", out var albumId))
|
||||
{
|
||||
var inline = ctx.QueryParams.GetFirstBoolean("inline");
|
||||
MediaEntry? mId = null;
|
||||
@@ -653,7 +812,25 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var db = new Database())
|
||||
{
|
||||
List<object> entries = new List<object>();
|
||||
await foreach (var item in db.GetMediaEntriesAsync(songId.ToString()))
|
||||
{
|
||||
entries.Add(new
|
||||
{
|
||||
Entry = item,
|
||||
RelativePathOnServer = item.ToString(),
|
||||
Exists = File.Exists(item.ToString())
|
||||
});
|
||||
}
|
||||
|
||||
await ctx.SendJsonAsync(entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
await ctx.SendNotFoundAsync();
|
||||
}
|
||||
@@ -667,13 +844,31 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
ctx.ParseBody();
|
||||
if (ctx.QueryParams.TryGetFirstGuid("id", out var id))
|
||||
{
|
||||
EnqueueTask(async()=>{
|
||||
EnqueueTask(async () =>
|
||||
{
|
||||
await AddAlbumAsync(id, true);
|
||||
});
|
||||
await ctx.SendTextAsync("<span>Added</span>");
|
||||
}
|
||||
|
||||
}
|
||||
private class ApiAddAlbumRequest
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
}
|
||||
private static async Task ApiAddAlbum(ServerContext ctx)
|
||||
{
|
||||
var req = await ctx.ReadJsonAsync<ApiAddAlbumRequest>();
|
||||
if (req is not null)
|
||||
{
|
||||
EnqueueTask(async () =>
|
||||
{
|
||||
await AddAlbumAsync(req.Id, true);
|
||||
});
|
||||
ctx.StatusCode = 204;
|
||||
await ctx.WriteHeadersAsync();
|
||||
}
|
||||
}
|
||||
public static async Task AddArtistAsync(AlbumSongQuery.AlbumSongQueryMedia.AlbumSongQueryMediaTrack.AlbumSongQueryMediaTrackRecording.AlbumSongQueryMediaTrackRecordingArtists artist)
|
||||
{
|
||||
await RateLimitAsync(async () => await EnsureArtistThumbnail(artist.Artist.Id));
|
||||
@@ -870,7 +1065,9 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
}
|
||||
|
||||
} else if(!string.IsNullOrWhiteSpace(songId)) {
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(songId))
|
||||
{
|
||||
AddVideo(value, TYTDResolution.AudioOnly);
|
||||
}
|
||||
|
||||
@@ -914,7 +1111,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
|
||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
|
||||
using (var res = await MyHttpClient.GetAsync(url))
|
||||
{
|
||||
@@ -925,7 +1123,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||
}
|
||||
}
|
||||
@@ -966,7 +1165,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
using (var dest = File.Create(path))
|
||||
await res.Content.CopyToAsync(dest);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||
}
|
||||
}
|
||||
@@ -995,7 +1195,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
using (var dest = File.Create(path))
|
||||
await res.Content.CopyToAsync(dest);
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||
}
|
||||
}
|
||||
@@ -1185,7 +1386,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
var file = await db.GetMediaEntryAsync(albumId.ToString(), songId.ToString());
|
||||
if (song is not null && album is not null && trk is not null && file is not null)
|
||||
{
|
||||
video ??= new SongVideo() {
|
||||
video ??= new SongVideo()
|
||||
{
|
||||
SongId = songId.ToString(),
|
||||
VideoTitle = "No Video",
|
||||
|
||||
@@ -1256,7 +1458,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
||||
await foreach (var album in db.GetMediaEntriesAsync(item.SongId))
|
||||
{
|
||||
if (html is null) html = File.Exists(album.ToString()) ? $"<a class=\"transparent\" href=\"/api/v1/song?albumId={HttpUtility.UrlEncode(album.AlbumId)}&songId={HttpUtility.UrlEncode(item.SongId)}\"><i>download</i></a>" : ((await db.GetSongVideoFromSongIdAsync(item.SongId)) is not null ? "<i>downloading</i>" : "<i>file_download_off</i>");
|
||||
else {
|
||||
else
|
||||
{
|
||||
html = "<i>list</i>";
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user