mirror of
https://github.com/truewhile/MeBox.git
synced 2026-11-12 19:34:24 +08:00
128 lines
4.8 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|