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)
|
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,
|
Offset = offset,
|
||||||
Count = count
|
Count = count
|
||||||
}));
|
}));
|
||||||
@@ -86,11 +87,11 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
yield return ent;
|
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 session = new Session();
|
||||||
session.AccountName = username;
|
session.AccountName = username;
|
||||||
session.Expires = Program.NowEpoch + Expires;
|
session.Expires = expires ? Program.NowEpoch + Expires : 0;
|
||||||
session.Key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
session.Key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));
|
||||||
|
|
||||||
await conn.ExecuteAsync("INSERT INTO sessions (AccountName,Key,Expires) VALUES (@AccountName,@Key,@Expires);", session);
|
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)
|
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 }));
|
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)
|
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 }));
|
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)
|
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 }));
|
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)
|
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 }));
|
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)
|
public async Task<SongVideo?> GetSongVideoFromVideoIdAsync(VideoId id)
|
||||||
{
|
{
|
||||||
@@ -150,27 +159,35 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
public async Task AddArtistAsync(Artist artist)
|
public async Task AddArtistAsync(Artist artist)
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
await conn.ExecuteAsync("INSERT INTO artists (ArtistId,ArtistName,ArtistDisambiguation) VALUES (@ArtistId,@ArtistName,@ArtistDisambiguation);", artist);
|
await conn.ExecuteAsync("INSERT INTO artists (ArtistId,ArtistName,ArtistDisambiguation) VALUES (@ArtistId,@ArtistName,@ArtistDisambiguation);", artist);
|
||||||
}catch(Exception) {}
|
}
|
||||||
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
public async Task AddAlbumAsync(Album album)
|
public async Task AddAlbumAsync(Album album)
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
await conn.ExecuteAsync("INSERT INTO albums (AlbumId,AlbumName,AlbumReleaseDate) VALUES (@AlbumId,@AlbumName,@AlbumReleaseDate);", album);
|
await conn.ExecuteAsync("INSERT INTO albums (AlbumId,AlbumName,AlbumReleaseDate) VALUES (@AlbumId,@AlbumName,@AlbumReleaseDate);", album);
|
||||||
}catch(Exception) {}
|
}
|
||||||
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
public async Task AddSongAsync(Song song)
|
public async Task AddSongAsync(Song song)
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
await conn.ExecuteAsync("INSERT INTO songs (SongId,SongName) VALUES (@SongId,@SongName);", song);
|
await conn.ExecuteAsync("INSERT INTO songs (SongId,SongName) VALUES (@SongId,@SongName);", song);
|
||||||
}catch(Exception) {}
|
}
|
||||||
|
catch (Exception) { }
|
||||||
}
|
}
|
||||||
public async Task AddTrackAsync(AlbumTrack track)
|
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);
|
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)
|
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)
|
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}%",
|
Query = $"%{q}%",
|
||||||
Offset = offset,
|
Offset = offset,
|
||||||
Count = count
|
Count = count
|
||||||
@@ -191,7 +209,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
public async Task<IEnumerable<Song>> GetSongsAsync(string q = "", long offset = 0, long count = 20)
|
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}%",
|
Query = $"%{q}%",
|
||||||
Offset = offset,
|
Offset = offset,
|
||||||
Count = count
|
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)
|
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}%",
|
Query = $"%{q}%",
|
||||||
Offset = offset,
|
Offset = offset,
|
||||||
Count = count,
|
Count = count,
|
||||||
@@ -208,7 +228,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
public async Task<IEnumerable<Album>> GetAlbumsAsync(string q = "", long offset = 0, long count = 20)
|
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}%",
|
Query = $"%{q}%",
|
||||||
Offset = offset,
|
Offset = offset,
|
||||||
Count = count
|
Count = count
|
||||||
@@ -234,9 +255,11 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
public async Task<AlbumTrack?> GetTrackAsync(string songId, string albumId)
|
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,
|
SongId = songId,
|
||||||
AlbumId = albumId}))).FirstOrDefault();
|
AlbumId = albumId
|
||||||
|
}))).FirstOrDefault();
|
||||||
}
|
}
|
||||||
public async Task<IEnumerable<AlbumSongTrack>> GetTracksAsync(string albumId)
|
public async Task<IEnumerable<AlbumSongTrack>> GetTracksAsync(string albumId)
|
||||||
{
|
{
|
||||||
@@ -281,7 +304,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
public async Task AddEventAsync(long epoch, Webhook webhook)
|
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);");
|
//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,
|
Time = epoch,
|
||||||
Type = webhook.Type,
|
Type = webhook.Type,
|
||||||
VideoTitle = webhook.Video.Title,
|
VideoTitle = webhook.Video.Title,
|
||||||
|
|||||||
+236
-33
@@ -82,7 +82,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
public static void AddVideo(VideoId id, TYTDResolution res = TYTDResolution.LowVideo)
|
public static void AddVideo(VideoId id, TYTDResolution res = TYTDResolution.LowVideo)
|
||||||
{
|
{
|
||||||
|
|
||||||
EnqueueTask(async()=>{
|
EnqueueTask(async () =>
|
||||||
|
{
|
||||||
var cfg = Config ?? new AuthConfig();
|
var cfg = Config ?? new AuthConfig();
|
||||||
HttpRequestMessage msg = new HttpRequestMessage(HttpMethod.Post, $"{cfg.TYTDUrl.TrimEnd('/')}/api/v1/add");
|
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)
|
public static async Task AuthRedirectAsync(ServerContext ctx)
|
||||||
{
|
{
|
||||||
|
if (ctx.UrlPath.StartsWith("/api/v1/"))
|
||||||
|
{
|
||||||
|
ctx.StatusCode = 401;
|
||||||
|
await ctx.WriteHeadersAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
string tmpToken = RandomNumberGenerator.GetHexString(24);
|
string tmpToken = RandomNumberGenerator.GetHexString(24);
|
||||||
@@ -245,7 +253,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
public static HttpActionAsync EnsureLoggedin(HttpActionAsync action)
|
public static HttpActionAsync EnsureLoggedin(HttpActionAsync action)
|
||||||
{
|
{
|
||||||
return async(ctx)=>{
|
return async (ctx) =>
|
||||||
|
{
|
||||||
AuthConfig? cfg;
|
AuthConfig? cfg;
|
||||||
lock (MyLockObject)
|
lock (MyLockObject)
|
||||||
{
|
{
|
||||||
@@ -284,6 +293,27 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
await AuthRedirectAsync(ctx);
|
await AuthRedirectAsync(ctx);
|
||||||
return;
|
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
|
else
|
||||||
{
|
{
|
||||||
await AuthRedirectAsync(ctx);
|
await AuthRedirectAsync(ctx);
|
||||||
@@ -306,6 +336,16 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
yield return new PageLink("Search", "search", "/search");
|
yield return new PageLink("Search", "search", "/search");
|
||||||
yield return new PageLink("Music", "music_note", "/music");
|
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)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory("data/Config");
|
Directory.CreateDirectory("data/Config");
|
||||||
@@ -323,7 +363,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
|
|
||||||
RouteServer routeServer = new RouteServer(new StaticServer("wwwroot"));
|
RouteServer routeServer = new RouteServer(new StaticServer("wwwroot"));
|
||||||
routeServer.Add("/api/v1/webhook",async(ctx)=>{
|
routeServer.Add("/api/v1/webhook", async (ctx) =>
|
||||||
|
{
|
||||||
string auth = "";
|
string auth = "";
|
||||||
ctx.RequestHeaders.TryGetFirst("Authorization", out 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]))
|
if (string.IsNullOrWhiteSpace(cfg.Session) || (authHdr.Length == 2 && authHdr[0] == "TYTD" && cfg.SSOAppKey == authHdr[1]))
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
|
{
|
||||||
var webhook = await ctx.ReadJsonAsync<Webhook>();
|
var webhook = await ctx.ReadJsonAsync<Webhook>();
|
||||||
if (webhook is not null)
|
if (webhook is not null)
|
||||||
{
|
{
|
||||||
@@ -346,7 +388,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
{
|
{
|
||||||
|
|
||||||
var vid = VideoId.TryParse(webhook.Video.VideoId);
|
var vid = VideoId.TryParse(webhook.Video.VideoId);
|
||||||
EnqueueTask(async()=>{
|
EnqueueTask(async () =>
|
||||||
|
{
|
||||||
if (vid.HasValue)
|
if (vid.HasValue)
|
||||||
await ProcessSongAsync(vid.Value);
|
await ProcessSongAsync(vid.Value);
|
||||||
});
|
});
|
||||||
@@ -354,7 +397,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
break;
|
break;
|
||||||
case "Started":
|
case "Started":
|
||||||
{
|
{
|
||||||
EnqueueTask(async()=>{
|
EnqueueTask(async () =>
|
||||||
|
{
|
||||||
using (var db = new Database())
|
using (var db = new Database())
|
||||||
{
|
{
|
||||||
foreach (var item in await db.GetSongVideosAsync())
|
foreach (var item in await db.GetSongVideosAsync())
|
||||||
@@ -371,7 +415,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch(Exception ex)
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ex);
|
Console.WriteLine(ex);
|
||||||
}
|
}
|
||||||
@@ -383,9 +428,11 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
routeServer.Add("/settings", EnsureLoggedin(SettingsAsync));
|
routeServer.Add("/settings", EnsureLoggedin(SettingsAsync));
|
||||||
routeServer.Add("/auth", AuthAsync, "POST");
|
routeServer.Add("/auth", AuthAsync, "POST");
|
||||||
routeServer.Add("/login", LoginAsync);
|
routeServer.Add("/login", LoginAsync);
|
||||||
routeServer.Add("/rescan",EnsureLoggedin(async(ctx)=>{
|
routeServer.Add("/rescan", EnsureLoggedin(async (ctx) =>
|
||||||
|
{
|
||||||
ctx.ParseBody();
|
ctx.ParseBody();
|
||||||
EnqueueTask(async()=>{
|
EnqueueTask(async () =>
|
||||||
|
{
|
||||||
using (var db = new Database())
|
using (var db = new Database())
|
||||||
{
|
{
|
||||||
foreach (var item in await db.GetSongVideosAsync())
|
foreach (var item in await db.GetSongVideosAsync())
|
||||||
@@ -398,19 +445,59 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
});
|
});
|
||||||
await ctx.WithStatusCode(System.Net.HttpStatusCode.ExpectationFailed).WriteHeadersAsync();
|
await ctx.WithStatusCode(System.Net.HttpStatusCode.ExpectationFailed).WriteHeadersAsync();
|
||||||
}), "POST");
|
}), "POST");
|
||||||
routeServer.Add("/",EnsureLoggedin(async(ctx)=>{
|
routeServer.Add("/", EnsureLoggedin(async (ctx) =>
|
||||||
|
{
|
||||||
var cfg = Config ?? new AuthConfig();
|
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()));
|
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"), []));
|
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();
|
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))
|
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,
|
username = user,
|
||||||
password = pass
|
password = pass
|
||||||
}));
|
}));
|
||||||
@@ -423,7 +510,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
{
|
{
|
||||||
var service_name_unique = RandomNumberGenerator.GetHexString(24);
|
var service_name_unique = RandomNumberGenerator.GetHexString(24);
|
||||||
var sso_app_key = RandomNumberGenerator.GetHexString(64);
|
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}",
|
service_name = $"tytdcompanion-{service_name_unique}",
|
||||||
sso_app_key,
|
sso_app_key,
|
||||||
service_pretty_name = "TYTD Companion",
|
service_pretty_name = "TYTD Companion",
|
||||||
@@ -454,7 +542,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
await InstallExtension();
|
await InstallExtension();
|
||||||
await RedirectAsync(ctx, "/");
|
await RedirectAsync(ctx, "/");
|
||||||
return;
|
return;
|
||||||
} else if(sso_resp2.Type != "db")
|
}
|
||||||
|
else if (sso_resp2.Type != "db")
|
||||||
{
|
{
|
||||||
throw new Exception($"Error of {sso_resp2.Type}, {sso_resp2.Reason}");
|
throw new Exception($"Error of {sso_resp2.Type}, {sso_resp2.Reason}");
|
||||||
}
|
}
|
||||||
@@ -468,7 +557,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}), "POST");
|
}), "POST");
|
||||||
routeServer.Add("/search", EnsureLoggedin(Search));
|
routeServer.Add("/search", EnsureLoggedin(Search));
|
||||||
routeServer.Add("/search_dialog", EnsureLoggedin(SearchDialog));
|
routeServer.Add("/search_dialog", EnsureLoggedin(SearchDialog));
|
||||||
routeServer.Add("/search_button",EnsureLoggedin(async(ctx)=>{
|
routeServer.Add("/search_button", EnsureLoggedin(async (ctx) =>
|
||||||
|
{
|
||||||
|
|
||||||
ctx.ParseBody();
|
ctx.ParseBody();
|
||||||
if (ctx.QueryParams.TryGetFirst("type", out var type))
|
if (ctx.QueryParams.TryGetFirst("type", out var type))
|
||||||
@@ -502,6 +592,7 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
routeServer.Add("/music/mysongs", EnsureLoggedin(MySongs));
|
routeServer.Add("/music/mysongs", EnsureLoggedin(MySongs));
|
||||||
routeServer.Add("/music/mysong", EnsureLoggedin(MySong));
|
routeServer.Add("/music/mysong", EnsureLoggedin(MySong));
|
||||||
routeServer.Add("/music/album/add", EnsureLoggedin(AddAlbum), "POST");
|
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/video-thumbnail", EnsureLoggedin(VideoThumbnail));
|
||||||
routeServer.Add("/api/v1/artist-thumbnail", EnsureLoggedin(ArtistThumbnail));
|
routeServer.Add("/api/v1/artist-thumbnail", EnsureLoggedin(ArtistThumbnail));
|
||||||
routeServer.Add("/api/v1/album-thumbnail", EnsureLoggedin(AlbumThumbnail));
|
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/artist", EnsureLoggedin(ApiArtist));
|
||||||
routeServer.Add("/api/v1/albums", EnsureLoggedin(ApiAlbums));
|
routeServer.Add("/api/v1/albums", EnsureLoggedin(ApiAlbums));
|
||||||
routeServer.Add("/api/v1/album", EnsureLoggedin(ApiAlbum));
|
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/song", EnsureLoggedin(ApiSong));
|
||||||
routeServer.Add("/api/v1/album.zip", EnsureLoggedin(ApiAlbumZip));
|
routeServer.Add("/api/v1/album.zip", EnsureLoggedin(ApiAlbumZip));
|
||||||
bool finished = false;
|
bool finished = false;
|
||||||
|
|
||||||
RegisterEventable(SearchResultHandler);
|
RegisterEventable(SearchResultHandler);
|
||||||
RegisterEventable(tasks);
|
RegisterEventable(tasks);
|
||||||
Task t = Task.Factory.StartNew(async()=>{
|
Task t = Task.Factory.StartNew(async () =>
|
||||||
|
{
|
||||||
while (!finished)
|
while (!finished)
|
||||||
{
|
{
|
||||||
foreach (var item in eventables)
|
foreach (var item in eventables)
|
||||||
await item.EventAsync();
|
await item.EventAsync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Task.Run(async()=>{
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
await Task.Delay(10000);
|
await Task.Delay(10000);
|
||||||
Console.WriteLine("Wait expired");
|
Console.WriteLine("Wait expired");
|
||||||
await InstallExtension();
|
await InstallExtension();
|
||||||
@@ -585,10 +679,51 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
count = 20;
|
count = 20;
|
||||||
|
|
||||||
using (var db = new Database())
|
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)
|
private static async Task ApiAlbums(ServerContext ctx)
|
||||||
{
|
{
|
||||||
if (!ctx.QueryParams.TryGetFirst("q", out var q))
|
if (!ctx.QueryParams.TryGetFirst("q", out var q))
|
||||||
@@ -600,38 +735,62 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
count = 20;
|
count = 20;
|
||||||
|
|
||||||
using (var db = new Database())
|
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)
|
private static async Task ApiAlbum(ServerContext ctx)
|
||||||
{
|
{
|
||||||
if (ctx.QueryParams.TryGetFirstGuid("albumId", out var albumId))
|
if (ctx.QueryParams.TryGetFirstGuid("albumId", out var albumId))
|
||||||
using (var db = new Database())
|
using (var db = new Database())
|
||||||
{
|
{
|
||||||
|
bool hasAny = false;
|
||||||
List<object> tracks = new List<object>();
|
List<object> tracks = new List<object>();
|
||||||
foreach (var item in await db.GetTracksAsync(albumId.ToString()))
|
foreach (var item in await db.GetTracksAsync(albumId.ToString()))
|
||||||
{
|
{
|
||||||
var artists = await db.GetContributingArtistsAsync(item.SongId);
|
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.SongId,
|
||||||
item.SongName,
|
item.SongName,
|
||||||
item.Track,
|
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 album = await db.GetAlbumAsync(albumId.ToString());
|
||||||
var albumArtists = await db.GetAlbumArtistsAsync(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,
|
Album = album,
|
||||||
AlbumArtists = albumArtists,
|
AlbumArtists = albumArtists,
|
||||||
Tracks = tracks
|
Tracks = tracks,
|
||||||
|
HasAny = hasAny
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static async Task ApiSong(ServerContext ctx)
|
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");
|
var inline = ctx.QueryParams.GetFirstBoolean("inline");
|
||||||
MediaEntry? mId = null;
|
MediaEntry? mId = null;
|
||||||
@@ -653,7 +812,25 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
return;
|
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();
|
await ctx.SendNotFoundAsync();
|
||||||
}
|
}
|
||||||
@@ -667,13 +844,31 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
ctx.ParseBody();
|
ctx.ParseBody();
|
||||||
if (ctx.QueryParams.TryGetFirstGuid("id", out var id))
|
if (ctx.QueryParams.TryGetFirstGuid("id", out var id))
|
||||||
{
|
{
|
||||||
EnqueueTask(async()=>{
|
EnqueueTask(async () =>
|
||||||
|
{
|
||||||
await AddAlbumAsync(id, true);
|
await AddAlbumAsync(id, true);
|
||||||
});
|
});
|
||||||
await ctx.SendTextAsync("<span>Added</span>");
|
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)
|
public static async Task AddArtistAsync(AlbumSongQuery.AlbumSongQueryMedia.AlbumSongQueryMediaTrack.AlbumSongQueryMediaTrackRecording.AlbumSongQueryMediaTrackRecordingArtists artist)
|
||||||
{
|
{
|
||||||
await RateLimitAsync(async () => await EnsureArtistThumbnail(artist.Artist.Id));
|
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);
|
AddVideo(value, TYTDResolution.AudioOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -914,7 +1111,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
|
|
||||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
|
|
||||||
using (var res = await MyHttpClient.GetAsync(url))
|
using (var res = await MyHttpClient.GetAsync(url))
|
||||||
{
|
{
|
||||||
@@ -925,7 +1123,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -966,7 +1165,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
using (var dest = File.Create(path))
|
using (var dest = File.Create(path))
|
||||||
await res.Content.CopyToAsync(dest);
|
await res.Content.CopyToAsync(dest);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -995,7 +1195,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
using (var dest = File.Create(path))
|
using (var dest = File.Create(path))
|
||||||
await res.Content.CopyToAsync(dest);
|
await res.Content.CopyToAsync(dest);
|
||||||
}
|
}
|
||||||
else {
|
else
|
||||||
|
{
|
||||||
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
File.Copy("wwwroot/tytd-artist.jpg", path, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1185,7 +1386,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
var file = await db.GetMediaEntryAsync(albumId.ToString(), songId.ToString());
|
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)
|
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(),
|
SongId = songId.ToString(),
|
||||||
VideoTitle = "No Video",
|
VideoTitle = "No Video",
|
||||||
|
|
||||||
@@ -1256,7 +1458,8 @@ namespace Tesses.YouTubeDownloader2025Companion
|
|||||||
await foreach (var album in db.GetMediaEntriesAsync(item.SongId))
|
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>");
|
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>";
|
html = "<i>list</i>";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user