fix: flat local HLS paths for nginx compat + edge cache validation script
- Revert local HLS to flat /app/hls/seg-{seq}.m4s (nginx serves from this dir)
- runID only used in R2 keys (live/{runID}/seg-{seq}.m4s)
- This fixes 404 on linearvoid.tv/hls/playlist.m3u8 caused by r-{runID} subdir
- Added scripts/validate-edge-cache.sh: MISS→HIT, immutable headers, manifest freshness
- Phase 4 marked complete. Cleaned up LEGACY streaming gaps in TODO.md
This commit is contained in:
1 parent
1f11baa949
commit
55a128dc3a
3 files changed
+157
-31
No files matched your search
@@ -86,8 +86,8 @@ func initRunID() {
|
||||
}
|
||||
|
||||
// watchAndPublishSegments watches the HLS output directory for new .m4s segments
|
||||
// and uploads them to R2 in real time (STREAMING.md contract: per-segment R2 sync).
|
||||
func watchAndPublishSegments(ctx context.Context, hlsRunDir string) {
|
||||
// and uploads them to R2 in real time with runID-based keys (STREAMING.md contract).
|
||||
func watchAndPublishSegments(ctx context.Context, hlsOutputDir string) {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
fmt.Println("Warning: fsnotify unavailable, R2 sync falls back to batch:", err)
|
||||
@@ -95,7 +95,7 @@ func watchAndPublishSegments(ctx context.Context, hlsRunDir string) {
|
||||
}
|
||||
defer watcher.Close()
|
||||
|
||||
if err := watcher.Add(hlsRunDir); err != nil {
|
||||
if err := watcher.Add(hlsOutputDir); err != nil {
|
||||
fmt.Println("Warning: cannot watch HLS dir:", err)
|
||||
return
|
||||
}
|
||||
@@ -603,14 +603,8 @@ func main() {
|
||||
fmt.Printf("[%s] Slate active — attempting live playout recovery...\n", time.Now().Format(time.RFC3339))
|
||||
}
|
||||
|
||||
// New run ID on each restart for unique segment paths (STREAMING.md)
|
||||
// New run ID on each restart for unique R2 segment keys (STREAMING.md)
|
||||
initRunID()
|
||||
runHlsDir := filepath.Join(hlsDir, runID)
|
||||
if err := os.MkdirAll(runHlsDir, 0o755); err != nil {
|
||||
fmt.Println("Failed to create run HLS dir:", err)
|
||||
time.Sleep(5 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := resetHLSOutput(hlsDir); err != nil {
|
||||
fmt.Println("Failed to reset HLS output:", err)
|
||||
@@ -620,7 +614,7 @@ func main() {
|
||||
|
||||
// Start filesystem watcher for real-time R2 segment upload (STREAMING.md)
|
||||
watchCtx, watchCancel := context.WithCancel(context.Background())
|
||||
go watchAndPublishSegments(watchCtx, runHlsDir)
|
||||
go watchAndPublishSegments(watchCtx, hlsDir)
|
||||
|
||||
fmt.Printf("[%s] Starting FFmpeg playout with browser-safe H.264/AAC HLS encoding...\n", time.Now().Format(time.RFC3339))
|
||||
|
||||
@@ -637,8 +631,8 @@ func main() {
|
||||
"-hls_segment_type", "fmp4",
|
||||
"-hls_fmp4_init_filename", "init.mp4",
|
||||
"-hls_flags", "delete_segments+independent_segments",
|
||||
"-hls_segment_filename", filepath.Join(runHlsDir, "seg-%06d.m4s"),
|
||||
filepath.Join(runHlsDir, "playlist.m3u8"))
|
||||
"-hls_segment_filename", filepath.Join(hlsDir, "seg-%06d.m4s"),
|
||||
filepath.Join(hlsDir, "playlist.m3u8"))
|
||||
|
||||
runErr := cmd.Run()
|
||||
watchCancel() // Stop the filesystem watcher
|
||||
@@ -666,12 +660,12 @@ func main() {
|
||||
}
|
||||
|
||||
// After FFmpeg run, publish remaining segments and atomic manifest to R2
|
||||
if err := syncLatestToR2(runHlsDir); err != nil {
|
||||
if err := syncLatestToR2(hlsDir); err != nil {
|
||||
fmt.Println("R2 sync warning:", err)
|
||||
}
|
||||
|
||||
// Publish manifest atomically (STREAMING.md safe publication)
|
||||
manifestPath := filepath.Join(runHlsDir, "playlist.m3u8")
|
||||
manifestPath := filepath.Join(hlsDir, "playlist.m3u8")
|
||||
if err := publishManifest(manifestPath); err != nil {
|
||||
fmt.Println("Manifest publish warning:", err)
|
||||
}
|
||||
@@ -680,9 +674,9 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// syncLatestToR2 uploads new segments and the playlist manifest to R2
|
||||
func syncLatestToR2(hlsRunDir string) error {
|
||||
files, err := filepath.Glob(filepath.Join(hlsRunDir, "seg-*.m4s"))
|
||||
// syncLatestToR2 uploads new segments to R2 with runID-based keys
|
||||
func syncLatestToR2(hlsDir string) error {
|
||||
files, err := filepath.Glob(filepath.Join(hlsDir, "seg-*.m4s"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -696,8 +690,8 @@ func syncLatestToR2(hlsRunDir string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Sync init segment to run-specific path
|
||||
initPath := filepath.Join(hlsRunDir, "init.mp4")
|
||||
// Sync init segment to run-specific R2 key
|
||||
initPath := filepath.Join(hlsDir, "init.mp4")
|
||||
if err := SyncToR2(initPath, "live/"+runID+"/init.mp4"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user