Harden relay preview mp4 handling

这个提交包含在:
cryptocommuniums-afk
2026-03-17 15:03:33 +08:00
父节点 b1752110fb
当前提交 1adadbad8c
修改 5 个文件,包含 390 行新增21 行删除

查看文件

@@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"os"
@@ -410,6 +411,186 @@ func TestProcessRelayPreviewPublishesBufferedWebM(t *testing.T) {
}
}
func TestHandleSegmentUploadPersistsRelayMP4InitSegment(t *testing.T) {
store, err := newSessionStore(t.TempDir())
if err != nil {
t.Fatalf("newSessionStore: %v", err)
}
server := newMediaServer(store)
session, err := store.createSession(CreateSessionRequest{UserID: "1", Title: "Relay MP4", Purpose: "relay", RelayBufferSeconds: 120})
if err != nil {
t.Fatalf("createSession: %v", err)
}
req := httptest.NewRequest(http.MethodPost, "/media/sessions/"+session.ID+"/segments?sequence=1&durationMs=10000", strings.NewReader("mp4-init"))
req.Header.Set("Content-Type", "video/mp4;codecs=avc1")
res := httptest.NewRecorder()
server.routes().ServeHTTP(res, req)
if res.Code != http.StatusAccepted {
t.Fatalf("expected segment upload 202, got %d", res.Code)
}
current, err := store.getSession(session.ID)
if err != nil {
t.Fatalf("getSession: %v", err)
}
if current.RelayInitFilename != "000001.mp4" {
t.Fatalf("expected relay init filename to be recorded, got %q", current.RelayInitFilename)
}
body, err := os.ReadFile(store.relayInitPath(session.ID))
if err != nil {
t.Fatalf("read relay init: %v", err)
}
if string(body) != "mp4-init" {
t.Fatalf("unexpected relay init contents: %q", string(body))
}
}
func TestProcessRelayPreviewUsesPersistedInitForMP4Fragments(t *testing.T) {
tempDir := t.TempDir()
store, err := newSessionStore(tempDir)
if err != nil {
t.Fatalf("newSessionStore: %v", err)
}
session, err := store.createSession(CreateSessionRequest{UserID: "1", Title: "Relay MP4 Preview", Purpose: "relay", RelayBufferSeconds: 120})
if err != nil {
t.Fatalf("createSession: %v", err)
}
if err := os.WriteFile(store.relayInitPath(session.ID), []byte(strings.Repeat("i", 6000)), 0o644); err != nil {
t.Fatalf("write relay init: %v", err)
}
if err := os.WriteFile(filepath.Join(store.segmentsDir(session.ID), "000082.mp4"), []byte(strings.Repeat("a", 6000)), 0o644); err != nil {
t.Fatalf("write segment 82: %v", err)
}
if err := os.WriteFile(filepath.Join(store.segmentsDir(session.ID), "000083.mp4"), []byte(strings.Repeat("b", 6000)), 0o644); err != nil {
t.Fatalf("write segment 83: %v", err)
}
if _, err := store.updateSession(session.ID, func(current *Session) error {
current.Purpose = PurposeRelay
current.RelayInitFilename = "000001.mp4"
current.Segments = []SegmentMeta{
{
Sequence: 82,
Filename: "000082.mp4",
DurationMS: 10000,
SizeBytes: 6000,
ContentType: "video/mp4;codecs=avc1",
},
{
Sequence: 83,
Filename: "000083.mp4",
DurationMS: 10000,
SizeBytes: 6000,
ContentType: "video/mp4;codecs=avc1",
},
}
return nil
}); err != nil {
t.Fatalf("updateSession: %v", err)
}
fakeFFmpeg := filepath.Join(tempDir, "ffmpeg")
script := "#!/bin/sh\ninput=''\noutput=''\nprev=''\nfor arg in \"$@\"; do\n if [ \"$prev\" = '-i' ]; then input=\"$arg\"; fi\n prev=\"$arg\"\n output=\"$arg\"\ndone\nif [ -n \"$input\" ] && [ -f \"$input\" ]; then cp \"$input\" \"$output\"; else : > \"$output\"; fi\n"
if err := os.WriteFile(fakeFFmpeg, []byte(script), 0o755); err != nil {
t.Fatalf("write fake ffmpeg: %v", err)
}
t.Setenv("PATH", tempDir+string(os.PathListSeparator)+os.Getenv("PATH"))
if err := processRollingPreview(store, session.ID); err != nil {
t.Fatalf("processRollingPreview: %v", err)
}
current, err := store.getSession(session.ID)
if err != nil {
t.Fatalf("getSession: %v", err)
}
if current.PreviewStatus != PreviewReady {
t.Fatalf("expected preview ready, got %s", current.PreviewStatus)
}
if current.Playback.PreviewURL == "" {
t.Fatalf("expected preview url to be populated")
}
}
func TestProcessRelayPreviewKeepsPreviousPreviewOnFailure(t *testing.T) {
tempDir := t.TempDir()
store, err := newSessionStore(tempDir)
if err != nil {
t.Fatalf("newSessionStore: %v", err)
}
session, err := store.createSession(CreateSessionRequest{UserID: "1", Title: "Relay Existing Preview", Purpose: "relay", RelayBufferSeconds: 120})
if err != nil {
t.Fatalf("createSession: %v", err)
}
if err := os.MkdirAll(store.publicDir(session.ID), 0o755); err != nil {
t.Fatalf("mkdir public dir: %v", err)
}
if err := os.WriteFile(filepath.Join(store.publicDir(session.ID), "preview.webm"), []byte("existing-preview"), 0o644); err != nil {
t.Fatalf("write preview: %v", err)
}
if err := os.WriteFile(filepath.Join(store.segmentsDir(session.ID), "000001.webm"), []byte("segment-one"), 0o644); err != nil {
t.Fatalf("write segment 1: %v", err)
}
if err := os.WriteFile(filepath.Join(store.segmentsDir(session.ID), "000002.webm"), []byte("segment-two"), 0o644); err != nil {
t.Fatalf("write segment 2: %v", err)
}
if _, err := store.updateSession(session.ID, func(current *Session) error {
current.Purpose = PurposeRelay
current.PreviewStatus = PreviewReady
current.Playback.PreviewURL = fmt.Sprintf("/media/assets/sessions/%s/preview.webm", session.ID)
current.Segments = []SegmentMeta{
{
Sequence: 1,
Filename: "000001.webm",
DurationMS: 10000,
SizeBytes: int64(len("segment-one")),
ContentType: "video/webm",
},
{
Sequence: 2,
Filename: "000002.webm",
DurationMS: 10000,
SizeBytes: int64(len("segment-two")),
ContentType: "video/webm",
},
}
return nil
}); err != nil {
t.Fatalf("updateSession: %v", err)
}
fakeFFmpeg := filepath.Join(tempDir, "ffmpeg")
script := "#!/bin/sh\nexit 1\n"
if err := os.WriteFile(fakeFFmpeg, []byte(script), 0o755); err != nil {
t.Fatalf("write fake ffmpeg: %v", err)
}
t.Setenv("PATH", tempDir+string(os.PathListSeparator)+os.Getenv("PATH"))
if err := processRollingPreview(store, session.ID); err == nil {
t.Fatalf("expected processRollingPreview to surface failure")
}
current, err := store.getSession(session.ID)
if err != nil {
t.Fatalf("getSession: %v", err)
}
if current.PreviewStatus != PreviewReady {
t.Fatalf("expected previous preview to remain ready, got %s", current.PreviewStatus)
}
if current.Playback.PreviewURL == "" {
t.Fatalf("expected preview url to remain available")
}
if current.LastError == "" {
t.Fatalf("expected last error to be recorded")
}
}
func TestPruneExpiredRelaySessionsRemovesOldCache(t *testing.T) {
store, err := newSessionStore(t.TempDir())
if err != nil {