mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-08 22:36:37 +08:00
fix: stabilize library paths and subscription matching
This commit is contained in:
@@ -120,7 +120,7 @@ func TestScanRootCloudLibraryCreatesAutoCategoryLibraries(t *testing.T) {
|
||||
wantLibraries := map[string]string{
|
||||
"cloud://openlist/电视剧/欧美剧/The Show/The.Show.S01E01.mkv": byDisplayDir["电视剧/欧美剧"].ID,
|
||||
"cloud://openlist/电影/华语电影/Movie.2024.mkv": byDisplayDir["电影/华语电影"].ID,
|
||||
"cloud://openlist/国漫/剑来/剑来.S01E01.mkv": byDisplayDir["动漫/国漫"].ID,
|
||||
"cloud://openlist/动漫/国漫/剑来/剑来.S01E01.mkv": byDisplayDir["动漫/国漫"].ID,
|
||||
}
|
||||
for _, row := range rows {
|
||||
if row.LibraryID != wantLibraries[row.Path] {
|
||||
@@ -162,6 +162,203 @@ func TestScanRootCloudLibraryCreatesAutoCategoryLibraries(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanRootCloudAutoCategoryAppendsExistingLibraryRoot(t *testing.T) {
|
||||
upstream := newOpenListAPIServer(t, func(path string, page, perPage int) ([]openListTestEntry, int) {
|
||||
switch path {
|
||||
case "/":
|
||||
return []openListTestEntry{{Name: "电影", IsDir: true}}, 1
|
||||
case "/电影":
|
||||
return []openListTestEntry{{Name: "华语电影", IsDir: true}}, 1
|
||||
case "/电影/华语电影":
|
||||
return []openListTestEntry{{Name: "Movie.2024.mkv", Size: 202}}, 1
|
||||
default:
|
||||
t.Fatalf("unexpected openlist path %q", path)
|
||||
return nil, 0
|
||||
}
|
||||
})
|
||||
defer upstream.Close()
|
||||
|
||||
db := newServiceTestDB(t, &model.Library{}, &model.LibraryRoot{}, &model.Media{}, &model.Setting{}, &model.StorageConfig{})
|
||||
repos := repository.New(db)
|
||||
storage := newOpenListStorageForTest(t, repos, upstream.URL)
|
||||
local := model.Library{Name: "华语电影", Path: "/media/电影/华语电影", Type: "movie", Enabled: true}
|
||||
if err := repos.Library.CreateWithRoots(t.Context(), &local, []model.LibraryRoot{{
|
||||
Name: "华语电影",
|
||||
Path: local.Path,
|
||||
Enabled: true,
|
||||
}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root := model.Library{Name: "OpenList", Path: "cloud://openlist", Type: "movie", Enabled: true}
|
||||
if err := repos.Library.Create(t.Context(), &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scanner := NewScannerService(&config.Config{}, zap.NewNop(), repos, NewHub(zap.NewNop()), nil, nil)
|
||||
scanner.SetStorageConfig(storage)
|
||||
|
||||
res, err := scanner.ScanLibrary(t.Context(), root.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("scan root cloud: %v", err)
|
||||
}
|
||||
if res.Added != 1 {
|
||||
t.Fatalf("added = %d, want 1", res.Added)
|
||||
}
|
||||
libs, err := repos.Library.List(t.Context())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, lib := range libs {
|
||||
if CloudLibraryAutoCategory(lib) {
|
||||
t.Fatalf("auto category should append to existing library, got extra library %#v", lib)
|
||||
}
|
||||
}
|
||||
roots, err := repos.Library.ListRoots(t.Context(), local.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(roots) != 2 {
|
||||
t.Fatalf("roots = %#v, want local root plus cloud root", roots)
|
||||
}
|
||||
cloudRoot := roots[1]
|
||||
if cloudRoot.Name != "华语电影" || !CloudLibraryAutoCategory(model.Library{Path: cloudRoot.Path}) {
|
||||
t.Fatalf("cloud root = %#v, want auto-category 华语电影 root", cloudRoot)
|
||||
}
|
||||
info, ok := ParseCloudLibraryMount(cloudRoot.Path)
|
||||
if !ok || info.DisplayDir != "电影/华语电影" || info.ScanDir != "电影/华语电影" {
|
||||
t.Fatalf("cloud root mount = %#v, want display/scan 电影/华语电影", info)
|
||||
}
|
||||
var media model.Media
|
||||
if err := repos.DB.First(&media, "path = ?", "cloud://openlist/电影/华语电影/Movie.2024.mkv").Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if media.LibraryID != local.ID || media.LibraryRootID != cloudRoot.ID {
|
||||
t.Fatalf("media placement = library %s root %s, want %s/%s", media.LibraryID, media.LibraryRootID, local.ID, cloudRoot.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanRootCloudAutoCategoryPreservesFlatScanDir(t *testing.T) {
|
||||
upstream := newOpenListAPIServer(t, func(path string, page, perPage int) ([]openListTestEntry, int) {
|
||||
switch path {
|
||||
case "/":
|
||||
return []openListTestEntry{{Name: "国漫", IsDir: true}}, 1
|
||||
case "/国漫":
|
||||
return []openListTestEntry{{Name: "剑来", IsDir: true}}, 1
|
||||
case "/国漫/剑来":
|
||||
return []openListTestEntry{{Name: "剑来.S01E01.mkv", Size: 303}}, 1
|
||||
default:
|
||||
t.Fatalf("unexpected openlist path %q", path)
|
||||
return nil, 0
|
||||
}
|
||||
})
|
||||
defer upstream.Close()
|
||||
|
||||
db := newServiceTestDB(t, &model.Library{}, &model.LibraryRoot{}, &model.Media{}, &model.Setting{}, &model.StorageConfig{})
|
||||
repos := repository.New(db)
|
||||
storage := newOpenListStorageForTest(t, repos, upstream.URL)
|
||||
local := model.Library{Name: "国漫", Path: "/media/动漫/国漫", Type: "anime", Enabled: true}
|
||||
if err := repos.Library.CreateWithRoots(t.Context(), &local, []model.LibraryRoot{{
|
||||
Name: "国漫",
|
||||
Path: local.Path,
|
||||
Enabled: true,
|
||||
}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root := model.Library{Name: "OpenList", Path: "cloud://openlist", Type: "movie", Enabled: true}
|
||||
if err := repos.Library.Create(t.Context(), &root); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scanner := NewScannerService(&config.Config{}, zap.NewNop(), repos, NewHub(zap.NewNop()), nil, nil)
|
||||
scanner.SetStorageConfig(storage)
|
||||
|
||||
if _, err := scanner.ScanLibrary(t.Context(), root.ID); err != nil {
|
||||
t.Fatalf("scan root cloud: %v", err)
|
||||
}
|
||||
roots, err := repos.Library.ListRoots(t.Context(), local.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(roots) != 2 {
|
||||
t.Fatalf("roots = %#v, want local root plus flat cloud root", roots)
|
||||
}
|
||||
cloudRoot := roots[1]
|
||||
info, ok := ParseCloudLibraryMount(cloudRoot.Path)
|
||||
if !ok || info.DisplayDir != "动漫/国漫" || info.ScanDir != "国漫" {
|
||||
t.Fatalf("flat cloud root mount = %#v, want display 动漫/国漫 and scan 国漫", info)
|
||||
}
|
||||
res, err := scanner.ScanLibraryRoot(t.Context(), local.ID, cloudRoot.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("scan flat cloud root: %v", err)
|
||||
}
|
||||
if res.Skipped != 1 && res.Updated != 1 {
|
||||
t.Fatalf("flat cloud root rescan = %#v, want existing media refreshed/skipped", res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanRootCloudAutoCategoryMigratesExistingAutoLibrary(t *testing.T) {
|
||||
upstream := newOpenListAPIServer(t, func(path string, page, perPage int) ([]openListTestEntry, int) {
|
||||
switch path {
|
||||
case "/":
|
||||
return []openListTestEntry{{Name: "电视剧", IsDir: true}}, 1
|
||||
case "/电视剧":
|
||||
return []openListTestEntry{{Name: "欧美剧", IsDir: true}}, 1
|
||||
case "/电视剧/欧美剧":
|
||||
return []openListTestEntry{{Name: "The Show", IsDir: true}}, 1
|
||||
case "/电视剧/欧美剧/The Show":
|
||||
return []openListTestEntry{{Name: "The.Show.S01E01.mkv", Size: 101}}, 1
|
||||
default:
|
||||
t.Fatalf("unexpected openlist path %q", path)
|
||||
return nil, 0
|
||||
}
|
||||
})
|
||||
defer upstream.Close()
|
||||
|
||||
db := newServiceTestDB(t, &model.Library{}, &model.LibraryRoot{}, &model.Media{}, &model.Setting{}, &model.StorageConfig{})
|
||||
repos := repository.New(db)
|
||||
storage := newOpenListStorageForTest(t, repos, upstream.URL)
|
||||
local := model.Library{Name: "欧美剧", Path: "/media/电视剧/欧美剧", Type: "tv", Enabled: true}
|
||||
if err := repos.Library.CreateWithRoots(t.Context(), &local, []model.LibraryRoot{{
|
||||
Name: "欧美剧",
|
||||
Path: local.Path,
|
||||
Enabled: true,
|
||||
}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
root := model.Library{Name: "OpenList", Path: "cloud://openlist", Type: "movie", Enabled: true}
|
||||
oldAuto := model.Library{Name: "欧美剧", Path: BuildCloudAutoCategoryLibraryPath("openlist", "电视剧/欧美剧"), Type: "tv", Enabled: true}
|
||||
for _, lib := range []*model.Library{&root, &oldAuto} {
|
||||
if err := repos.Library.Create(t.Context(), lib); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
mediaPath := "cloud://openlist/电视剧/欧美剧/The Show/The.Show.S01E01.mkv"
|
||||
if err := repos.DB.Create(&model.Media{LibraryID: oldAuto.ID, Title: "The Show", Path: mediaPath}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
scanner := NewScannerService(&config.Config{}, zap.NewNop(), repos, NewHub(zap.NewNop()), nil, nil)
|
||||
scanner.SetStorageConfig(storage)
|
||||
|
||||
if _, err := scanner.ScanLibrary(t.Context(), root.ID); err != nil {
|
||||
t.Fatalf("scan root cloud: %v", err)
|
||||
}
|
||||
if old, err := repos.Library.FindByID(t.Context(), oldAuto.ID); err != nil || old != nil {
|
||||
t.Fatalf("old auto library = %#v, err=%v; want removed", old, err)
|
||||
}
|
||||
roots, err := repos.Library.ListRoots(t.Context(), local.ID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(roots) != 2 {
|
||||
t.Fatalf("roots = %#v, want local root plus migrated cloud root", roots)
|
||||
}
|
||||
var media model.Media
|
||||
if err := repos.DB.First(&media, "path = ?", mediaPath).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if media.LibraryID != local.ID || media.LibraryRootID != roots[1].ID {
|
||||
t.Fatalf("migrated media placement = %s/%s, want %s/%s", media.LibraryID, media.LibraryRootID, local.ID, roots[1].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestScanCloudLibraryListsChildDirectoriesConcurrently(t *testing.T) {
|
||||
var active int32
|
||||
var maxActive int32
|
||||
@@ -242,3 +439,19 @@ func TestScanCloudLibraryListsChildDirectoriesConcurrently(t *testing.T) {
|
||||
t.Fatalf("scan result = %#v, want visited=2 added=2", res)
|
||||
}
|
||||
}
|
||||
|
||||
func newOpenListStorageForTest(t *testing.T, repos *repository.Container, serverURL string) *StorageConfigService {
|
||||
t.Helper()
|
||||
log := zap.NewNop()
|
||||
storage := NewStorageConfigService(log, repos, NewCryptoService("", log))
|
||||
if _, err := storage.Save(t.Context(), StorageInput{
|
||||
Type: "openlist",
|
||||
Config: map[string]any{
|
||||
"server": serverURL,
|
||||
"token": "openlist-token",
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return storage
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user