212 lines
4.2 KiB
Go
212 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
absBasePath = "/opt/synapse/download"
|
|
seriesPath = absBasePath + "/series"
|
|
filmPath = absBasePath + "/film"
|
|
musicPath = absBasePath + "/music"
|
|
programsPath = absBasePath + "/programs"
|
|
)
|
|
|
|
type kindOf int
|
|
|
|
func (k kindOf) String() string {
|
|
switch k {
|
|
case series:
|
|
return "series"
|
|
case film:
|
|
return "film"
|
|
case music:
|
|
return "music"
|
|
case program:
|
|
return "program"
|
|
default:
|
|
panic("Unkwnown kind")
|
|
}
|
|
}
|
|
|
|
func (k kindOf) replace(other kindOf) bool {
|
|
switch {
|
|
case k == program && other == film:
|
|
return true
|
|
case k == program && other == series:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
const (
|
|
series kindOf = iota
|
|
film
|
|
music
|
|
program
|
|
)
|
|
|
|
var maybeSeries, maybeFilm, maybeMusic, maybeProgram *regexp.Regexp
|
|
|
|
type classification map[string]kindOf
|
|
|
|
func (c classification) add(element string, kind kindOf) {
|
|
if pre, ok := c[element]; ok {
|
|
if kind == pre {
|
|
return
|
|
}
|
|
l.Printf("%s yet present with different type (%s != %s)\n", element, pre, kind)
|
|
if !pre.replace(kind) {
|
|
l.Printf("(%s > %s), replacing kind for %s\n", kind, pre, element)
|
|
return
|
|
}
|
|
}
|
|
c[element] = kind
|
|
}
|
|
|
|
func (c classification) get(element string) (string, bool) {
|
|
kind, ok := c[element]
|
|
if !ok {
|
|
return "", false
|
|
}
|
|
return fmt.Sprint(kind), true
|
|
}
|
|
|
|
var cls classification
|
|
var l *log.Logger
|
|
|
|
func init() {
|
|
maybeSeries = regexp.MustCompile(`(?i)^.*(((s|S)(\d+)((e|E)(\d+))?)|((season)(\ )?(\d+)?)).*\.(mkv|mp4|avi)$`)
|
|
maybeFilm = regexp.MustCompile(`(?i)^.*\.(mkv|mp4|avi)$`)
|
|
maybeMusic = regexp.MustCompile(`(?i)^.*\.(mp3|ogg|flac)$`)
|
|
maybeProgram = regexp.MustCompile(`(?i)^.*\.(zip|tar|tar\.gz|exe|msi|rar)$`)
|
|
cls = make(classification)
|
|
l = log.Default()
|
|
}
|
|
|
|
func isInSpecialDir(dir string) bool {
|
|
if strings.Contains(dir, seriesPath) {
|
|
return true
|
|
}
|
|
if strings.Contains(dir, filmPath) {
|
|
return true
|
|
}
|
|
if strings.Contains(dir, musicPath) {
|
|
return true
|
|
}
|
|
if strings.Contains(dir, programsPath) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func baseDirOrFile(name string) string {
|
|
dir := filepath.Dir(name)
|
|
if dir == absBasePath {
|
|
return name
|
|
}
|
|
rel := strings.Trim(strings.Replace(dir, absBasePath, "", 1), "/")
|
|
comps := strings.Split(rel, "/")
|
|
return filepath.Join(absBasePath, comps[0])
|
|
}
|
|
|
|
func findSeriesByDir(path string, d fs.DirEntry, err error) error {
|
|
if !d.IsDir() {
|
|
return nil
|
|
}
|
|
files, err := ioutil.ReadDir(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
videoFiles := []string{}
|
|
for _, f := range files {
|
|
if maybeFilm.Match([]byte(f.Name())) {
|
|
videoFiles = append(videoFiles, f.Name())
|
|
}
|
|
}
|
|
|
|
if len(videoFiles) > 2 {
|
|
cls.add(baseDirOrFile(path), series)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func findByFile(name string, info fs.FileInfo, err error) error {
|
|
// skip directories
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
// skip special directories
|
|
if isInSpecialDir(name) {
|
|
return nil
|
|
}
|
|
baseFile := baseDirOrFile(name)
|
|
base := filepath.Base(name)
|
|
v := []byte(base)
|
|
switch {
|
|
case maybeSeries.Match(v):
|
|
cls.add(baseFile, series)
|
|
case maybeFilm.Match(v):
|
|
cls.add(baseFile, film)
|
|
case maybeMusic.Match(v):
|
|
cls.add(baseFile, music)
|
|
case maybeProgram.Match(v):
|
|
cls.add(baseFile, program)
|
|
default:
|
|
l.Printf("No match: %s\n", base)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func link(basePath, target string, kind kindOf) error {
|
|
targetName := filepath.Base(target)
|
|
switch kind {
|
|
case series:
|
|
src := filepath.Join(basePath, "series", targetName)
|
|
return doLink(target, src)
|
|
case film:
|
|
src := filepath.Join(basePath, "film", targetName)
|
|
return doLink(target, src)
|
|
case music:
|
|
src := filepath.Join(basePath, "music", targetName)
|
|
return doLink(target, src)
|
|
case program:
|
|
src := filepath.Join(basePath, "programs", targetName)
|
|
return doLink(target, src)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func doLink(target, src string) error {
|
|
err := os.Symlink(target, src)
|
|
if err == os.ErrExist {
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
func main() {
|
|
if err := filepath.WalkDir(absBasePath, findSeriesByDir); err != nil {
|
|
l.Fatal(err)
|
|
}
|
|
if err := filepath.Walk(absBasePath, findByFile); err != nil {
|
|
l.Fatal(err)
|
|
}
|
|
l.Printf("Result: %s\n", cls)
|
|
for file, kind := range cls {
|
|
if err := link(absBasePath, file, kind); err != nil {
|
|
l.Fatal(err)
|
|
}
|
|
}
|
|
}
|