From 61569b05150142434eefe30bbb14b3e5de88c2c3 Mon Sep 17 00:00:00 2001 From: Bel LaPointe <153096461+breel-render@users.noreply.github.com> Date: Fri, 4 Apr 2025 17:11:36 -0600 Subject: [PATCH] tdd whee --- main.go | 60 +++++++++++++++++++++++++++++- main_test.go | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 159 insertions(+), 4 deletions(-) diff --git a/main.go b/main.go index e5e3002..9e64b0d 100644 --- a/main.go +++ b/main.go @@ -2,7 +2,12 @@ package main import ( "context" + "fmt" + "maps" + "os" "os/signal" + "path" + "regexp" "syscall" ) @@ -10,11 +15,62 @@ func main() { ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT) defer can() - if err := Run(ctx); err != nil { + if err := Run(ctx, + os.Args[1], + os.Args[2], + os.Args[3:], + nil, + ); err != nil { panic(err) } } -func Run(ctx context.Context) error { +func Run(ctx context.Context, outd, ind string, patterns []string, overrides map[string]string) error { + entries, err := os.ReadDir(ind) + if err != nil { + return err + } + for _, entry := range entries { + if err := one(ctx, outd, path.Join(ind, entry.Name()), patterns, overrides); err != nil { + return err + } + } return nil } + +func one(ctx context.Context, outd, inf string, patterns []string, overrides map[string]string) error { + f := path.Base(inf) + for _, pattern := range patterns { + re := regexp.MustCompile(pattern) + if !re.MatchString(f) { + continue + } + + found := maps.Clone(overrides) + groupNames := re.SubexpNames() + groups := re.FindStringSubmatch(f) + for i := 1; i < len(groupNames); i++ { + k := groupNames[i] + v := groups[i] + found[k] = v + } + + if found["title"] == "" || found["season"] == "" || found["episode"] == "" { + continue + } + + if err := mvNLn(ctx, outd, inf, found["title"], found["season"], found["episode"]); err != nil { + return err + } + return nil + } + return nil +} + +func mvNLn(ctx context.Context, outd, inf string, title, season, episode string) error { + outf := path.Join(outd, fmt.Sprintf("%s_S%sE%s%s", title, season, episode, path.Ext(inf))) + if err := os.Rename(inf, outf); err != nil { + return err + } + return os.Symlink(outf, inf) +} diff --git a/main_test.go b/main_test.go index fea8799..e5bdbe1 100644 --- a/main_test.go +++ b/main_test.go @@ -2,13 +2,112 @@ package main_test import ( "context" + "io/fs" + "io/ioutil" + "os" + "path" + "slices" "testing" main "gitea/show-ingestion" ) func TestRun(t *testing.T) { - if err := main.Run(context.Background()); err != nil { - t.Fatal(err) + cases := map[string]struct { + given []string + patterns []string + overrides map[string]string + want []string + }{ + "empty": {}, + "fallback survivor": { + given: []string{ + "Survivor.AU.S12E11.1080p.HEVC.x265-MeGusta[EZTVx.to].mkv", + "Survivor.AU.S12E11.720p.HEVC.x265-MeGusta[EZTVx.to].mkv", + "Survivor.AU.S12E12.720p.HEVC.x265-MeGusta[EZTVx.to].mkv", + }, + patterns: []string{ + ".urvivor.[Aa][Uu].*[sS](?P[0-9]+)[eE](?P[0-9]*).*1080.*MeGusta", + ".urvivor.[Aa][Uu].*[sS](?P[0-9]+)[eE](?P[0-9]*).*720.*MeGusta", + }, + overrides: map[string]string{ + "title": "Australian_Survivor", + }, + want: []string{ + "Australian_Survivor_S12E11.mkv", + "Australian_Survivor_S12E12.mkv", + }, + }, + "easy w group": { + given: []string{ + "[SubsPlease] Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san - 01 (720p) [A12844D5].mkv", + "[SubsPlease] Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san - 02 (720p) [2608F490].mkv", + }, + want: []string{ + "Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E01.mkv", + "Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E02.mkv", + }, + }, + } + + for name, d := range cases { + c := d + t.Run(name, func(t *testing.T) { + ind := t.TempDir() + for _, f := range c.given { + ioutil.WriteFile(path.Join(ind, f), []byte{}, os.ModePerm) + } + outd := t.TempDir() + + if err := main.Run(context.Background(), outd, ind, c.patterns, c.overrides); err != nil { + t.Fatal(err) + } + + for _, f := range c.want { + if stat, err := os.Stat(path.Join(outd, f)); os.IsNotExist(err) { + t.Errorf("expected %s", f) + } else if !stat.Mode().IsRegular() { + t.Errorf("%s not a regular file: %v", f, stat.Mode()) + } + } + + if entries, err := os.ReadDir(outd); err != nil { + t.Error("failed to list outdir: %w", err) + } else { + for _, entry := range entries { + if !slices.Contains(c.want, path.Base(entry.Name())) { + t.Errorf("unexpected %s", entry.Name()) + } + if stat, err := entry.Info(); err != nil { + t.Errorf("cant read info of %s: %v", entry.Name(), err) + } else if !stat.Mode().IsRegular() { + t.Errorf("non-regular file %s in out: %v", entry.Name(), stat.Mode()) + } + } + } + + if entries, err := os.ReadDir(outd); err != nil { + t.Error("failed to list outdir: %w", err) + } else { + for _, entry := range entries { + if !slices.Contains(c.want, path.Base(entry.Name())) { + t.Errorf("unexpected %s", entry.Name()) + } + } + } + + if entries, err := os.ReadDir(ind); err != nil { + t.Error("failed to list indir: %w", err) + } else { + for _, entry := range entries { + inf := path.Join(ind, entry.Name()) + if stat, err := os.Stat(inf); err != nil { + t.Errorf("%s no longer in ind: %v", inf, err) + } else if stat.Mode() != fs.ModeSymlink { + t.Errorf("%s not replaced with symlink: %v", inf, stat.Mode())entry.Name(), + } + } + } + }) } }