37cf19c405
Relocate both the framework multipart buffer (via ASPNETCORE_TEMP) and the controller staging file to a configurable data-disk directory, so large WAV/FLAC/MP3 uploads no longer fail on the host's small tmpfs.
111 lines
5.6 KiB
C#
111 lines
5.6 KiB
C#
using DeepDrftAPI.Models;
|
|
using DeepDrftContent;
|
|
using DeepDrftContent.Constants;
|
|
using DeepDrftContent.FileDatabase.Models;
|
|
using DeepDrftContent.FileDatabase.Services;
|
|
using DeepDrftContent.Processors;
|
|
using Microsoft.Extensions.Logging;
|
|
using NetBlocks.Utilities.Environment;
|
|
|
|
namespace DeepDrftAPI
|
|
{
|
|
public static class Startup
|
|
{
|
|
public static Task ConfigureDomainServices(WebApplicationBuilder builder)
|
|
{
|
|
// Audio services
|
|
builder.Services.AddSingleton<AudioProcessor>();
|
|
builder.Services.AddSingleton<Mp3AudioProcessor>();
|
|
builder.Services.AddSingleton<FlacAudioProcessor>();
|
|
builder.Services.AddSingleton<AudioProcessorRouter>();
|
|
builder.Services.AddSingleton<TrackContentService>();
|
|
|
|
// Image services
|
|
builder.Services.AddSingleton<ImageProcessor>();
|
|
|
|
// Waveform loudness profiling (upload-time, off the playback path)
|
|
builder.Services.Configure<WaveformProfileOptions>(
|
|
builder.Configuration.GetSection(nameof(WaveformProfileOptions)));
|
|
builder.Services.AddSingleton<ILoudnessAlgorithm, RmsLoudnessAlgorithm>();
|
|
builder.Services.AddSingleton<WaveformProfileService>();
|
|
|
|
// File Database
|
|
var fileDatabasePath = CredentialTools.ResolvePathOrThrow("filedatabase", "environment/filedatabase.json");
|
|
builder.Configuration.AddJsonFile(fileDatabasePath, optional: false, reloadOnChange: false);
|
|
var fileDatabaseSettings = builder.Configuration.GetSection(nameof(FileDatabaseSettings)).Get<FileDatabaseSettings>();
|
|
if (fileDatabaseSettings is null) { throw new Exception("File database settings are not configured"); }
|
|
|
|
var vaultPath = fileDatabaseSettings.VaultPath;
|
|
builder.Services.AddSingleton(sp =>
|
|
{
|
|
var logger = sp.GetRequiredService<ILogger<FileDatabase>>();
|
|
var db = FileDatabase.FromAsync(vaultPath, logger).GetAwaiter().GetResult();
|
|
if (db is null) throw new Exception("Unable to initialize file database");
|
|
InitializeTrackVault(db).GetAwaiter().GetResult();
|
|
InitializeImageVault(db).GetAwaiter().GetResult();
|
|
InitializeTrackWaveformsVault(db).GetAwaiter().GetResult();
|
|
return db;
|
|
});
|
|
|
|
// Upload staging directory. Large audio bodies (multi-hundred-MB WAVs) must never stage on
|
|
// the system temp mount — on the Linux host /tmp is a small RAM-backed tmpfs. We move BOTH
|
|
// on-disk copies of an upload off /tmp onto the data disk:
|
|
// Layer 1 — the framework's multipart file-section buffer (FileBufferingReadStream), which
|
|
// reads its directory from the ASPNETCORE_TEMP env var (falling back to
|
|
// Path.GetTempPath()). Setting the var here, before the host runs, relocates it.
|
|
// Layer 2 — the controller's own staging file, via the injected UploadStagingDirectory.
|
|
// Default location is a "staging" subdirectory beside the vaults; override with
|
|
// Upload:StagingPath in appsettings.json.
|
|
var uploadSettings = builder.Configuration.GetSection("Upload").Get<UploadSettings>();
|
|
var stagingPath = ResolveStagingPath(uploadSettings?.StagingPath, vaultPath);
|
|
Directory.CreateDirectory(stagingPath);
|
|
|
|
// AspNetCoreTempDirectory caches this value on first read and throws if the directory is
|
|
// absent, so set it (and create the dir) before any request is served.
|
|
Environment.SetEnvironmentVariable("ASPNETCORE_TEMP", stagingPath);
|
|
builder.Services.AddSingleton(new UploadStagingDirectory(stagingPath));
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolves the absolute upload-staging directory. An explicit <paramref name="configuredPath"/>
|
|
/// (from <c>Upload:StagingPath</c>) wins; otherwise it defaults to a <c>staging</c> subdirectory
|
|
/// under <paramref name="vaultPath"/> — on the data disk, never the system temp mount. Pure so
|
|
/// the "never <c>/tmp</c>" invariant is unit-testable without standing up the host.
|
|
/// </summary>
|
|
public static string ResolveStagingPath(string? configuredPath, string vaultPath)
|
|
{
|
|
var path = string.IsNullOrWhiteSpace(configuredPath)
|
|
? Path.Combine(vaultPath, "staging")
|
|
: configuredPath;
|
|
return Path.GetFullPath(path);
|
|
}
|
|
|
|
private static async Task InitializeTrackVault(FileDatabase fileDatabase)
|
|
{
|
|
if (!fileDatabase.HasVault(VaultConstants.Tracks))
|
|
{
|
|
await fileDatabase.CreateVaultAsync(VaultConstants.Tracks, MediaVaultType.Audio);
|
|
}
|
|
}
|
|
|
|
private static async Task InitializeImageVault(FileDatabase fileDatabase)
|
|
{
|
|
if (!fileDatabase.HasVault(VaultConstants.Images))
|
|
{
|
|
await fileDatabase.CreateVaultAsync(VaultConstants.Images, MediaVaultType.Image);
|
|
}
|
|
}
|
|
|
|
// Ensure the track-waveforms vault exists. Holds the per-track high-resolution waveform datum
|
|
// (every track — Mix, Session, Cut), keyed by the track's EntryKey.
|
|
private static async Task InitializeTrackWaveformsVault(FileDatabase fileDatabase)
|
|
{
|
|
if (!fileDatabase.HasVault(VaultConstants.TrackWaveforms))
|
|
{
|
|
await fileDatabase.CreateVaultAsync(VaultConstants.TrackWaveforms, MediaVaultType.Media);
|
|
}
|
|
}
|
|
}
|
|
} |