Files
truewhile 79943d9067 优化
2026-11-05 17:03:26 +08:00

128 lines
4.8 KiB
Go

package reader
import (
"errors"
"os"
"path/filepath"
"strings"
"testing"
)
// 原生解码与媒体通道独立:本地文件和 STRM 都保留原始目标及签名,不依赖 ffmpeg。
func TestNativeLocalAudioContent(t *testing.T) {
for _, tc := range []struct {
name string
target string // 非空时写入 STRM;空则直接导入 WMA
}{
{name: "local WMA"},
{name: "relative STRM WMA", target: "../original.wma"},
{name: "remote STRM WMA", target: "https://cdn.example.com/01.wma?token=original"},
{name: "STRM playback API WMA", target: "http://127.0.0.1:8080/api/strm/play/cloud115/video.wma?acct=a&pickcode=p"},
{name: "remote STRM HLS", target: "https://cdn.example.com/01.m3u8?token=original"},
} {
t.Run(tc.name, func(t *testing.T) {
svc := newLocalBookService(t)
svc.cfg.App.FFmpegPath = filepath.Join(t.TempDir(), "missing-ffmpeg")
dir := t.TempDir()
original := filepath.Join(dir, "01.wma")
body := "original-wma-bytes"
if tc.target != "" {
original = tc.target
body = tc.target + "\n"
}
name := "01.wma"
if tc.target != "" {
name = "01.strm"
}
if err := os.WriteFile(filepath.Join(dir, name), []byte(body), 0o640); err != nil {
t.Fatal(err)
}
if tc.target != "" && !isRemoteMediaURL(original) {
original = filepath.Clean(filepath.Join(dir, original))
}
book, err := svc.ImportLocalAudioDir(t.Context(), "u1", dir)
if err != nil {
t.Fatal(err)
}
for _, opts := range []ContentOptions{
{NativeAudio: true},
{NativeAudio: true, MediaDirect: true},
{NativeAudio: true, ForceProxy: true},
} {
out, err := svc.GetContentForBook(t.Context(), "u1", book.ID, 0, opts)
if err != nil {
t.Fatalf("native opts=%+v: %v", opts, err)
}
if out.Type != "audio" || out.Transcoding || out.MediaDirect || len(out.Tracks) != 1 {
t.Fatalf("native content = %+v", out)
}
if out.IsHLS != looksLikeHLSURL(original) {
t.Fatalf("HLS flag=%v for %q", out.IsHLS, original)
}
track := out.Tracks[0]
var got string
if isRemoteMediaURL(original) {
if !strings.HasPrefix(track, "/api/reader/media?") {
t.Fatalf("remote target must use signed proxy: %q", track)
}
got, err = svc.VerifyProxyURL(book.ID, queryParam(t, track, "u"), queryParam(t, track, "s"))
if _, badErr := svc.VerifyProxyURL(book.ID, queryParam(t, track, "u"), "bad"); badErr == nil {
t.Fatal("tampered proxy signature accepted")
}
} else {
if !strings.HasPrefix(track, "/api/reader/local/audio?") {
t.Fatalf("local target must use signed audio endpoint: %q", track)
}
got, err = svc.VerifyLocalAudioURL(book.ID, queryParam(t, track, "p"), queryParam(t, track, "s"))
if _, badErr := svc.VerifyLocalAudioURL(book.ID, queryParam(t, track, "p"), "bad"); badErr == nil {
t.Fatal("tampered local signature accepted")
}
}
if err != nil || got != original {
t.Fatalf("original target = %q, err=%v, want %q", got, err, original)
}
batch, err := svc.GetContentBatch(t.Context(), "u1", book.ID, []int{0}, false, opts)
if err != nil {
t.Fatal(err)
}
if len(batch.Items) != 1 || batch.Items[0].Error != "" || batch.Items[0].Transcoding ||
len(batch.Items[0].Tracks) != 1 || batch.Items[0].Tracks[0] != track || batch.Items[0].IsHLS != out.IsHLS {
t.Fatalf("native batch = %+v", batch)
}
}
if _, err := svc.GetContentForBook(t.Context(), "other", book.ID, 0, ContentOptions{NativeAudio: true}); err == nil {
t.Fatal("native audio must retain book ownership check")
}
if !needsAudioTranscode(original) {
return
}
// 网页默认及旧方法仍要求 ffmpeg。
if _, err := svc.GetContentForBook(t.Context(), "u1", book.ID, 0, ContentOptions{}); !errors.Is(err, ErrAudioTranscodeUnavailable) {
t.Fatalf("browser without ffmpeg: %v", err)
}
if _, err := svc.LocalChapterContent(t.Context(), "u1", book.ID, 0); !errors.Is(err, ErrAudioTranscodeUnavailable) {
t.Fatalf("legacy local content without ffmpeg: %v", err)
}
// 这里只选择转码地址,不执行 ffmpeg;用已存在的可执行文件满足 LookPath。
svc.cfg.App.FFmpegPath, err = os.Executable()
if err != nil {
t.Fatal(err)
}
out, err := svc.GetContentForBook(t.Context(), "u1", book.ID, 0, ContentOptions{})
if err != nil {
t.Fatal(err)
}
if !out.Transcoding || len(out.Tracks) != 1 || out.Tracks[0] != svc.AudioTranscodeURL(book.ID, original) {
t.Fatalf("browser must still select transcode: %+v", out)
}
batch, err := svc.GetContentBatch(t.Context(), "u1", book.ID, []int{0}, false, ContentOptions{})
if err != nil {
t.Fatal(err)
}
if len(batch.Items) != 1 || batch.Items[0].Error != "" || !batch.Items[0].Transcoding {
t.Fatalf("browser batch must report transcoding: %+v", batch)
}
})
}
}