mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-05 04:56:37 +08:00
feat(strm): 同名多版本择优 / keep_ext,并支持合并展示与播放切换 (#30)
* feat(strm): 同名多版本择优/keep_ext,并支持合并展示与播放切换 默认对同名不同扩展(如 .mkv/.mp4)按体积→mtime→扩展名优先级择优生成一条 strm,冲突打日志;开启 keep_ext 则分别生成 name.ext.strm。媒体详情与播放器可切换版本,列表继续合并同片多版本,Emby MediaSources 名称区分容器/分辨率。 Co-authored-by: truewhile <truewhile@users.noreply.github.com> * fix(meta): 边车匹配忽略 .mkv.strm 中间扩展,同片共享 NFO/海报/字幕 NFO、海报、字幕与刮削写出统一使用 mediaSidecarBase 词干,使竞女01.mkv.strm 与竞女01.mp4.strm 配对同一套 竞女01.nfo / 竞女01-poster / 竞女01.srt; 仍兼容历史上的 name.mkv.nfo 命名。 Co-authored-by: truewhile <truewhile@users.noreply.github.com> * fix(emby): People NFO 按共享词干匹配 keep_ext strm resolveMediaPeople 改为查找竞女01.nfo(并兼容旧的竞女01.mkv.nfo), 同时修正把 decodeNFOFile 的 partial 误当 ok 导致完整 NFO 被跳过的问题。 Co-authored-by: truewhile <truewhile@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: truewhile <truewhile@users.noreply.github.com>
This commit is contained in:
@@ -1570,3 +1570,84 @@ func TestScanLocalMetaForUploadRequeuesWhenRecentDoneSizeChanged(t *testing.T) {
|
||||
t.Fatal("expected a pending upload task for resized local file")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleVideoPreferPicksLargestAndSkipsOthers(t *testing.T) {
|
||||
svc := testStrmService(t)
|
||||
local := t.TempDir()
|
||||
p := syncPathRecord(t, svc, model.StrmProviderLocal, t.TempDir(), local, true)
|
||||
p.KeepExt = false
|
||||
st := &strmSyncState{
|
||||
s: svc,
|
||||
ctx: context.Background(),
|
||||
p: p,
|
||||
cfg: &strmPathConfig{BaseURL: "http://test.local:8096", VideoExt: csvSplit(StrmDefaultVideoExt), AddPath: 1, KeepExt: false},
|
||||
rec: &model.StrmSyncRecord{},
|
||||
syncType: model.StrmSyncTypeFull,
|
||||
seenVideo: map[string]bool{},
|
||||
seenVideoTarget: map[string]cloud.FileEntry{},
|
||||
remoteVideos: map[string][]remoteVideoCandidate{},
|
||||
}
|
||||
st.handleVideo(cloud.FileEntry{ID: "1", Name: "竞女01.mp4", Size: 100, PickCode: "pc-mp4", MTime: 1000}, "竞女01.mp4", ".mp4")
|
||||
st.handleVideo(cloud.FileEntry{ID: "2", Name: "竞女01.mkv", Size: 500, PickCode: "pc-mkv", MTime: 900}, "竞女01.mkv", ".mkv")
|
||||
st.flushPreferredVideos()
|
||||
|
||||
preferPath := filepath.Join(local, "竞女01.strm")
|
||||
if _, err := os.Stat(preferPath); err != nil {
|
||||
t.Fatalf("expected prefer strm at %s: %v", preferPath, err)
|
||||
}
|
||||
data, err := os.ReadFile(preferPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
content := string(data)
|
||||
if !strings.Contains(content, "video.mkv") {
|
||||
t.Fatalf("prefer should pick larger mkv, content=%s", content)
|
||||
}
|
||||
if !strings.Contains(content, "%E7%AB%9E%E5%A5%B301.mkv") && !strings.Contains(content, "竞女01.mkv") {
|
||||
t.Fatalf("prefer strm should point at winner path, content=%s", content)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(local, "竞女01.mkv.strm")); !os.IsNotExist(err) {
|
||||
t.Fatal("prefer mode must not write keep_ext names")
|
||||
}
|
||||
if st.rec.NewStrm != 1 {
|
||||
t.Fatalf("NewStrm=%d want 1", st.rec.NewStrm)
|
||||
}
|
||||
if st.rec.Skipped < 1 {
|
||||
t.Fatalf("Skipped=%d want >=1 for loser", st.rec.Skipped)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleVideoKeepExtWritesAllVersions(t *testing.T) {
|
||||
svc := testStrmService(t)
|
||||
local := t.TempDir()
|
||||
p := syncPathRecord(t, svc, model.StrmProviderLocal, t.TempDir(), local, true)
|
||||
p.KeepExt = true
|
||||
st := &strmSyncState{
|
||||
s: svc,
|
||||
ctx: context.Background(),
|
||||
p: p,
|
||||
cfg: &strmPathConfig{BaseURL: "http://test.local:8096", VideoExt: csvSplit(StrmDefaultVideoExt), AddPath: 1, KeepExt: true},
|
||||
rec: &model.StrmSyncRecord{},
|
||||
syncType: model.StrmSyncTypeFull,
|
||||
seenVideo: map[string]bool{},
|
||||
seenVideoTarget: map[string]cloud.FileEntry{},
|
||||
remoteVideos: map[string][]remoteVideoCandidate{},
|
||||
}
|
||||
st.handleVideo(cloud.FileEntry{ID: "1", Name: "竞女01.mp4", Size: 100, PickCode: "pc-mp4"}, "竞女01.mp4", ".mp4")
|
||||
st.handleVideo(cloud.FileEntry{ID: "2", Name: "竞女01.mkv", Size: 500, PickCode: "pc-mkv"}, "竞女01.mkv", ".mkv")
|
||||
st.flushPreferredVideos()
|
||||
|
||||
mkvPath := filepath.Join(local, "竞女01.mkv.strm")
|
||||
mp4Path := filepath.Join(local, "竞女01.mp4.strm")
|
||||
for _, path := range []string{mkvPath, mp4Path} {
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
t.Fatalf("expected keep_ext strm %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(local, "竞女01.strm")); !os.IsNotExist(err) {
|
||||
t.Fatal("keep_ext mode must not write stripped name.strm")
|
||||
}
|
||||
if st.rec.NewStrm != 2 {
|
||||
t.Fatalf("NewStrm=%d want 2", st.rec.NewStrm)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user