Compare commits
3 Commits
61569b0515
...
805e666230
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
805e666230 | ||
|
|
81d1ce1dde | ||
|
|
b696251895 |
52
main.go
52
main.go
@@ -2,35 +2,48 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"maps"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Fields struct {
|
||||||
|
Title string
|
||||||
|
Season string
|
||||||
|
Episode string
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
|
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
|
||||||
defer can()
|
defer can()
|
||||||
|
|
||||||
|
var overrides Fields
|
||||||
|
json.Unmarshal([]byte(os.Args[3]), &overrides)
|
||||||
|
|
||||||
if err := Run(ctx,
|
if err := Run(ctx,
|
||||||
os.Args[1],
|
os.Args[1],
|
||||||
os.Args[2],
|
os.Args[2],
|
||||||
os.Args[3:],
|
os.Args[4:],
|
||||||
nil,
|
overrides,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(ctx context.Context, outd, ind string, patterns []string, overrides map[string]string) error {
|
func Run(ctx context.Context, outd, ind string, patterns []string, overrides Fields) error {
|
||||||
entries, err := os.ReadDir(ind)
|
entries, err := os.ReadDir(ind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
|
if !entry.Type().IsRegular() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err := one(ctx, outd, path.Join(ind, entry.Name()), patterns, overrides); err != nil {
|
if err := one(ctx, outd, path.Join(ind, entry.Name()), patterns, overrides); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -38,7 +51,7 @@ func Run(ctx context.Context, outd, ind string, patterns []string, overrides map
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func one(ctx context.Context, outd, inf string, patterns []string, overrides map[string]string) error {
|
func one(ctx context.Context, outd, inf string, patterns []string, overrides Fields) error {
|
||||||
f := path.Base(inf)
|
f := path.Base(inf)
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
re := regexp.MustCompile(pattern)
|
re := regexp.MustCompile(pattern)
|
||||||
@@ -46,29 +59,38 @@ func one(ctx context.Context, outd, inf string, patterns []string, overrides map
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
found := maps.Clone(overrides)
|
found := overrides
|
||||||
groupNames := re.SubexpNames()
|
groupNames := re.SubexpNames()
|
||||||
groups := re.FindStringSubmatch(f)
|
groups := re.FindStringSubmatch(f)
|
||||||
for i := 1; i < len(groupNames); i++ {
|
for i := 1; i < len(groupNames); i++ {
|
||||||
k := groupNames[i]
|
|
||||||
v := groups[i]
|
v := groups[i]
|
||||||
found[k] = v
|
switch groupNames[i] {
|
||||||
|
case "title":
|
||||||
|
found.Title = v
|
||||||
|
case "season":
|
||||||
|
found.Season = v
|
||||||
|
case "episode":
|
||||||
|
found.Episode = v
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected capture group %q", groupNames[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if found["title"] == "" || found["season"] == "" || found["episode"] == "" {
|
if found.Title == "" || found.Season == "" || found.Episode == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
found.Title = strings.Join(strings.Fields(found.Title), "_")
|
||||||
|
|
||||||
if err := mvNLn(ctx, outd, inf, found["title"], found["season"], found["episode"]); err != nil {
|
return mvNLn(ctx, outd, inf, found)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func mvNLn(ctx context.Context, outd, inf string, title, season, episode string) error {
|
func mvNLn(ctx context.Context, outd, inf string, fields Fields) error {
|
||||||
outf := path.Join(outd, fmt.Sprintf("%s_S%sE%s%s", title, season, episode, path.Ext(inf)))
|
outf := path.Join(outd, fmt.Sprintf("%s_S%sE%s%s", fields.Title, fields.Season, fields.Episode, path.Ext(inf)))
|
||||||
|
if _, err := os.Stat(outf); err == nil {
|
||||||
|
return nil // fmt.Errorf("conflict: %s already exists", path.Base(outf))
|
||||||
|
}
|
||||||
if err := os.Rename(inf, outf); err != nil {
|
if err := os.Rename(inf, outf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
38
main_test.go
38
main_test.go
@@ -2,7 +2,6 @@ package main_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io/fs"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -16,7 +15,7 @@ func TestRun(t *testing.T) {
|
|||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
given []string
|
given []string
|
||||||
patterns []string
|
patterns []string
|
||||||
overrides map[string]string
|
overrides main.Fields
|
||||||
want []string
|
want []string
|
||||||
}{
|
}{
|
||||||
"empty": {},
|
"empty": {},
|
||||||
@@ -30,8 +29,8 @@ func TestRun(t *testing.T) {
|
|||||||
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*1080.*MeGusta",
|
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*1080.*MeGusta",
|
||||||
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*720.*MeGusta",
|
".urvivor.[Aa][Uu].*[sS](?P<season>[0-9]+)[eE](?P<episode>[0-9]*).*720.*MeGusta",
|
||||||
},
|
},
|
||||||
overrides: map[string]string{
|
overrides: main.Fields{
|
||||||
"title": "Australian_Survivor",
|
Title: "Australian_Survivor",
|
||||||
},
|
},
|
||||||
want: []string{
|
want: []string{
|
||||||
"Australian_Survivor_S12E11.mkv",
|
"Australian_Survivor_S12E11.mkv",
|
||||||
@@ -43,6 +42,12 @@ func TestRun(t *testing.T) {
|
|||||||
"[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 - 01 (720p) [A12844D5].mkv",
|
||||||
"[SubsPlease] Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san - 02 (720p) [2608F490].mkv",
|
"[SubsPlease] Tokidoki Bosotto Russia-go de Dereru Tonari no Alya-san - 02 (720p) [2608F490].mkv",
|
||||||
},
|
},
|
||||||
|
patterns: []string{
|
||||||
|
`^\[[^\]]*\] (?P<title>.*) - (?<episode>[0-9]*)`,
|
||||||
|
},
|
||||||
|
overrides: main.Fields{
|
||||||
|
Season: "01",
|
||||||
|
},
|
||||||
want: []string{
|
want: []string{
|
||||||
"Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E01.mkv",
|
"Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E01.mkv",
|
||||||
"Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E02.mkv",
|
"Tokidoki_Bosotto_Russia-go_de_Dereru_Tonari_no_Alya-san_S01E02.mkv",
|
||||||
@@ -60,7 +65,9 @@ func TestRun(t *testing.T) {
|
|||||||
outd := t.TempDir()
|
outd := t.TempDir()
|
||||||
|
|
||||||
if err := main.Run(context.Background(), outd, ind, c.patterns, c.overrides); err != nil {
|
if err := main.Run(context.Background(), outd, ind, c.patterns, c.overrides); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal("err on first run:", err)
|
||||||
|
} else if err := main.Run(context.Background(), outd, ind, c.patterns, c.overrides); err != nil {
|
||||||
|
t.Fatal("err on second run:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range c.want {
|
for _, f := range c.want {
|
||||||
@@ -75,23 +82,12 @@ func TestRun(t *testing.T) {
|
|||||||
t.Error("failed to list outdir: %w", err)
|
t.Error("failed to list outdir: %w", err)
|
||||||
} else {
|
} else {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
|
t.Logf("%s", entry.Name())
|
||||||
if !slices.Contains(c.want, path.Base(entry.Name())) {
|
if !slices.Contains(c.want, path.Base(entry.Name())) {
|
||||||
t.Errorf("unexpected %s", entry.Name())
|
t.Errorf("unexpected %s", entry.Name())
|
||||||
}
|
}
|
||||||
if stat, err := entry.Info(); err != nil {
|
if !entry.Type().IsRegular() {
|
||||||
t.Errorf("cant read info of %s: %v", entry.Name(), err)
|
t.Errorf("non-regular file %s in out: %v", entry.Name(), entry.Type())
|
||||||
} 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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -101,10 +97,8 @@ func TestRun(t *testing.T) {
|
|||||||
} else {
|
} else {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
inf := path.Join(ind, entry.Name())
|
inf := path.Join(ind, entry.Name())
|
||||||
if stat, err := os.Stat(inf); err != nil {
|
if _, err := os.Stat(inf); err != nil {
|
||||||
t.Errorf("%s no longer in ind: %v", inf, err)
|
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(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user