feat: add native fnOS package

This commit is contained in:
2026-08-02 14:43:37 +08:00
parent e62dfd2de1
commit e8772a39b5
24 changed files with 842 additions and 4 deletions
@@ -130,7 +130,7 @@ public sealed class SystemSettingsService : ISystemSettingsService
return new SystemSettingsDto
{
FfmpegPath = GetValue(lookup, FfmpegPathKey, "ffmpeg"),
OutputRoot = GetValue(lookup, OutputRootKey, "records"),
OutputRoot = GetValue(lookup, OutputRootKey, GetDefaultOutputRoot()),
OutputDirectoryTemplate = GetValue(lookup, OutputDirectoryTemplateKey, "{platform}/{yyyy}/{MM}/{dd}/{anchor}"),
OutputFileNameTemplate = GetValue(lookup, OutputFileNameTemplateKey, "{HHmmss}_{quality}_{anchor}_{title}_{roomId}{segmentSuffix}"),
DefaultQuality = GetValue(lookup, DefaultQualityKey, "origin"),
@@ -292,6 +292,12 @@ public sealed class SystemSettingsService : ISystemSettingsService
};
}
private static string GetDefaultOutputRoot()
{
var configured = Environment.GetEnvironmentVariable("LIVE_RECORDER_DEFAULT_OUTPUT_ROOT");
return string.IsNullOrWhiteSpace(configured) ? "records" : configured.Trim();
}
public async Task<SystemSettingsDto> UpdateAsync(UpdateSystemSettingsRequest request, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
@@ -6,6 +6,8 @@ namespace LiveRecorder.Infrastructure.Persistence;
public sealed class DatabaseInitializer
{
private const string DefaultAdminPasswordEnvironmentVariable = "LIVE_RECORDER_DEFAULT_ADMIN_PASSWORD";
private readonly LiveRecorderDbContext _dbContext;
public DatabaseInitializer(LiveRecorderDbContext dbContext)
@@ -28,14 +30,16 @@ public sealed class DatabaseInitializer
if (!await _dbContext.UserAccounts.AnyAsync(cancellationToken))
{
var now = DateTimeOffset.UtcNow;
var admin = new UserAccount("admin", "Administrator", PasswordHasher.Hash("Admin@123"), now);
var passwordHash = PasswordHasher.Hash(GetDefaultAdminPassword());
Environment.SetEnvironmentVariable(DefaultAdminPasswordEnvironmentVariable, null);
var admin = new UserAccount("admin", "Administrator", passwordHash, now);
await _dbContext.UserAccounts.AddAsync(admin, cancellationToken);
}
var defaults = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
["ffmpeg.path"] = "ffmpeg",
["recording.output_root"] = "records",
["recording.output_root"] = GetDefaultOutputRoot(),
["recording.output_directory_template"] = "{platform}/{yyyy}/{MM}/{dd}/{anchor}",
["recording.output_file_name_template"] = "{HHmmss}_{anchor}_{title}_{roomId}{segmentSuffix}",
["recording.default_quality"] = "origin",
@@ -167,4 +171,16 @@ public sealed class DatabaseInitializer
await _dbContext.SaveChangesAsync(cancellationToken);
}
private static string GetDefaultOutputRoot()
{
var configured = Environment.GetEnvironmentVariable("LIVE_RECORDER_DEFAULT_OUTPUT_ROOT");
return string.IsNullOrWhiteSpace(configured) ? "records" : configured.Trim();
}
private static string GetDefaultAdminPassword()
{
var configured = Environment.GetEnvironmentVariable(DefaultAdminPasswordEnvironmentVariable);
return string.IsNullOrEmpty(configured) ? "Admin@123" : configured;
}
}
+18 -1
View File
@@ -247,14 +247,26 @@ builder.Services.AddHostedService<RetentionCleanupBackgroundService>();
builder.Services.AddHostedService<OpenListUploadBackgroundService>();
var app = builder.Build();
var webRootPath = app.Environment.WebRootPath ?? Path.Combine(app.Environment.ContentRootPath, "wwwroot");
var hasBundledFrontend = File.Exists(Path.Combine(webRootPath, "index.html"));
app.UseMiddleware<ExceptionHandlingMiddleware>();
app.UseSwagger();
app.UseSwaggerUI();
app.UseCors("frontend");
if (hasBundledFrontend)
{
app.UseDefaultFiles();
app.UseStaticFiles();
}
app.UseMiddleware<ApiTokenAuthenticationMiddleware>();
app.MapGet("/", () => Results.Redirect("/swagger"));
if (!hasBundledFrontend)
{
app.MapGet("/", () => Results.Redirect("/swagger"));
}
app.MapControllers();
// ── Health check endpoints ────────────────────────────────────────────
@@ -359,6 +371,11 @@ if (resetRecordingData)
return;
}
if (hasBundledFrontend)
{
app.MapFallbackToFile("index.html");
}
app.Run();
static async Task<Dictionary<string, long>> ReadRecordingDataCountsAsync(LiveRecorderDbContext dbContext)