fix: stabilize library paths and subscription matching

This commit is contained in:
ShukeBta
2026-06-28 12:15:10 +08:00
parent e4abb03ca5
commit a97dc35790
102 changed files with 4582 additions and 1519 deletions
+36 -10
View File
@@ -15,6 +15,7 @@ type cloudScanImportRequest struct {
existingMedia map[string]existingCloudMedia
writeBatch *localMediaWriteBatch
probeBudget *int
defaultRootID string
progress *cloudScanProgressState
result *ScanResult
}
@@ -34,6 +35,14 @@ type cloudLibraryScanCompletion struct {
}
func (s *ScannerService) scanCloudLibrary(ctx context.Context, lib *model.Library, mount CloudMountInfo, autoScrape bool) (*ScanResult, error) {
return s.scanCloudLibraryWithRoot(ctx, lib, mount, "", autoScrape)
}
func (s *ScannerService) scanCloudLibraryRoot(ctx context.Context, lib *model.Library, root *model.LibraryRoot, mount CloudMountInfo, autoScrape bool) (*ScanResult, error) {
return s.scanCloudLibraryWithRoot(ctx, lib, mount, libraryRootID(root), autoScrape)
}
func (s *ScannerService) scanCloudLibraryWithRoot(ctx context.Context, lib *model.Library, mount CloudMountInfo, defaultRootID string, autoScrape bool) (*ScanResult, error) {
res := &ScanResult{LibraryID: lib.ID}
if s.storage == nil {
return res, fmt.Errorf("cloud storage service unavailable")
@@ -78,6 +87,7 @@ func (s *ScannerService) scanCloudLibrary(ctx context.Context, lib *model.Librar
existingMedia: existingMedia,
writeBatch: writeBatch,
probeBudget: &probeBudget,
defaultRootID: defaultRootID,
progress: progress,
result: res,
})
@@ -86,7 +96,12 @@ func (s *ScannerService) scanCloudLibrary(ctx context.Context, lib *model.Librar
}
scopeIDs = appendUniqueLibraryIDs(scopeIDs, imported.scopeLibraryIDs...)
writeBatch.Flush()
removed, err := s.pruneMissingCloudMediaForLibraries(ctx, scopeIDs, imported.seen)
var removed int64
if defaultRootID != "" {
removed, err = s.pruneMissingCloudMediaForRoot(ctx, lib.ID, defaultRootID, imported.seen)
} else {
removed, err = s.pruneMissingCloudMediaForLibraries(ctx, scopeIDs, imported.seen)
}
if err != nil {
s.log.Warn("prune missing cloud media failed", zap.String("library_id", lib.ID), zap.Error(err))
} else {
@@ -102,38 +117,49 @@ func (s *ScannerService) scanCloudLibrary(ctx context.Context, lib *model.Librar
return res, nil
}
type cloudScanTarget struct {
lib *model.Library
rootID string
}
func (s *ScannerService) importCloudScanCandidates(ctx context.Context, rootLib *model.Library, req cloudScanImportRequest) (cloudScanImportResult, error) {
imported := cloudScanImportResult{
seen: make(map[string]struct{}),
touchedLibraryIDs: []string{},
scopeLibraryIDs: []string{},
}
targetLibs := map[string]*model.Library{"": rootLib}
targetLibs := map[string]cloudScanTarget{"": {lib: rootLib, rootID: req.defaultRootID}}
for _, candidate := range req.candidates {
select {
case <-ctx.Done():
return imported, ctx.Err()
default:
}
targetLib := rootLib
target := targetLibs[""]
if candidate.categoryDisplayDir != "" {
if cached, ok := targetLibs[candidate.categoryDisplayDir]; ok {
targetLib = cached
} else if categoryLib, err := s.ensureCloudAutoCategoryLibrary(ctx, rootLib, req.provider, candidate.categoryDisplayDir); err == nil && categoryLib != nil {
targetLib = categoryLib
targetLibs[candidate.categoryDisplayDir] = categoryLib
imported.scopeLibraryIDs = appendUniqueLibraryIDs(imported.scopeLibraryIDs, categoryLib.ID)
categoryKey := candidate.categoryDisplayDir + "\x00" + candidate.categoryScanDir
if cached, ok := targetLibs[categoryKey]; ok {
target = cached
} else if categoryTarget, err := s.ensureCloudAutoCategoryTarget(ctx, rootLib, req.provider, candidate.categoryDisplayDir, candidate.categoryScanDir); err == nil && categoryTarget.Library != nil {
target = cloudScanTarget{lib: categoryTarget.Library, rootID: categoryTarget.RootID}
targetLibs[categoryKey] = target
imported.scopeLibraryIDs = appendUniqueLibraryIDs(imported.scopeLibraryIDs, categoryTarget.Library.ID)
} else if err != nil {
s.log.Warn("ensure cloud auto category library failed",
zap.String("library_id", rootLib.ID),
zap.String("provider", req.provider),
zap.String("category", candidate.categoryDisplayDir),
zap.String("scan_dir", candidate.categoryScanDir),
zap.Error(err))
}
}
targetLib := target.lib
if targetLib == nil {
targetLib = rootLib
}
imported.touchedLibraryIDs = appendUniqueLibraryIDs(imported.touchedLibraryIDs, targetLib.ID)
imported.seen[candidate.path] = struct{}{}
s.ingestCloudFile(ctx, targetLib, req.provider, candidate.ref, candidate.path, candidate.name, candidate.size, candidate.localMeta, req.existingMedia, req.writeBatch, req.probeBudget, req.result)
s.ingestCloudFile(ctx, targetLib, target.rootID, req.provider, candidate.ref, candidate.path, candidate.name, candidate.size, candidate.localMeta, req.existingMedia, req.writeBatch, req.probeBudget, req.result)
req.progress.publish(s, rootLib.ID, req.result, "importing", req.result.Visited == 1 || req.result.Visited%100 == 0)
}
return imported, nil