diff --git a/internal/transfer/rsync.go b/cmd/mediawatcher/main.go similarity index 100% rename from internal/transfer/rsync.go rename to cmd/mediawatcher/main.go diff --git a/internal/classifier/classifier.go b/internal/classifier/classifier.go new file mode 100644 index 0000000..0b09c57 --- /dev/null +++ b/internal/classifier/classifier.go @@ -0,0 +1,27 @@ +package classifier + +import ( + "path/filepath" + "regexp" + "strings" +) + +var movieYear = regexp.MustCompile(`(?i)(19|20)\d{2}`) + +func classifyMovie(filename string) (bool, string, int) { + base := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)) + + // Find year + yearMatch := movieYear.FindString(base) + if yearMatch == "" { + return false, "", 0 + } + + year := parseInt(yearMatch) + + // Extract title (everything before year) + title := strings.TrimSpace(strings.Replace(base, yearMatch, "", 1)) + title = cleanupTitle(title) + + return true, title, year +} diff --git a/internal/classifier/misc.go b/internal/classifier/misc.go new file mode 100644 index 0000000..e69de29 diff --git a/internal/classifier/movie.go b/internal/classifier/movie.go new file mode 100644 index 0000000..f57da08 --- /dev/null +++ b/internal/classifier/movie.go @@ -0,0 +1,28 @@ +package classifier + + +import ( + "path/filepath" + "regexp" + "strings" +) + +var movieYear = regexp.MustCompile(`(?i)(19|20)\d{2}`) + +func classifyMovie(filename string) (bool, string, int) { + base := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)) + + // Find year + yearMatch := movieYear.FindString(base) + if yearMatch == "" { + return false, "", 0 + } + + year := parseInt(yearMatch) + + // Extract title (everything before year) + title := strings.TrimSpace(strings.Replace(base, yearMatch, "", 1)) + title = cleanupTitle(title) + + return true, title, year +} diff --git a/internal/classifier/tv.go b/internal/classifier/tv.go new file mode 100644 index 0000000..754a286 --- /dev/null +++ b/internal/classifier/tv.go @@ -0,0 +1,21 @@ +package classifier + +var tvPattern = regexp.MustCompile(`(?i)(S?)(\d{1,2})(E|x)(\d{1,2})`) + +func classifyTV(filename string) (bool, string, int, int) { + base := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)) + + match := tvPattern.FindStringSubmatch(base) + if len(match) < 5 { + return false, "", 0, 0 + } + + season := parseInt(match[2]) + episode := parseInt(match[4]) + + // Remove S01E02 or similar from the string + cleaned := tvPattern.ReplaceAllString(base, "") + title := cleanupTitle(cleaned) + + return true, title, season, episode +} diff --git a/internal/config/config.go b/internal/config/config.go index e69de29..d0eb57f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -0,0 +1,37 @@ +package config + +import ( + "os" + "gopkg.in/yaml.v3" +) + +type StructureConfig struct { + MoviesDir string `yaml:"movies_dir"` + TVDir string `yaml:"tv_dir"` + MiscDir string `yaml:"misc_dir"` + UnknownDir string `yaml:"unknown_dir"` + AutoCreate bool `yaml:"auto_create"` +} + +type WatchConfig struct { + Dirs []string `yaml:"dirs"` +} + +type Config struct { + Watch WatchConfig `yaml:"watch"` + Structure StructureConfig `yaml:"structure"` +} + +func LoadConfig(path string) (*Config, error) { + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + + var cfg Config + if err := yaml.Unmarshal(data, &cfg); err != nil { + return nil, err + } + + return &cfg, nil +} diff --git a/internal/mover/mover.go b/internal/mover/mover.go new file mode 100644 index 0000000..e69de29 diff --git a/internal/util/file.go b/internal/util/file.go new file mode 100644 index 0000000..e69de29 diff --git a/internal/watcher/watcher.go b/internal/watcher/watcher.go index e69de29..280da3c 100644 --- a/internal/watcher/watcher.go +++ b/internal/watcher/watcher.go @@ -0,0 +1,34 @@ +package watcher + +import ( + "log" + "github.com/fsnotify/fsnotify" +) + +type FileHandler func(path string) + +func WatchDirs(dirs []string, handler FileHandler) error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return err + } + defer watcher.Close() + + for _, d := range dirs { + if err := watcher.Add(d); err != nil { + return err + } + } + + for { + select { + case event := <-watcher.Events: + if event.Op&(fsnotify.Create|fsnotify.Write) != 0 { + handler(event.Name) + } + + case err := <-watcher.Errors: + log.Printf("Watcher error: %v\n", err) + } + } +}