Files
MeBox/internal/service/organizer_target.go
T
2026-06-27 10:49:36 +08:00

126 lines
3.1 KiB
Go

package service
import (
"context"
"fmt"
"path/filepath"
"strings"
)
type organizeTargetInput struct {
Root string
MediaType string
Category string
Title string
Source string
Ext string
Year int
Season int
Episode int
Series bool
}
type organizeTargetPath struct {
Dir string
Path string
EpisodeTag string
}
func (o *OrganizerService) buildOrganizeTargetPath(ctx context.Context, in organizeTargetInput) (organizeTargetPath, error) {
root := filepath.Clean(strings.TrimSpace(in.Root))
if root == "" || root == "." {
return organizeTargetPath{}, fmt.Errorf("organize target root required")
}
title := sanitizeFilename(strings.TrimSpace(in.Title))
if title == "" {
title = "Unknown"
}
ext := strings.TrimSpace(in.Ext)
if ext != "" && !strings.HasPrefix(ext, ".") {
ext = "." + ext
}
episodeTag := ""
if in.Series {
season := in.Season
if season < 0 {
season = 1
}
episode := in.Episode
if episode <= 0 {
episode = 1
}
episodeTag = fmt.Sprintf("S%02dE%02d", season, episode)
}
template := strings.TrimSpace(o.organizeNamingFormat(ctx, in.MediaType, in.Series))
var rel string
if template == "" {
rel = defaultOrganizeRelativePath(title, ext, in.Year, in.Season, in.Episode, in.Series)
} else {
rel = renderOrganizeNamingTemplate(template, organizeNamingData{
Title: title,
Year: in.Year,
Season: in.Season,
Episode: in.Episode,
Ext: strings.TrimPrefix(ext, "."),
FileExt: ext,
Category: sanitizeFilename(in.Category),
MediaType: normalizeOrganizeMediaType(in.MediaType),
EpisodeTag: episodeTag,
VideoFormat: extractOrganizeReleaseTag(in.Source),
Part: extractOrganizeReleaseTag(in.Source),
})
rel = cleanOrganizeRelativePath(rel)
if rel == "" {
rel = defaultOrganizeRelativePath(title, ext, in.Year, in.Season, in.Episode, in.Series)
}
if ext != "" && !strings.EqualFold(filepath.Ext(rel), ext) {
rel += ext
}
}
dst := filepath.Join(root, rel)
return organizeTargetPath{
Dir: filepath.Dir(dst),
Path: dst,
EpisodeTag: episodeTag,
}, nil
}
func (o *OrganizerService) organizeNamingFormat(ctx context.Context, mediaType string, series bool) string {
if o == nil || o.repo == nil || o.repo.Setting == nil {
return ""
}
key := "organize.movie_format"
if series {
if normalizeOrganizeMediaType(mediaType) == "anime" {
key = "organize.anime_format"
} else {
key = "organize.tv_format"
}
}
value, err := o.repo.Setting.Get(ctx, key)
if err != nil {
return ""
}
return strings.TrimSpace(value)
}
func defaultOrganizeRelativePath(title, ext string, year, season, episode int, series bool) string {
if series {
if season < 0 {
season = 1
}
if episode <= 0 {
episode = 1
}
episodeTag := fmt.Sprintf("S%02dE%02d", season, episode)
return filepath.Join(title, fmt.Sprintf("Season %02d", season), fmt.Sprintf("%s - %s%s", title, episodeTag, ext))
}
folder := title
if year > 0 {
folder = fmt.Sprintf("%s (%d)", title, year)
}
return filepath.Join(folder, folder+ext)
}