mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 03:26:37 +08:00
fix(media): scan new library roots immediately
This commit is contained in:
@@ -106,8 +106,21 @@ func createLibraryHandler(svc *service.Container) gin.HandlerFunc {
|
||||
}
|
||||
uid, _ := c.Get("ctx_user_id")
|
||||
svc.Audit.Record(c.Request.Context(), toString(uid), "library.create", l.ID, c.ClientIP(), l.Path)
|
||||
// Refresh fsnotify watcher to pick up the new library root.
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
// Refresh fsnotify watcher to pick up the new library root, then perform
|
||||
// an initial scan. Without the scan a newly-created library remained
|
||||
// empty until the operator pressed the separate "扫描" action.
|
||||
if svc.Watcher != nil {
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
}
|
||||
if len(l.Roots) == 0 {
|
||||
queueLibraryRootScan(svc, l.ID, "")
|
||||
} else {
|
||||
for _, root := range l.Roots {
|
||||
if root.Enabled {
|
||||
queueLibraryRootScan(svc, l.ID, root.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusCreated, l)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,12 @@ func createLibraryRootHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
if svc.Watcher != nil {
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
}
|
||||
if root.Enabled {
|
||||
queueLibraryRootScan(svc, c.Param("id"), root.ID)
|
||||
}
|
||||
c.JSON(http.StatusCreated, root)
|
||||
}
|
||||
}
|
||||
@@ -53,7 +58,12 @@ func updateLibraryRootHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "library root not found"})
|
||||
return
|
||||
}
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
if svc.Watcher != nil {
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
}
|
||||
if root.Enabled {
|
||||
queueLibraryRootScan(svc, c.Param("id"), root.ID)
|
||||
}
|
||||
c.JSON(http.StatusOK, root)
|
||||
}
|
||||
}
|
||||
@@ -64,7 +74,9 @@ func deleteLibraryRootHandler(svc *service.Container) gin.HandlerFunc {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
if svc.Watcher != nil {
|
||||
go func() { _ = svc.Watcher.Refresh(context.Background()) }()
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,33 @@ func scanLibraryRootHandler(svc *service.Container) gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// queueLibraryRootScan starts the same background scan used by the manual
|
||||
// scan endpoints. Library creation/root addition used to refresh only the
|
||||
// watcher, which left a newly-created library empty until the user clicked
|
||||
// "扫描" manually. Keeping this helper in the scan handler makes all mutation
|
||||
// paths use one scan lifecycle and preserves the local-scan de-duplication.
|
||||
func queueLibraryRootScan(svc *service.Container, libraryID, rootID string) {
|
||||
if svc == nil || svc.Scan == nil || strings.TrimSpace(libraryID) == "" {
|
||||
return
|
||||
}
|
||||
key := libraryID
|
||||
if strings.TrimSpace(rootID) != "" {
|
||||
key += ":" + rootID
|
||||
}
|
||||
finish, ok := svc.Scan.TryBeginLocalScan(key)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
defer finish()
|
||||
if strings.TrimSpace(rootID) == "" {
|
||||
_, _ = svc.Scan.ScanLibrary(context.Background(), libraryID)
|
||||
return
|
||||
}
|
||||
_, _ = svc.Scan.ScanLibraryRoot(context.Background(), libraryID, rootID)
|
||||
}()
|
||||
}
|
||||
|
||||
func startScanHTTPTask(svc *service.Container, name, libraryName, path string) *service.TaskHandle {
|
||||
if svc == nil || svc.Tasks == nil {
|
||||
return nil
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -23,6 +25,47 @@ import (
|
||||
"github.com/ShukeBta/MediaStationGo/internal/service"
|
||||
)
|
||||
|
||||
func TestQueueLibraryRootScanImportsNewLibraryMedia(t *testing.T) {
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.AutoMigrate(&model.Library{}, &model.LibraryRoot{}, &model.Media{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rootPath := t.TempDir()
|
||||
mediaPath := filepath.Join(rootPath, "Example Movie (2026).mkv")
|
||||
if err := os.WriteFile(mediaPath, []byte("movie"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repos := repository.New(db)
|
||||
lib := model.Library{Name: "电影", Path: rootPath, Type: "movie", Enabled: true}
|
||||
root := model.LibraryRoot{Name: "电影", Path: rootPath, Enabled: true}
|
||||
if err := repos.Library.CreateWithRoots(t.Context(), &lib, []model.LibraryRoot{root}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
libWithRoots, err := repos.Library.FindByID(t.Context(), lib.ID)
|
||||
if err != nil || libWithRoots == nil || len(libWithRoots.Roots) != 1 {
|
||||
t.Fatalf("library roots=%#v err=%v", libWithRoots, err)
|
||||
}
|
||||
scanner := service.NewScannerService(&config.Config{}, zap.NewNop(), repos, service.NewHub(zap.NewNop()), nil, nil)
|
||||
svc := &service.Container{Repo: repos, Scan: scanner}
|
||||
queueLibraryRootScan(svc, lib.ID, libWithRoots.Roots[0].ID)
|
||||
|
||||
deadline := time.Now().Add(3 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
var count int64
|
||||
if err := repos.DB.Model(&model.Media{}).Where("library_id = ?", lib.ID).Count(&count).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if count == 1 {
|
||||
return
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
t.Fatal("initial library scan did not import the media file")
|
||||
}
|
||||
|
||||
func TestListLibrariesHidesAdultDirectoriesUnlessAdminRequestsAll(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
|
||||
Reference in New Issue
Block a user