Files
gopeed/pkg/download/media_spooling_test.go
T

151 lines
4.5 KiB
Go

package download
import (
"bytes"
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
"github.com/GopeedLab/gopeed/pkg/base"
media "github.com/GopeedLab/gopeed/pkg/download/engine/ffmpeg"
)
func TestQueuedMergeReportsInputProgress(t *testing.T) {
for _, mode := range []string{"streams", "http"} {
t.Run(mode, func(t *testing.T) {
release1, err := media.Acquire(context.Background())
if err != nil {
t.Fatal(err)
}
defer release1()
release2, err := media.Acquire(context.Background())
if err != nil {
t.Fatal(err)
}
defer release2()
video, err := os.ReadFile("engine/testdata/ffmpeg/video.mp4")
if err != nil {
t.Fatal(err)
}
audio, err := os.ReadFile("engine/testdata/ffmpeg/audio.mp4")
if err != nil {
t.Fatal(err)
}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
b := video
if r.URL.Path == "/audio" {
b = audio
}
http.ServeContent(w, r, "media", time.Time{}, bytes.NewReader(b))
}))
defer server.Close()
d := NewDownloader(&DownloaderConfig{Storage: NewMemStorage(), StorageDir: t.TempDir(), TempDir: t.TempDir(), RefreshInterval: 50})
if err := d.Setup(); err != nil {
t.Fatal(err)
}
defer d.Clear()
e, _ := d.newExtensionEngine()
defer e.Close()
e.Runtime.Set("videoBytes", e.Runtime.NewArrayBuffer(video))
e.Runtime.Set("audioBytes", e.Runtime.NewArrayBuffer(audio))
e.Runtime.Set("mode", mode)
e.Runtime.Set("sourceURL", server.URL)
result, err := e.RunString(`__gopeed_blob_create_object_url(()=>{
const source=b=>new ReadableStream({start(c){c.enqueue(new Uint8Array(b));c.close();}});
return __gopeed_ffmpeg.merge(mode==='http'?{video:{url:sourceURL+'/video'},audio:{url:sourceURL+'/audio'}}:
{inputs:()=>({video:source(videoBytes),audio:source(audioBytes)})});
},{contentType:'video/mp4'})`)
if err != nil {
t.Fatal(err)
}
output := t.TempDir()
id, err := d.CreateDirect(&base.Request{URL: fmt.Sprint(result)}, &base.Options{Path: output, Name: "merged.mp4"})
if err != nil {
t.Fatal(err)
}
deadline := time.Now().Add(5 * time.Second)
for {
task := d.GetTask(id)
task.lock.Lock()
task.statusLock.Lock()
status, downloaded := task.Status, task.Progress.Downloaded
total := int64(0)
if task.Meta.Res != nil {
total = task.Meta.Res.Size
}
task.statusLock.Unlock()
task.lock.Unlock()
if downloaded == int64(len(video)+len(audio)) {
if status != base.DownloadStatusRunning || total != 0 {
t.Fatalf("queued task: status=%v total=%d", status, total)
}
break
}
if time.Now().After(deadline) {
t.Fatalf("input progress not reported: status=%v bytes=%d", status, downloaded)
}
time.Sleep(20 * time.Millisecond)
}
task := d.GetTask(id)
if mode == "streams" {
time.Sleep(16 * time.Second)
} else {
time.Sleep(200 * time.Millisecond)
}
task.lock.Lock()
task.statusLock.Lock()
speed := task.Progress.Speed
task.statusLock.Unlock()
task.lock.Unlock()
// Once the upstream is drained, waiting for FFmpeg is still a running task.
if speed < 0 || (mode == "streams" && speed != 0) {
t.Fatal("negative queued speed", speed)
}
release1()
deadline = time.Now().Add(100 * time.Second)
for d.taskStatus(task) != base.DownloadStatusDone {
if time.Now().After(deadline) {
t.Fatal("merge did not complete")
}
time.Sleep(20 * time.Millisecond)
}
// Done is published before extension hooks finish. Let the watcher
// release its Blob/session before tearing down the downloader.
deadline = time.Now().Add(5 * time.Second)
for {
if _, active := d.watchedTasks.Load(id); !active {
break
}
if time.Now().After(deadline) {
t.Fatal("completion hooks did not finish")
}
time.Sleep(10 * time.Millisecond)
}
body, err := os.ReadFile(filepath.Join(output, "merged.mp4"))
if err != nil {
t.Fatal(err)
}
if !bytes.Contains(body, []byte("moof")) {
t.Fatal("invalid merged output")
}
task.lock.Lock()
task.statusLock.Lock()
downloaded, total := task.Progress.Downloaded, task.Meta.Res.Size
task.statusLock.Unlock()
task.lock.Unlock()
if downloaded != int64(len(body)) || total != int64(len(body)) {
t.Fatal("completion did not use output size", downloaded, total, len(body))
}
files, err := os.ReadDir(d.cfg.TempDir)
if err != nil || len(files) != 0 {
t.Fatal("temporary media leaked", files, err)
}
})
}
}