Working thing

This commit is contained in:
sfigato 2021-08-03 00:37:51 +02:00
parent bcb6c0f388
commit d4a632f48d
Signed by: blallo
GPG Key ID: 0CBE577C9B72DC3F
3 changed files with 309 additions and 23 deletions

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.abbiamoundominio.org/blallo/classifier
go 1.16

155
main.go
View File

@ -1,7 +1,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"io/fs" "io/fs"
"io/ioutil" "io/ioutil"
@ -19,24 +18,58 @@ const (
musicPath = absBasePath + "/music" musicPath = absBasePath + "/music"
programsPath = absBasePath + "/programs" 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 ( const (
series = iota series kindOf = iota
film film
music music
program program
) )
var maybeSeries, maybeFilm, maybeMusic, maybeProgram *regexp.Regexp 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 { func (c classification) add(element string, kind kindOf) {
if _, ok := c[element]; ok { if pre, ok := c[element]; ok {
return errYetPresent 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 c[element] = kind
return nil
} }
func (c classification) get(element string) (string, bool) { func (c classification) get(element string) (string, bool) {
@ -44,21 +77,11 @@ func (c classification) get(element string) (string, bool) {
if !ok { if !ok {
return "", false return "", false
} }
switch kind { return fmt.Sprint(kind), true
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))
}
} }
var cls classification var cls classification
var l *log.Logger
func init() { func init() {
maybeSeries = regexp.MustCompile(`(?i)^.*(((s|S)(\d+)((e|E)(\d+))?)|((season)(\ )?(\d+)?)).*\.(mkv|mp4|avi)$`) 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)$`) maybeMusic = regexp.MustCompile(`(?i)^.*\.(mp3|ogg|flac)$`)
maybeProgram = regexp.MustCompile(`(?i)^.*\.(zip|tar|tar\.gz|exe|msi|rar)$`) maybeProgram = regexp.MustCompile(`(?i)^.*\.(zip|tar|tar\.gz|exe|msi|rar)$`)
cls = make(classification) cls = make(classification)
l = log.Default()
} }
func isInSpecialDir(dir string) bool { func isInSpecialDir(dir string) bool {
@ -85,7 +109,39 @@ func isInSpecialDir(dir string) bool {
return false 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 // skip directories
if info.IsDir() { if info.IsDir() {
return nil return nil
@ -94,9 +150,62 @@ func find(name string, info fs.FileInfo, err error) error {
if isInSpecialDir(name) { if isInSpecialDir(name) {
return nil 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 return nil
} }
func main() { func link(basePath, target string, kind kindOf) error {
fmt.Println("vim-go") 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)
}
}
} }

174
main_test.go Normal file
View File

@ -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)
}