mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-06 13:26:38 +08:00
refactor: split modules and harden scraping workflows
This commit is contained in:
@@ -0,0 +1,180 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"go.uber.org/zap"
|
||||
|
||||
"github.com/ShukeBta/MediaStationGo/internal/model"
|
||||
)
|
||||
|
||||
const cloudAutoCategoryQueryKey = "auto_category"
|
||||
|
||||
func BuildCloudAutoCategoryLibraryPath(provider, displayDir string) string {
|
||||
base := BuildCloudLibraryPath(provider, "", displayDir)
|
||||
if base == "" || strings.TrimSpace(displayDir) == "" {
|
||||
return ""
|
||||
}
|
||||
sep := "?"
|
||||
if strings.Contains(base, "?") {
|
||||
sep = "&"
|
||||
}
|
||||
return base + sep + cloudAutoCategoryQueryKey + "=1"
|
||||
}
|
||||
|
||||
func CloudLibraryAutoCategory(lib model.Library) bool {
|
||||
u, err := url.Parse(strings.TrimSpace(lib.Path))
|
||||
if err != nil || strings.ToLower(u.Scheme) != "cloud" {
|
||||
return false
|
||||
}
|
||||
switch strings.ToLower(strings.TrimSpace(u.Query().Get(cloudAutoCategoryQueryKey))) {
|
||||
case "1", "true", "yes", "on":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func cloudRootMountNeedsAutoCategory(mount CloudMountInfo) bool {
|
||||
return strings.TrimSpace(mount.DisplayDir) == "" && strings.TrimSpace(mount.ScanDir) == ""
|
||||
}
|
||||
|
||||
func cloudAutoCategoryDisplayDirForMediaPath(path string) string {
|
||||
info, ok := ParseCloudLibraryMount(path)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
parts := strmSlashParts(info.DisplayDir)
|
||||
if len(parts) <= 1 {
|
||||
return ""
|
||||
}
|
||||
parts = parts[:len(parts)-1]
|
||||
categoryParts := cloudAutoCategoryParts(parts)
|
||||
if len(categoryParts) == 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(categoryParts, "/")
|
||||
}
|
||||
|
||||
func cloudAutoCategoryParts(parts []string) []string {
|
||||
for i, part := range parts {
|
||||
root := strmCanonicalRoot(part)
|
||||
if root != "" {
|
||||
if i+1 >= len(parts) {
|
||||
return nil
|
||||
}
|
||||
category := strings.TrimSpace(parts[i+1])
|
||||
if cloudAutoCategoryRootMatches(root, category) {
|
||||
return []string{root, category}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if root := strmCategoryRoot(part); root != "" {
|
||||
return []string{root, strings.TrimSpace(part)}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cloudAutoCategoryRootMatches(root, category string) bool {
|
||||
category = strings.TrimSpace(category)
|
||||
if category == "" {
|
||||
return false
|
||||
}
|
||||
if strmCategoryRoot(category) == root {
|
||||
return true
|
||||
}
|
||||
if root == "电影" {
|
||||
return containsAnyText(strings.ToLower(category), "纪录片", "纪录", "documentary")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *ScannerService) ensureCloudAutoCategoryLibrary(ctx context.Context, rootLib *model.Library, provider, displayDir string) (*model.Library, error) {
|
||||
displayDir = normalizeCloudMountDir(provider, displayDir)
|
||||
if s == nil || s.repo == nil || s.repo.DB == nil || rootLib == nil || provider == "" || displayDir == "" {
|
||||
return rootLib, nil
|
||||
}
|
||||
if existing := s.findCloudLibraryByDisplayDir(ctx, provider, displayDir); existing != nil {
|
||||
return existing, nil
|
||||
}
|
||||
path := BuildCloudAutoCategoryLibraryPath(provider, displayDir)
|
||||
if path == "" {
|
||||
return rootLib, nil
|
||||
}
|
||||
name := cloudMountDirBase(displayDir)
|
||||
if name == "" {
|
||||
name = displayDir
|
||||
}
|
||||
lib := &model.Library{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Type: InferCloudMountMediaType(displayDir, name),
|
||||
Enabled: true,
|
||||
}
|
||||
if err := s.repo.Library.Create(ctx, lib); err != nil {
|
||||
if existing := s.findCloudLibraryByDisplayDir(ctx, provider, displayDir); existing != nil {
|
||||
return existing, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if s.log != nil {
|
||||
s.log.Info("created cloud auto category library",
|
||||
zap.String("root_library_id", rootLib.ID),
|
||||
zap.String("library_id", lib.ID),
|
||||
zap.String("provider", provider),
|
||||
zap.String("display_dir", displayDir))
|
||||
}
|
||||
return lib, nil
|
||||
}
|
||||
|
||||
func (s *ScannerService) findCloudLibraryByDisplayDir(ctx context.Context, provider, displayDir string) *model.Library {
|
||||
if s == nil || s.repo == nil || s.repo.Library == nil {
|
||||
return nil
|
||||
}
|
||||
libs, err := s.repo.Library.List(ctx)
|
||||
if err != nil {
|
||||
if s.log != nil {
|
||||
s.log.Warn("list libraries for cloud auto category failed", zap.Error(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
displayDir = normalizeCloudMountDir(provider, displayDir)
|
||||
for _, lib := range libs {
|
||||
info, ok := ParseCloudLibraryMount(lib.Path)
|
||||
if !ok || info.Provider != provider || normalizeCloudMountDir(provider, info.DisplayDir) != displayDir {
|
||||
continue
|
||||
}
|
||||
return &lib
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ScannerService) cloudScanLibraryScopeIDs(ctx context.Context, lib *model.Library, mount CloudMountInfo) []string {
|
||||
if lib == nil {
|
||||
return nil
|
||||
}
|
||||
ids := []string{lib.ID}
|
||||
if !cloudRootMountNeedsAutoCategory(mount) || s == nil || s.repo == nil || s.repo.Library == nil {
|
||||
return ids
|
||||
}
|
||||
libs, err := s.repo.Library.List(ctx)
|
||||
if err != nil {
|
||||
if s.log != nil {
|
||||
s.log.Warn("list libraries for cloud scan scope failed", zap.String("library_id", lib.ID), zap.Error(err))
|
||||
}
|
||||
return ids
|
||||
}
|
||||
for _, candidate := range libs {
|
||||
if candidate.ID == lib.ID || !CloudLibraryAutoCategory(candidate) {
|
||||
continue
|
||||
}
|
||||
info, ok := ParseCloudLibraryMount(candidate.Path)
|
||||
if ok && info.Provider == mount.Provider {
|
||||
ids = appendUniqueLibraryIDs(ids, candidate.ID)
|
||||
}
|
||||
}
|
||||
return ids
|
||||
}
|
||||
Reference in New Issue
Block a user