Song server: search and download songs, mobile web UI
- Sources: "free" (Creative Commons songs with media from github.com/UltraStar-Deluxe/songs) and "usdb" (usdb.animux.de with an account; audio/video via yt-dlp from the song's #VIDEO hints or YouTube links in the comments, covers from fanart.tv/YouTube) - Aggregated search (all sources in parallel, in-library flag), download queue into <library>/Downloads/<source>/<Artist - Title>, then rescan - Web UI in wwwroot (plain JS, no build): search, downloads with progress, library; phone first, dark mode, installable - Docker image with yt-dlp, ffmpeg and deno; library mounted writable - Tests for matching, USDB parsing and song rewriting Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
efcc63a640
commit
1e65864c8f
19 files changed
+1862
-12
No files matched your search
@@ -5,6 +5,7 @@ using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Options;
|
||||
using VocaluxeSongServer.Contracts;
|
||||
using VocaluxeSongServer.Library;
|
||||
using VocaluxeSongServer.Sources;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -13,6 +14,21 @@ builder.Services.Configure<CLibraryOptions>(builder.Configuration.GetSection("Li
|
||||
builder.Services.AddSingleton<CLibraryScanner>();
|
||||
builder.Services.AddSingleton<CLibraryIndex>();
|
||||
builder.Services.AddHostedService(sp => sp.GetRequiredService<CLibraryIndex>());
|
||||
// Song sources for search and download (see Sources/). USDB needs Usdb__User and Usdb__Password.
|
||||
builder.Services.AddSingleton<CYtDlp>();
|
||||
builder.Services.AddHttpClient("sources", c => c.Timeout = TimeSpan.FromMinutes(10));
|
||||
builder.Services.AddSingleton<ISongSource>(sp => new CGitHubSongSource(sp.GetRequiredService<IHttpClientFactory>().CreateClient("sources")));
|
||||
builder.Services.AddSingleton<ISongSource>(sp => new CUsdbSongSource(
|
||||
new HttpClient(new HttpClientHandler { CookieContainer = new System.Net.CookieContainer(), AutomaticDecompression = System.Net.DecompressionMethods.All })
|
||||
{
|
||||
Timeout = TimeSpan.FromMinutes(2),
|
||||
DefaultRequestHeaders = { { "User-Agent", "VocaluxeSongServer" } }
|
||||
},
|
||||
builder.Configuration.GetSection("Usdb").Get<CUsdbOptions>() ?? new CUsdbOptions(),
|
||||
sp.GetRequiredService<CYtDlp>(), sp.GetRequiredService<ILogger<CUsdbSongSource>>()));
|
||||
builder.Services.AddSingleton<CSongSearch>();
|
||||
builder.Services.AddSingleton<CDownloadQueue>();
|
||||
builder.Services.AddHostedService(sp => sp.GetRequiredService<CDownloadQueue>());
|
||||
builder.Services.AddResponseCompression(o =>
|
||||
{
|
||||
o.EnableForHttps = true;
|
||||
@@ -23,6 +39,9 @@ builder.Services.ConfigureHttpJsonOptions(o => o.SerializerOptions.PropertyNamin
|
||||
|
||||
var app = builder.Build();
|
||||
app.UseResponseCompression();
|
||||
// The web UI (wwwroot): search and download songs, browse the library. Mobile first.
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles();
|
||||
|
||||
app.MapGet("/health", (CLibraryIndex index) => index.Current == null
|
||||
? Results.Json(new { status = "scanning" }, statusCode: 503)
|
||||
@@ -121,6 +140,28 @@ api.MapPost("/files/bundle", async (BundleRequest request, CLibraryIndex index,
|
||||
return Results.File(buffer, "application/zip", "bundle.zip");
|
||||
});
|
||||
|
||||
// Song search and downloads
|
||||
api.MapGet("/sources", (CSongSearch search) => search.Sources.Select(s => new SourceDto
|
||||
{
|
||||
Id = s.Id, Name = s.Name, Description = s.Description, Enabled = s.Enabled, Hint = s.Hint
|
||||
}));
|
||||
|
||||
api.MapGet("/search", (string q, CSongSearch search, CancellationToken ct) => search.SearchAsync(q, ct));
|
||||
|
||||
api.MapGet("/downloads", (CDownloadQueue queue) => queue.Jobs);
|
||||
|
||||
api.MapPost("/downloads", (DownloadRequest request, CDownloadQueue queue) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return Results.Json(queue.Enqueue(request));
|
||||
}
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
return Results.Problem(title: e.Message, statusCode: StatusCodes.Status400BadRequest);
|
||||
}
|
||||
});
|
||||
|
||||
app.Run();
|
||||
|
||||
public partial class Program { }
|
||||
Reference in new issue
Block a user