From d4a632f48de19e56bd68923d8bc82c1cbcc066df Mon Sep 17 00:00:00 2001 From: Blallo Date: Tue, 3 Aug 2021 00:37:51 +0200 Subject: [PATCH] Working thing --- go.mod | 3 + main.go | 155 ++++++++++++++++++++++++++++++++++++++------- main_test.go | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 309 insertions(+), 23 deletions(-) create mode 100644 go.mod create mode 100644 main_test.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..18a95b8 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.abbiamoundominio.org/blallo/classifier + +go 1.16 diff --git a/main.go b/main.go index 2f4645c..1d4dad1 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "io/fs" "io/ioutil" @@ -19,24 +18,58 @@ const ( 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 = iota + series kindOf = iota film music program ) var maybeSeries, maybeFilm, maybeMusic, maybeProgram *regexp.Regexp -var errYetPresent = errors.New("element yet present") -type classification map[string]int +type classification map[string]kindOf -func (c classification) add(element string, kind int) error { - if _, ok := c[element]; ok { - return errYetPresent +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 - return nil } func (c classification) get(element string) (string, bool) { @@ -44,21 +77,11 @@ func (c classification) get(element string) (string, bool) { if !ok { return "", false } - switch kind { - case series: - return "series", true - case film: - return "film", true - case music: - return "music", true - case program: - return "program", true - default: - panic(fmt.Sprintf("kind value now allowed: %d\n", kind)) - } + 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)$`) @@ -66,6 +89,7 @@ func init() { 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 { @@ -85,7 +109,39 @@ func isInSpecialDir(dir string) bool { return false } -func find(name string, info fs.FileInfo, err error) error { +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 @@ -94,9 +150,62 @@ func find(name string, info fs.FileInfo, err error) error { 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 main() { - fmt.Println("vim-go") +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) + } + } } diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..86f3e31 --- /dev/null +++ b/main_test.go @@ -0,0 +1,174 @@ +package main + +import ( + "io/ioutil" + "os" + "path/filepath" + "reflect" + "testing" +) + +func TestIsInSpecialDir(t *testing.T) { + special := []string{ + filepath.Join(absBasePath, "series", "media1", "file"), + filepath.Join(absBasePath, "film", "file"), + filepath.Join(absBasePath, "music", "album", "file"), + filepath.Join(absBasePath, "programs", "file"), + } + + for _, s := range special { + if !isInSpecialDir(s) { + t.Errorf("Should be special: %s\n", s) + } + } + + other := []string{ + filepath.Join(absBasePath, "media1", "film", "file"), + filepath.Join(absBasePath, "aFilm", "film", "file"), + filepath.Join(absBasePath, "album", "music", "file"), + filepath.Join(absBasePath, "myProgram", "program", "theProgram.exe"), + } + + for _, o := range other { + if isInSpecialDir(o) { + t.Errorf("Should be special: %s\n", o) + } + } +} + +func TestBaseDirOrFile(t *testing.T) { + data := []struct { + name string + expected string + }{ + { + name: filepath.Join(absBasePath, "file"), + expected: filepath.Join(absBasePath, "file"), + }, + { + name: filepath.Join(absBasePath, "baseDir", "file"), + expected: filepath.Join(absBasePath, "baseDir"), + }, + { + name: filepath.Join(absBasePath, "baseDir", "otherDir", "file"), + expected: filepath.Join(absBasePath, "baseDir"), + }, + } + + for _, datum := range data { + if res := baseDirOrFile(datum.name); res != datum.expected { + t.Errorf("%s != %s\n", res, datum.expected) + } + } +} + +func TestLink(t *testing.T) { + dir, err := os.MkdirTemp("/tmp", "classifier-test-*") + if err != nil { + t.Fatal(err) + } + + for _, d := range []string{"series", "film", "music", "programs"} { + if err := os.Mkdir(filepath.Join(dir, d), 0700); err != nil { + t.Fatal(err) + } + } + + testDirs := []struct { + path string + kind kindOf + }{ + {path: filepath.Join(dir, "testFilm1"), kind: film}, + {path: filepath.Join(dir, "testSeries"), kind: series}, + {path: filepath.Join(dir, "testAlbum"), kind: music}, + {path: filepath.Join(dir, "testProgram1"), kind: program}, + } + for _, d := range testDirs { + err := os.Mkdir(d.path, 0700) + if err != nil { + t.Fatal(err) + } + err = link(dir, d.path, d.kind) + if err != nil { + return + } + } + + testFiles := []struct { + path string + kind kindOf + }{ + {path: filepath.Join(dir, "testFilm2.avi"), kind: film}, + {path: filepath.Join(dir, "testProgram2.exe"), kind: program}, + } + for _, f := range testFiles { + fh, err := os.Create(f.path) + if err != nil { + t.Fatal(err) + } + fh.Close() + err = link(dir, f.path, f.kind) + if err != nil { + return + } + } + + fFilm, err := ioutil.ReadDir(filepath.Join(dir, "film")) + if err != nil { + t.Fatal(err) + } + sFilm := []string{} + for _, f := range fFilm { + sFilm = append(sFilm, f.Name()) + } + if reflect.DeepEqual(sFilm, []string{ + filepath.Join(dir, "film", "testFilm1"), + filepath.Join(dir, "film", "testFilm2.avi"), + }) { + t.Errorf("Unexpected film links: %s\n", sFilm) + } + + fSeries, err := ioutil.ReadDir(filepath.Join(dir, "series")) + if err != nil { + t.Fatal(err) + } + sSeries := []string{} + for _, f := range fSeries { + sSeries = append(sSeries, f.Name()) + } + if reflect.DeepEqual(sFilm, []string{ + filepath.Join(dir, "film", "testSeries"), + }) { + t.Errorf("Unexpected series links: %s\n", sFilm) + } + + fMusic, err := ioutil.ReadDir(filepath.Join(dir, "music")) + if err != nil { + t.Fatal(err) + } + sMusic := []string{} + for _, f := range fMusic { + sMusic = append(sMusic, f.Name()) + } + if reflect.DeepEqual(sMusic, []string{ + filepath.Join(dir, "film", "testAlbum"), + }) { + t.Errorf("Unexpected music links: %s\n", sMusic) + } + + fProg, err := ioutil.ReadDir(filepath.Join(dir, "film")) + if err != nil { + t.Fatal(err) + } + sProg := []string{} + for _, f := range fProg { + sProg = append(sProg, f.Name()) + } + if reflect.DeepEqual(sProg, []string{ + filepath.Join(dir, "film", "testProgram1"), + filepath.Join(dir, "film", "testProgram2.exe"), + }) { + t.Errorf("Unexpected programs links: %s\n", sProg) + } + os.RemoveAll(dir) +}