TASK 5 items 1-3: reusable stream, health banner, docs refresh

- Reusable stream: GetOrCreateReusableStreamAsync, cached via LayoutStore, bound at broadcast insert
- Health monitoring: GetStreamHealthAsync polls liveStreams.status, report-by-exception banner
- Go-live order: ensure stream → create broadcast → then start pump (URL must exist first)
- Full broadcast form scoped (TASK 5 items 6-7) in TASKS.md + ai.md
- TASK 23 trimmed to technical scope, TASK 26 added (marcom/launch kit)
- Confidential files: MONETIZATION.md + MARCOM.md (gitignored, not committed)
- .gitignore updated for confidential business files
- HANDOFF.md updated with session state and next steps
This commit is contained in:
2026-08-16 17:48:37 -07:00
parent 92f1471ab4
commit 0fac1e6a5a
16 changed files with 810 additions and 140 deletions
+29
View File
@@ -615,6 +615,35 @@ public class LayoutStore : IDisposable
insert.ExecuteNonQuery();
}
/// <summary>The cached reusable stream (TASK 5), or null if never created.
/// Survives restarts so the pump has its RTMP URL the moment go-live starts
/// instead of waiting on the YouTube round-trip.</summary>
public ReusableStream? LoadReusableStream()
{
using var cmd = _connection.CreateCommand();
cmd.CommandText = "SELECT Value FROM Settings WHERE Key = 'ReusableStream';";
var value = cmd.ExecuteScalar() as string;
if (string.IsNullOrWhiteSpace(value)) return null;
var parts = value.Split('\u001F');
return parts.Length == 3
? new ReusableStream(parts[0], parts[1], parts[2])
: null;
}
public void SaveReusableStream(ReusableStream? stream)
{
using var cmd = _connection.CreateCommand();
cmd.CommandText = "DELETE FROM Settings WHERE Key = 'ReusableStream';";
cmd.ExecuteNonQuery();
if (stream == null) return;
using var insert = _connection.CreateCommand();
insert.CommandText = "INSERT INTO Settings (Key, Value) VALUES ('ReusableStream', $value);";
insert.Parameters.AddWithValue("$value", $"{stream.Id}\u001F{stream.IngestionAddress}\u001F{stream.StreamName}");
insert.ExecuteNonQuery();
}
public void Save(IEnumerable<Scene> scenes, Webcam? webcam, SocialsConfig? socials, Music? music = null)
{
using var tx = _connection.BeginTransaction();