初始提交:短剧库源码(含转码优化 ultrafast)
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPlaybackBrowserFixture(t *testing.T) {
|
||||
root := os.Getenv("JUKU_PLAYBACK_BROWSER_ROOT")
|
||||
if root == "" {
|
||||
t.Skip("isolated browser fixture is opt-in")
|
||||
}
|
||||
mediaDir := os.Getenv("JUKU_PLAYBACK_BROWSER_MEDIA")
|
||||
if mediaDir == "" {
|
||||
t.Fatal("synthetic fixture directory required")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(mediaDir, "original.mp4")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ffmpeg, err := exec.LookPath("ffmpeg")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(root, 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
processLog := filepath.Join(root, "ffmpeg-calls.txt")
|
||||
wrapper := filepath.Join(root, "ffmpeg-fixture")
|
||||
quote := func(value string) string { return "'" + strings.ReplaceAll(value, "'", "'\"'\"'") + "'" }
|
||||
if err := os.WriteFile(wrapper, []byte("#!/bin/sh\nprintf '%s\\n' \"$1\" >> "+quote(processLog)+"\nexec "+quote(ffmpeg)+" \"$@\"\n"), 0700); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var mu sync.Mutex
|
||||
var blockClient atomic.Bool
|
||||
var requests []map[string]string
|
||||
files := http.FileServer(http.Dir(mediaDir))
|
||||
mediaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
mu.Lock()
|
||||
requests = append(requests, map[string]string{"path": r.URL.Path, "method": r.Method, "range": r.Header.Get("Range")})
|
||||
mu.Unlock()
|
||||
if r.Header.Get("Cookie") != "" || r.Header.Get("Authorization") != "" || r.Header.Get("X-Juku-Viewer") != "" {
|
||||
t.Error("site credentials reached the synthetic media origin")
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Expose-Headers", "Content-Range, Accept-Ranges")
|
||||
if blockClient.Load() && r.Header.Get("Sec-Fetch-Site") != "" {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
if r.URL.Path == "/redirect.m3u8" {
|
||||
http.Redirect(w, r, "/ts/index.m3u8", http.StatusFound)
|
||||
return
|
||||
}
|
||||
if r.URL.Path == "/denied.mp4" {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
files.ServeHTTP(w, r)
|
||||
}))
|
||||
defer mediaServer.Close()
|
||||
mediaOrigin := strings.Replace(mediaServer.URL, "127.0.0.1", "localhost", 1)
|
||||
app := viewerTestApp(t)
|
||||
app.loadedAt = time.Now()
|
||||
app.tasks = map[string]*UITask{}
|
||||
app.cond = sync.NewCond(&app.mu)
|
||||
app.statePath = filepath.Join(app.cfg.dataDirectory(), "ui-state.json")
|
||||
app.downloader.cfg.FFmpeg, app.cfg.FFmpeg = wrapper, wrapper
|
||||
app.downloader.ffmpegInstaller = &ffmpegInstaller{state: ffmpegInstallState{Status: "ready", Path: wrapper}}
|
||||
transport := mediaServer.Client().Transport
|
||||
app.downloader.client = &http.Client{Transport: rankingTransport(func(request *http.Request) (*http.Response, error) {
|
||||
if !strings.HasPrefix(request.URL.String(), mediaOrigin+"/") {
|
||||
t.Error("playback fixture attempted an external request", request.URL.Host)
|
||||
return nil, errors.New("external requests forbidden")
|
||||
}
|
||||
return transport.RoundTrip(request)
|
||||
})}
|
||||
app.cfg.adminUsername, app.cfg.adminPassword, app.cfg.adminPasswordExplicit = "admin", accountFixturePassword, true
|
||||
if err := app.prepareBrowserViewers(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
client := app.downloader.hongguoClient()
|
||||
entry := client.details["7000000000000000001"]
|
||||
entry.Chapters = nil
|
||||
for index, path := range []string{"/original.mp4", "/redirect.m3u8", "/fmp4/index.m3u8", "/qualities/master.m3u8", "/ac3.mp4", "/denied.mp4", "/original.mp4"} {
|
||||
entry.Chapters = append(entry.Chapters, Chapter{ID: providerChapterID(sourceHongguo, "7000000000000000001", fmt.Sprint(index+1)), Source: sourceHongguo, CurrentEpisode: rawEpisode(index + 1), VideoURL: mediaOrigin + path})
|
||||
}
|
||||
entry.Drama.Title, entry.Drama.TotalEpisode, entry.Drama.ReleaseStatus = "本机合成播放样本", len(entry.Chapters), "finished"
|
||||
client.details["7000000000000000001"] = entry
|
||||
app.dramas = []Drama{entry.Drama}
|
||||
task := Task{DramaID: entry.Drama.ID, DramaTitle: entry.Drama.Title, Index: 1, Total: 1, OutPath: filepath.Join(mediaDir, "original.mp4"), Chapter: entry.Chapters[0]}
|
||||
app.taskOrder = []string{"local-fixture"}
|
||||
app.tasks["local-fixture"] = &UITask{ID: "local-fixture", DramaID: task.DramaID, DramaTitle: task.DramaTitle, Status: uiStatusSuccess, Path: task.OutPath, Source: task}
|
||||
t.Cleanup(app.embySyncer().stop)
|
||||
routes := app.routes()
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/fixture/status":
|
||||
mu.Lock()
|
||||
snapshot := append([]map[string]string(nil), requests...)
|
||||
mu.Unlock()
|
||||
body, _ := os.ReadFile(processLog)
|
||||
count := 0
|
||||
if len(body) > 0 {
|
||||
count = len(strings.Split(strings.TrimSpace(string(body)), "\n"))
|
||||
}
|
||||
resources := app.mediaResources()
|
||||
resources.mu.Lock()
|
||||
active := make(map[string]int)
|
||||
for kind, value := range resources.active {
|
||||
active[kind] = value
|
||||
}
|
||||
used := resources.cacheBytes
|
||||
resources.mu.Unlock()
|
||||
writeJSON(w, http.StatusOK, map[string]any{"ffmpegCalls": count, "requests": snapshot, "active": active, "cacheBytes": used})
|
||||
return
|
||||
case "/fixture/direct-access":
|
||||
var input struct {
|
||||
Blocked bool `json:"blocked"`
|
||||
}
|
||||
if json.NewDecoder(r.Body).Decode(&input) != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
blockClient.Store(input.Blocked)
|
||||
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
||||
return
|
||||
case "/fixture/expire":
|
||||
app.closePlaybacks()
|
||||
writeJSON(w, http.StatusOK, map[string]bool{"ok": true})
|
||||
return
|
||||
case "/api/ui/cover/repair":
|
||||
writeJSON(w, http.StatusOK, map[string]any{"dramaId": historyFixtureDramaID, "retryAfter": 300})
|
||||
return
|
||||
case "/api/ui/image":
|
||||
t.Error("playback fixture must never request source images")
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
routes.ServeHTTP(w, r)
|
||||
}))
|
||||
defer server.Close()
|
||||
ready, _ := json.Marshal(map[string]string{"url": server.URL, "mediaOrigin": mediaOrigin, "adminPassword": accountFixturePassword})
|
||||
if err := os.WriteFile(filepath.Join(root, "ready.json"), ready, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deadline := time.NewTimer(8 * time.Minute)
|
||||
defer deadline.Stop()
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if _, err := os.Stat(filepath.Join(root, "stop")); err == nil {
|
||||
return
|
||||
}
|
||||
case <-deadline.C:
|
||||
t.Fatal("isolated playback browser fixture timed out")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user