Files
MeBox/internal/handler/media_scan.go
T
truewhile 44ca451cd4 优化
优化
2026-08-24 17:40:43 +08:00

168 lines
5.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"context"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/ShukeBta/MMTL/internal/service"
)
func scanLibraryHandler(svc *service.Container) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
lib, err := svc.Repo.Library.FindByID(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if lib == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "library not found"})
return
}
if _, ok := service.ParseCloudLibraryMount(lib.Path); ok {
// 云盘扫描已随网盘后端移除;此处保留空分支以兼容既有客户端。
c.JSON(http.StatusBadRequest, gin.H{"error": "cloud 网盘后端已移除,无法扫描云盘媒体库"})
return
}
finishScan, ok := svc.Scan.TryBeginLocalScan(id)
if !ok {
c.JSON(http.StatusAccepted, gin.H{
"library_id": id,
"queued": true,
"already_running": true,
"message": "该媒体库正在后台扫描,请在任务面板查看进度",
"estimate_message": "页面关闭不会中断扫描",
})
return
}
task := startScanHTTPTask(svc, "手动扫描入库", lib.Name, lib.Path)
go func(libraryID string, task *service.TaskHandle, finish func()) {
defer finish()
res, err := svc.Scan.ScanLibrary(context.Background(), libraryID)
if err != nil {
finishHTTPTask(task, err, "scan", "手动扫描入库失败", scanTaskMetrics(res), scanTaskDetails(res, 20))
return
}
finishHTTPTask(task, nil, "completed", "手动扫描入库结束", scanTaskMetrics(res), scanTaskDetails(res, 20))
}(id, task, finishScan)
c.JSON(http.StatusAccepted, gin.H{
"library_id": id,
"queued": true,
"message": "本地媒体库扫描已在后台运行,页面关闭不会中断",
"estimate_message": "可在右上角任务面板查看扫描进度",
})
}
}
func scanLibraryRootHandler(svc *service.Container) gin.HandlerFunc {
return func(c *gin.Context) {
id := c.Param("id")
rootID := c.Param("root_id")
finishScan, ok := svc.Scan.TryBeginLocalScan(id + ":" + rootID)
if !ok {
c.JSON(http.StatusAccepted, gin.H{
"library_id": id,
"queued": true,
"already_running": true,
"message": "该路径正在后台扫描,请在任务面板查看进度",
"estimate_message": "页面关闭不会中断扫描",
})
return
}
task := startScanHTTPTask(svc, "手动扫描媒体库路径", id, rootID)
go func(libraryID, libraryRootID string, task *service.TaskHandle, finish func()) {
defer finish()
res, err := svc.Scan.ScanLibraryRoot(context.Background(), libraryID, libraryRootID)
if err != nil {
finishHTTPTask(task, err, "scan", "手动扫描路径失败", scanTaskMetrics(res), scanTaskDetails(res, 20))
return
}
finishHTTPTask(task, nil, "completed", "手动扫描路径结束", scanTaskMetrics(res), scanTaskDetails(res, 20))
}(id, rootID, task, finishScan)
c.JSON(http.StatusAccepted, gin.H{
"library_id": id,
"queued": true,
"message": "媒体库路径扫描已在后台运行,页面关闭不会中断",
"estimate_message": "可在右上角任务面板查看扫描进度",
})
}
}
// 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
}
if libraryName != "" {
name += ":" + libraryName
}
return svc.Tasks.Start(service.TaskKindScan, name, service.TaskUpdate{
Stage: "scan",
SourcePath: path,
Message: "正在扫描并入库",
})
}
func scanTaskMetrics(res *service.ScanResult) map[string]int64 {
if res == nil {
return nil
}
return map[string]int64{
"visited": int64(res.Visited),
"added": int64(res.Added),
"updated": int64(res.Updated),
"skipped": int64(res.Skipped),
"probed": int64(res.Probed),
"local_metadata": int64(res.LocalMetadata),
"removed": res.Removed,
"errors": int64(res.ErrorCount),
}
}
func scanTaskDetails(res *service.ScanResult, limit int) []string {
if res == nil || limit <= 0 {
return nil
}
out := make([]string, 0, limit)
for _, line := range res.Errors {
line = strings.TrimSpace(line)
if line == "" {
continue
}
out = append(out, "错误: "+line)
if len(out) >= limit {
return out
}
}
return out
}