-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
99 lines (83 loc) · 4.4 KB
/
Copy pathProgram.cs
File metadata and controls
99 lines (83 loc) · 4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
using BoothFitnessVideoManager.Options;
using BoothFitnessVideoManager.Services;
using BoothFitnessVideoManager.Workers;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
var builder = WebApplication.CreateBuilder(args);
// Load user-secrets in every environment (they're only auto-loaded in
// Development) so the connection string can stay out of the repo while testing
// against prod locally. On the kiosk there are no user-secrets, so this is a
// no-op there and the secret comes from appsettings.Production.json / env var.
builder.Configuration.AddUserSecrets<Program>(optional: true);
// Run as a Windows Service when installed as one; no-op as a console app (dev).
builder.Host.UseWindowsService();
// Strongly-typed configuration.
builder.Services.Configure<BlobStorageOptions>(
builder.Configuration.GetSection(BlobStorageOptions.SectionName));
builder.Services.Configure<LocalVideoOptions>(
builder.Configuration.GetSection(LocalVideoOptions.SectionName));
builder.Services.Configure<ServerOptions>(
builder.Configuration.GetSection(ServerOptions.SectionName));
builder.Services.Configure<SyncOptions>(
builder.Configuration.GetSection(SyncOptions.SectionName));
var serverOptions = builder.Configuration.GetSection(ServerOptions.SectionName).Get<ServerOptions>()
?? new ServerOptions();
var localOptions = builder.Configuration.GetSection(LocalVideoOptions.SectionName).Get<LocalVideoOptions>()
?? new LocalVideoOptions();
// Loopback-only web server.
builder.WebHost.UseUrls(serverOptions.Url);
// App services. The blob client is built lazily inside the downloader so a
// missing/invalid connection string never crashes the host at startup.
builder.Services.AddSingleton<BlobVideoDownloader>();
builder.Services.AddSingleton<IDownloadActivityLog, LoggerDownloadActivityLog>();
builder.Services.AddHostedService<VideoSyncWorker>();
// Allow the gym app (different local origin/port) to fetch manifests/segments.
const string corsPolicy = "LocalGymApp";
builder.Services.AddCors(options => options.AddPolicy(corsPolicy, policy =>
policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()));
var app = builder.Build();
// Print the effective, resolved configuration at startup so it's unambiguous
// which settings won (base appsettings.json vs. environment/user-secrets overrides).
app.Logger.LogInformation(
"Effective config — environment={Env} | videosPath={Path} | container={Container} | " +
"prefix={Prefix} | maxVideosPerRun={Max} | connectionStringSet={HasConnString}",
app.Environment.EnvironmentName,
localOptions.Path,
app.Configuration["BlobStorage:ContainerName"],
app.Configuration["BlobStorage:ProductionPrefix"],
app.Configuration["Sync:MaxVideosPerRun"],
!string.IsNullOrWhiteSpace(app.Configuration["BlobStorage:ConnectionString"]));
app.UseCors(corsPolicy);
// Only serve files if the configured path is a valid absolute path for this OS.
// Otherwise (e.g. the default Windows path while accidentally running in the
// Production environment on macOS) log a clear reason and skip — never crash the
// host or create a junk folder in the working directory.
if (!Path.IsPathFullyQualified(localOptions.Path))
{
app.Logger.LogError(
"LocalVideos:Path '{Path}' is not a valid absolute path on this OS, so video " +
"serving is disabled. If developing locally, run with ASPNETCORE_ENVIRONMENT=Development.",
localOptions.Path);
}
else
{
Directory.CreateDirectory(localOptions.Path);
// Content types for HLS. .master is a custom manifest extension; map it (and
// the standard HLS/segment types) so players get the right Content-Type.
var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.Mappings[".master"] = "application/vnd.apple.mpegurl";
contentTypeProvider.Mappings[".m3u8"] = "application/vnd.apple.mpegurl";
contentTypeProvider.Mappings[".ts"] = "video/mp2t";
contentTypeProvider.Mappings[".m4s"] = "video/mp4";
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(localOptions.Path),
RequestPath = serverOptions.RequestPath,
ContentTypeProvider = contentTypeProvider,
ServeUnknownFileTypes = true,
DefaultContentType = "application/octet-stream",
});
}
// Simple liveness endpoint for the kiosk / monitoring.
app.MapGet("/health", () => Results.Ok(new { status = "ok" }));
app.Run();