Files
mediawatcher/internal/classifier/classifier.go
2025-12-08 07:56:46 -05:00

113 lines
2.7 KiB
Go

package classifier
import (
"fmt"
"path/filepath"
"regexp"
"strconv"
"strings"
"mediawatcher/internal/config"
)
type Type string
const (
TypeTV Type = "tv"
TypeMovie Type = "movie"
TypeMisc Type = "misc"
TypeUnknown Type = "unknown"
)
type Result struct {
Type Type
Title string
Season int
Episode int
Year int
DestDir string
DestName string
SourcePath string
}
// regexes
var (
tvPattern = regexp.MustCompile(`(?i)(S?(\d{1,2}))[ ._-]*[Ex](\d{1,3})`)
yearPattern = regexp.MustCompile(`\b(19\d{2}|20[0-3]\d)\b`)
)
func Classify(path string, cfg *config.Config) Result {
base := filepath.Base(path)
nameNoExt := strings.TrimSuffix(base, filepath.Ext(base))
// TV first
if m := tvPattern.FindStringSubmatch(nameNoExt); len(m) == 4 {
season := atoiSafe(m[2])
episode := atoiSafe(m[3])
cleaned := tvPattern.ReplaceAllString(nameNoExt, "")
title := cleanupTitle(cleaned)
destDir := filepath.Join(cfg.Structure.TVDir, title, fmt.Sprintf("Season %02d", season))
return Result{
Type: TypeTV,
Title: title,
Season: season,
Episode: episode,
DestDir: destDir,
DestName: base,
SourcePath: path,
}
}
// Movie
if ym := yearPattern.FindString(nameNoExt); ym != "" {
year := atoiSafe(ym)
cleaned := strings.Replace(nameNoExt, ym, "", 1)
title := cleanupTitle(cleaned)
destDir := filepath.Join(cfg.Structure.MoviesDir, fmt.Sprintf("%s (%d)", title, year))
return Result{
Type: TypeMovie,
Title: title,
Year: year,
DestDir: destDir,
DestName: base,
SourcePath: path,
}
}
// Misc based on extension
ext := strings.ToLower(filepath.Ext(base))
if ext == ".mp3" || ext == ".flac" || ext == ".m4a" || ext == ".aac" {
return Result{
Type: TypeMisc,
DestDir: cfg.Structure.MiscDir,
DestName: base,
SourcePath: path,
}
}
// Unknown
return Result{
Type: TypeUnknown,
DestDir: cfg.Structure.UnknownDir,
DestName: base,
SourcePath: path,
}
}
func cleanupTitle(s string) string {
s = strings.ReplaceAll(s, ".", " ")
s = strings.ReplaceAll(s, "_", " ")
s = strings.ReplaceAll(s, "-", " ")
s = strings.Join(strings.Fields(s), " ")
return strings.TrimSpace(s)
}
func atoiSafe(s string) int {
n, _ := strconv.Atoi(s)
return n
}