diff --git a/main.go b/main.go index 3edca52..c4c7b5a 100644 --- a/main.go +++ b/main.go @@ -1,283 +1,32 @@ -package main +package envconf -import ("regexp" - "fmt" - "strconv" +import ("fmt" "strings" - "time" - "path/filepath" - "io/fs" - "os" - "github.com/fsnotify/fsnotify") + "os") -type SeasonInfo struct { - Name string - Season int - IsSeries bool - IncludeSeriesName bool -} +const TypeNone = (1 << 1) +const TypeInt = (1 << 2) +const TypeDuration = (1 << 3) +const TypeString = (1 << 4) type Config struct { - apikey string - ignore string // Ignore files with this file extension (case insensitive) - size int64 // Minimum file size for files to be moved - src string - dst string - req int64 // Number of requests per second to API + env map[string]map[string]int + } -var seriesMatcher []*regexp.Regexp -var moviesMatcher *regexp.Regexp - -func getEnvString(key string)(val string, ok bool) { - val, ok = os.LookupEnv(key) - if ok { - val = strings.TrimSuffix(val, "/") - } else { - fmt.Printf("Environment variable missing: %s\n", key) +func NewConfig()(*Config) { + config := new(Config) + config.env = make(map[string]string) + for _,v := range os.Environ() { + splitted := strings.SplitN(v, "=", 2) + if len(splitted) == 2 { + key := strings.ToLower(splitted[0]) + strval := splitted[1] + val := make(map[string]int) + val[strval] = TypeNone + env[key] = val + } } - return + fmt.Println(config) } -func getEnvBool(key string)(val bool, ok bool) { - var str string - var err error - str, ok = getEnvString(key) - if ok { - val, err = strconv.ParseBool(str) - if err != nil { - ok = false - fmt.Printf("Failed to parse environment variable as bool: %s\n", key) - } - } - return -} -func getEnvInt(key string)(val int64, ok bool) { - var str string - var err error - str, ok = getEnvString(key) - if ok { - upper := strings.ToUpper(str) - mod := int64(1) - if strings.HasSuffix(upper, "K") { - mod = 1024 - upper = strings.TrimSuffix(upper, "K") - } else if strings.HasSuffix(upper, "M") { - mod = 1024 * 1024 - upper = strings.TrimSuffix(upper, "M") - } else if strings.HasSuffix(upper, "G") { - mod = 1024 * 1024 * 1024 - upper = strings.TrimSuffix(upper, "G") - } - var tmp int64 - tmp, err = strconv.ParseInt(upper, 10, 64) - if err == nil { - val = tmp * mod - } else { - ok = false - fmt.Printf("Failed to parse environment variable as int: %s\n", key) - } - } - return -} - -func main() { - compileRegex() - - anyfail := false - var ok bool - var config Config - var info SeasonInfo - - config.apikey, ok = getEnvString("THEMOVIEDB_API_KEY") - anyfail = !ok || anyfail - config.src, ok = getEnvString("SOURCE_FOLDER") - anyfail = !ok || anyfail - config.dst, ok = getEnvString("DESTINATION_FOLDER") - anyfail = !ok || anyfail - config.ignore, ok = getEnvString("IGNORE_FILE_EXTENSION") - anyfail = !ok || anyfail - config.req, ok = getEnvInt("REQUESTS_PER_SECOND") - anyfail = !ok || anyfail - config.size, ok = getEnvInt("MINIMUM_FILE_SIZE") - anyfail = !ok || anyfail - info.IncludeSeriesName, ok = getEnvBool("INCLUDE_SERIES_IN_FILENAME") - anyfail = !ok || anyfail - - if anyfail { - return - } - setupRequest(time.Duration(int64(time.Second) / int64(config.req))) - watch(config, info) -} - -func watch(config Config, info SeasonInfo) { - watcher, err := fsnotify.NewWatcher() - if err != nil { - printerr(err) - return - } - defer watcher.Close() - - walkDirFunc := func(path string, dir fs.DirEntry, err error) (nerr error) { - if err != nil { - printerr(err) - return - } - if dir.IsDir() { - return watcher.Add(path) - } else { - go move(path, config, info) - } - return - } - filepath.WalkDir(config.src, walkDirFunc) - - for { - event, ok := <-watcher.Events - if ok { - if event.Op & fsnotify.Create == fsnotify.Create { - finfo, err := os.Stat(event.Name) - if err != nil { - printerr(err) - continue - } - if finfo.IsDir() { - watcher.Add(event.Name) - } else { - go move(event.Name, config, info) - } - } else if event.Op & fsnotify.Remove == fsnotify.Remove { - watcher.Remove(event.Name) - } - } - } -} - -func compileRegex() { - seriesMatcherString := [...]string { - "(?:\\[.*\\])?(.*)(?:[Ss])([0-9]{2})(?:[Ee])([0-9]{2})(?:.*)\\.(.+)", - "(?:\\[.*\\])?(.*)(?:[Ss])([0-9]{2})(?:[Ee])([0-9])(?:.*)\\.(.+)", - "(?:\\[.*\\])?(.*)(?:[Ss])([0-9])(?:[Ee])([0-9]{2})(?:.*)\\.(.+)", - "(?:\\[.*\\])?(.*)(?:[Ss])([0-9])(?:[Ee])([0-9])(?:.*)\\.(.+)", - "(?:\\[.*\\])?(.*)([0-9]{2})(?:[Xx])([0-9]{2})(?:.*)\\.(.+)", - "(?:\\[.*\\])?(.*)([0-9]{2})(?:[Xx])([0-9])(?:.*)\\.(.+)", - "(?:\\[.*\\])?(.*)([0-9])(?:[Xx])([0-9]{2})(?:.*)\\.(.+)", - "(?:\\[.*\\])?(.*)([0-9])(?:[Xx])([0-9])(?:.*)\\.(.+)", - } - - /* - SERIES S01E01 - SERIES S01E1 - SERIES S1E01 - SERIES S1E1 - SERIES 01X01 - SERIES 01X1 - SERIES 1X01 - SERIES 1X1 - */ - - seriesMatcher = make([]*regexp.Regexp, len(seriesMatcherString)) - - for k,v := range seriesMatcherString { - seriesMatcher[k] = regexp.MustCompile(v) - } - - moviesMatcher = regexp.MustCompile("(?:\\[.*\\])?(.+)(?:\\(?)((?:18|19|20)(?:[0-9]{2}))(?:\\)?)(?:.*)?\\.(.+)") -} - -func move(name string, config Config, info SeasonInfo) { - fmt.Printf(`------ lookup file: "%s" ------%s`, name, "\n") - config.src = name - finfo, err := os.Lstat(config.src) - if err != nil { - printerr(err) - return - } - lname := strings.ToLower(config.src) - suffix := "." + strings.ToLower(config.ignore) - if strings.HasSuffix(lname, suffix) { - fmt.Printf(`------ file ignored: "%s" ------%s`, config.src, "\n") - return - } - if finfo.Mode().IsRegular() { - config.src = strings.TrimSuffix(strings.TrimSuffix(config.src, finfo.Name()), "/") - if finfo.Size() > config.size { - rename(finfo.Name(), config, info) - } - } -} - -func rename(filename string, config Config, info SeasonInfo){ - var episode int64 - var extension string - - find := func(re *regexp.Regexp)(bool){ - res := re.FindStringSubmatch(filename) - tf := func(r rune) bool { - return r < '0' - } - for k,v := range res { - splitted := strings.Split(v, "(") - if len(splitted) > 1 { - res[k] = strings.TrimFunc(splitted[0], tf) - } else { - res[k] = strings.TrimFunc(v, tf) - } - } - - if len(res) == 5{ - for _,v := range res{ - fmt.Println(v) - } - fmt.Println("") - info.Name = res[1] - - season,_ := strconv.ParseInt(res[2], 10, 64) - episode,_ = strconv.ParseInt(res[3], 10, 64) - info.Season = int(season) - extension = res[4] - info.IsSeries = true - return true - }else{ - return false - } - } - for _,v := range seriesMatcher { - if find(v) { - break - } - } - - if info.IsSeries { - newFilename, success := themoviedb(info.Name, info.Season, int(episode), extension, config.apikey, info.IncludeSeriesName, 0) - if success { - linkFile(config.src + "/" + filename, config.dst + "/" + newFilename) - } - } else { - res := moviesMatcher.FindStringSubmatch(filename) - if len(res) == 4{ - for _,v := range res{ - fmt.Println(v) - } - fmt.Println("") - - year,_ := strconv.ParseInt(res[2], 10, 64) - - if (year < 1800) || (year > 2100){ - year = 0 - } - - newFilename, success := themoviedb(res[1], info.Season, int(episode), res[3], config.apikey, info.IncludeSeriesName, int(year)) - if success { - linkFile(config.src + "/" + filename, config.dst + "/" + newFilename) - } - } - } -} - -func printerr(err error){ - if err != nil { - fmt.Println(err) - } -}