mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 11:16:37 +08:00
495292ab3b
bug处理优化
138 lines
5.5 KiB
Go
138 lines
5.5 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
func skipAlreadyOrganizedSource(req organizeSourceFileRequest, plan organizeSourceFilePlan) {
|
|
req.Result.Skipped++
|
|
req.Result.Items = append(req.Result.Items, OrganizePreviewItem{
|
|
Source: req.Source, Target: plan.Target.Path, Action: "skip", Reason: organizeSkipAlreadyOrganized,
|
|
MediaType: plan.Layout.MediaType, Category: plan.Layout.Category, Title: plan.Identity.Title,
|
|
})
|
|
}
|
|
|
|
func (o *OrganizerService) collectExistingSourceVersions(ctx context.Context, req organizeSourceFileRequest, plan organizeSourceFilePlan) organizeExistingSourceVersions {
|
|
externalExisting := o.existingByExternalIdentity(ctx, req.DestRoot, plan.MetadataMatch, plan.Identity.Season, plan.Identity.Episode)
|
|
identityExisting := o.existingByIdentity(ctx, req.DestRoot, plan.Identity.ParsedTitle, plan.Identity.Year, plan.Identity.Season, plan.Identity.Episode)
|
|
folderExisting := o.existingByFolder(plan.Target.Dir, plan.Target.EpisodeTag)
|
|
return organizeExistingSourceVersions{
|
|
External: externalExisting,
|
|
Identity: identityExisting,
|
|
Folder: folderExisting,
|
|
All: mergeExistingVersionPaths(externalExisting, identityExisting, folderExisting),
|
|
}
|
|
}
|
|
|
|
func (o *OrganizerService) handleExistingSourceVersions(ctx context.Context, req organizeSourceFileRequest, plan organizeSourceFilePlan, existing organizeExistingSourceVersions) error {
|
|
reclassified, err := o.reclassifyExistingMedia(ctx, organizeExistingReclassifyRequest{
|
|
Source: req.Source,
|
|
Target: plan.Target.Path,
|
|
DestRoot: req.DestRoot,
|
|
TargetLibraryID: plan.TargetLibraryID,
|
|
Existing: existing.All,
|
|
DryRun: req.DryRun,
|
|
MediaType: plan.Layout.MediaType,
|
|
Category: plan.Layout.Category,
|
|
Title: plan.Identity.Title,
|
|
Year: plan.Identity.Year,
|
|
Season: plan.Identity.Season,
|
|
Episode: plan.Identity.Episode,
|
|
MetadataMatch: plan.MetadataMatch,
|
|
Result: req.Result,
|
|
})
|
|
if err != nil || reclassified {
|
|
return err
|
|
}
|
|
if replaced, err := o.replaceSourceWithBetterVersion(ctx, req, plan, existing.All); replaced || err != nil {
|
|
return err
|
|
}
|
|
o.skipExistingSourceDuplicate(ctx, req, plan, existing)
|
|
return nil
|
|
}
|
|
|
|
func (o *OrganizerService) replaceSourceWithBetterVersion(ctx context.Context, req organizeSourceFileRequest, plan organizeSourceFilePlan, existing []string) (bool, error) {
|
|
srcArea := o.resolutionArea(ctx, req.Source)
|
|
bestArea := 0
|
|
for _, e := range existing {
|
|
if a := o.resolutionArea(ctx, e); a > bestArea {
|
|
bestArea = a
|
|
}
|
|
}
|
|
if !req.AllowReplaceExisting || srcArea <= 0 || bestArea <= 0 || srcArea <= bestArea {
|
|
return false, nil
|
|
}
|
|
req.Result.Items = append(req.Result.Items, OrganizePreviewItem{
|
|
Source: req.Source, Target: plan.Target.Path, Action: "replace", Reason: "higher resolution",
|
|
MediaType: plan.Layout.MediaType, Category: plan.Layout.Category, Title: plan.Identity.Title,
|
|
})
|
|
if req.DryRun {
|
|
req.Result.Replaced++
|
|
return true, nil
|
|
}
|
|
if err := o.replaceVersions(ctx, req.Source, existing, plan.Target.Path, req.Mode); err != nil {
|
|
return true, err
|
|
}
|
|
o.log.Info("organize replaced lower-resolution media",
|
|
zap.String("from", req.Source),
|
|
zap.String("to", plan.Target.Path),
|
|
zap.Int("src_area", srcArea),
|
|
zap.Int("existing_area", bestArea),
|
|
)
|
|
req.Result.Replaced++
|
|
return true, nil
|
|
}
|
|
|
|
func (o *OrganizerService) skipExistingSourceDuplicate(ctx context.Context, req organizeSourceFileRequest, plan organizeSourceFilePlan, existing organizeExistingSourceVersions) {
|
|
reason := organizeSkipTargetExists
|
|
if len(existing.External) > 0 || len(existing.Identity) > 0 || o.allExistingPathsInDB(ctx, existing.All) {
|
|
reason = organizeSkipDuplicateLibrary
|
|
}
|
|
o.log.Debug("organize skip duplicate",
|
|
zap.String("src", req.Source), zap.String("dest_dir", plan.Target.Dir), zap.String("reason", reason))
|
|
req.Result.Skipped++
|
|
req.Result.Items = append(req.Result.Items, OrganizePreviewItem{
|
|
Source: req.Source, Target: plan.Target.Path, Action: "skip", Reason: reason,
|
|
MediaType: plan.Layout.MediaType, Category: plan.Layout.Category, Title: plan.Identity.Title,
|
|
})
|
|
}
|
|
|
|
func (o *OrganizerService) writeOrganizedSourceFile(ctx context.Context, req organizeSourceFileRequest, plan organizeSourceFilePlan) error {
|
|
req.Result.Items = append(req.Result.Items, OrganizePreviewItem{
|
|
Source: req.Source, Target: plan.Target.Path, Action: "organize",
|
|
MediaType: plan.Layout.MediaType, Category: plan.Layout.Category, Title: plan.Identity.Title,
|
|
})
|
|
if req.DryRun {
|
|
req.Result.Organized++
|
|
return nil
|
|
}
|
|
if err := os.MkdirAll(plan.Target.Dir, 0o755); err != nil { // #nosec G301 -- organized media directories must remain readable by NAS/player users.
|
|
return err
|
|
}
|
|
if _, err := os.Stat(plan.Target.Path); err == nil {
|
|
req.Result.Skipped++
|
|
if len(req.Result.Items) > 0 {
|
|
req.Result.Items[len(req.Result.Items)-1].Action = "skip"
|
|
req.Result.Items[len(req.Result.Items)-1].Reason = organizeSkipTargetExists
|
|
}
|
|
return nil
|
|
}
|
|
if err := transferFile(req.Source, plan.Target.Path, req.Mode); err != nil {
|
|
return err
|
|
}
|
|
if err := transferSidecarNFO(req.Source, plan.Target.Path, req.Mode); err != nil {
|
|
o.log.Warn("organize sidecar nfo failed",
|
|
zap.String("from", req.Source), zap.String("to", plan.Target.Path), zap.Error(err))
|
|
}
|
|
if err := transferSidecarArtwork(req.Source, plan.Target.Path, req.Mode); err != nil {
|
|
o.log.Warn("organize sidecar artwork failed",
|
|
zap.String("from", req.Source), zap.String("to", plan.Target.Path), zap.Error(err))
|
|
}
|
|
o.persistOrganizedSourceMetadata(ctx, plan)
|
|
req.Result.Organized++
|
|
return nil
|
|
}
|