test recursive and run as go run ./ r
parent
6af1f231df
commit
daa520de7d
2
go.mod
2
go.mod
|
|
@ -1,3 +1,5 @@
|
|||
module gitea/show-ingestion
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
112
main.go
112
main.go
|
|
@ -5,13 +5,15 @@ import (
|
|||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Fields struct {
|
||||
|
|
@ -26,19 +28,65 @@ func main() {
|
|||
ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT)
|
||||
defer can()
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
if err := Recursive(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
if err := Main(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
foo := Main
|
||||
if len(os.Args) == 2 && os.Args[1] == "r" {
|
||||
foo = Recursive
|
||||
}
|
||||
if err := foo(ctx); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Recursive(ctx context.Context) error {
|
||||
return io.EOF
|
||||
q := []string{"./"}
|
||||
for len(q) > 0 {
|
||||
d := q[0]
|
||||
q = q[1:]
|
||||
|
||||
p := path.Join(d, ".show-ingestion.yaml")
|
||||
if _, err := os.Stat(p); err != nil {
|
||||
} else if err := func() error {
|
||||
var y struct {
|
||||
C Fields
|
||||
O string
|
||||
D bool
|
||||
P []string
|
||||
}
|
||||
b, _ := os.ReadFile(path.Join(d, ".show-ingestion.yaml"))
|
||||
if err := yaml.Unmarshal(b, &y); err != nil {
|
||||
return fmt.Errorf("%s: %w", p, err)
|
||||
}
|
||||
|
||||
was, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chdir(d); err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Chdir(was)
|
||||
|
||||
if err := Run(ctx, y.O, "./", y.P, y.C, y.D); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Chdir(was)
|
||||
}(); err != nil {
|
||||
return fmt.Errorf("%s: %w", p, err)
|
||||
}
|
||||
|
||||
entries, err := readDir(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
q = append(q, path.Join(d, entry.Name()))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Main(ctx context.Context) error {
|
||||
|
|
@ -54,24 +102,34 @@ func Main(ctx context.Context) error {
|
|||
var overrides Fields
|
||||
json.Unmarshal([]byte(*overridesS), &overrides)
|
||||
|
||||
mvNLn := RealMvNLn
|
||||
if *dry {
|
||||
mvNLn = DryMvNLn()
|
||||
}
|
||||
|
||||
return Run(ctx,
|
||||
*outd,
|
||||
*ind,
|
||||
append(flags.Args(),
|
||||
`^\[[^\]]*\] (?P<title>.*) - (?P<episode>[0-9]+) .*`,
|
||||
flags.Args(),
|
||||
overrides,
|
||||
*dry,
|
||||
)
|
||||
}
|
||||
|
||||
func Run(ctx context.Context, outd, ind string, patterns []string, overrides Fields, dry bool) error {
|
||||
mvNLn := RealMvNLn
|
||||
if dry {
|
||||
mvNLn = DryMvNLn()
|
||||
}
|
||||
return RunWith(ctx,
|
||||
outd,
|
||||
ind,
|
||||
append(patterns,
|
||||
`^\[[^\]]*\] (?P<title>.*) - (?P<episode>[0-9]+).*`,
|
||||
`^(?P<title>.*) S(?P<season>[0-9]+)E(?P<episode>[0-9]+).*`,
|
||||
),
|
||||
overrides,
|
||||
mvNLn,
|
||||
)
|
||||
}
|
||||
|
||||
func Run(ctx context.Context, outd, ind string, patterns []string, overrides Fields, mvNLn MvNLn) error {
|
||||
entries, err := os.ReadDir(ind)
|
||||
func RunWith(ctx context.Context, outd, ind string, patterns []string, overrides Fields, mvNLn MvNLn) error {
|
||||
entries, err := readDir(ind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -131,7 +189,10 @@ func RealMvNLn(outf, inf string) error {
|
|||
return fmt.Errorf("cannot mv_n_ln(%s): (%v) mode=%v", inf, err, stat.Mode())
|
||||
}
|
||||
if _, err := os.Stat(outf); err == nil {
|
||||
return nil // fmt.Errorf("conflict: %s already exists", path.Base(outf))
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(outf), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.Rename(inf, outf); err != nil {
|
||||
return err
|
||||
|
|
@ -155,3 +216,14 @@ func DryMvNLn() func(string, string) error {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func readDir(d string) ([]fs.DirEntry, error) {
|
||||
entries, err := os.ReadDir(d)
|
||||
result := []fs.DirEntry{}
|
||||
for _, entry := range entries {
|
||||
if !strings.HasPrefix(entry.Name(), ".") {
|
||||
result = append(result, entry)
|
||||
}
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
|
|
|||
102
main_test.go
102
main_test.go
|
|
@ -28,7 +28,7 @@ func TestRunChoosesOne(t *testing.T) {
|
|||
"Australian_Survivor_S12E12.mkv": false,
|
||||
}
|
||||
|
||||
if err := main.Run(context.Background(),
|
||||
if err := main.RunWith(context.Background(),
|
||||
outd,
|
||||
ind,
|
||||
[]string{
|
||||
|
|
@ -53,7 +53,7 @@ func TestRunChoosesOne(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
func TestRunWith(t *testing.T) {
|
||||
cases := map[string]struct {
|
||||
given []string
|
||||
patterns []string
|
||||
|
|
@ -106,9 +106,9 @@ func TestRun(t *testing.T) {
|
|||
}
|
||||
outd := t.TempDir()
|
||||
|
||||
if err := main.Run(context.Background(), outd, ind, c.patterns, c.overrides, main.RealMvNLn); err != nil {
|
||||
if err := main.RunWith(context.Background(), outd, ind, c.patterns, c.overrides, main.RealMvNLn); err != nil {
|
||||
t.Fatal("err on first run:", err)
|
||||
} else if err := main.Run(context.Background(), outd, ind, c.patterns, c.overrides, main.RealMvNLn); err != nil {
|
||||
} else if err := main.RunWith(context.Background(), outd, ind, c.patterns, c.overrides, main.RealMvNLn); err != nil {
|
||||
t.Fatal("err on second run:", err)
|
||||
}
|
||||
|
||||
|
|
@ -147,3 +147,97 @@ func TestRun(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecursive(t *testing.T) {
|
||||
was, _ := os.Getwd()
|
||||
t.Cleanup(func() { os.Chdir(was) })
|
||||
os.Chdir(t.TempDir())
|
||||
|
||||
outd := t.TempDir()
|
||||
os.MkdirAll(path.Join(outd, "A"), os.ModePerm)
|
||||
|
||||
// use config
|
||||
write("./showA/.show-ingestion.yaml", `{
|
||||
"c": {
|
||||
"title": "A",
|
||||
"season": "A",
|
||||
"episode": "A"
|
||||
},
|
||||
"p": [".*"],
|
||||
"o": "`+outd+`/A"
|
||||
}`)
|
||||
write("./showA/file.a")
|
||||
|
||||
// parse files
|
||||
write("./showB/.show-ingestion.yaml", `{
|
||||
"o": "`+outd+`/B",
|
||||
"p": []
|
||||
}`)
|
||||
write("./showB/title S01E02.b")
|
||||
|
||||
// use file pattern
|
||||
write("./dirA/showC/.show-ingestion.yaml", `{
|
||||
"o": "`+outd+`/C",
|
||||
"p": ["^(?P<title>.) (?P<season>.) (?P<episode>.)"]
|
||||
}`)
|
||||
write("./dirA/showC/t s e.c")
|
||||
|
||||
// dry run
|
||||
write("./dirA/showD/.show-ingestion.yaml", `{
|
||||
"o": "`+outd+`/D",
|
||||
"d": true
|
||||
}`)
|
||||
write("./dirA/showD/title S02E04.d")
|
||||
|
||||
// not configured
|
||||
os.MkdirAll("./dirB/showE", os.ModePerm)
|
||||
write("./dirB/showE/title S03E06.e")
|
||||
|
||||
if err := main.Recursive(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
exists(t, path.Join(outd, "A", "A_SAEA.a"))
|
||||
exists(t, path.Join(outd, "B", "title_S01E02.b"))
|
||||
exists(t, path.Join(outd, "C", "t_SsEe.c"))
|
||||
notExists(t, path.Join(outd, "D", "title_S02E04.d"))
|
||||
notExists(t, path.Join(outd, "title_S03E06.e"))
|
||||
}
|
||||
|
||||
func write(f string, b ...string) {
|
||||
if len(b) == 0 {
|
||||
b = append(b, "")
|
||||
}
|
||||
|
||||
os.MkdirAll(path.Dir(f), os.ModePerm)
|
||||
os.WriteFile(f, []byte(b[0]), os.ModePerm)
|
||||
}
|
||||
|
||||
func exists(t *testing.T, p string) {
|
||||
if _, err := os.Stat(p); os.IsNotExist(err) {
|
||||
d := path.Dir(path.Dir(p))
|
||||
t.Errorf("expected %s of %s (%+v)", path.Base(p), d, ls(d))
|
||||
}
|
||||
}
|
||||
|
||||
func notExists(t *testing.T, p string) {
|
||||
if _, err := os.Stat(p); !os.IsNotExist(err) {
|
||||
d := path.Dir(path.Dir(p))
|
||||
t.Errorf("unexpected %s of %s (%+v)", path.Base(p), d, ls(d))
|
||||
}
|
||||
}
|
||||
|
||||
func ls(d string) []string {
|
||||
result := []string{}
|
||||
entries, _ := os.ReadDir(d)
|
||||
for _, entry := range entries {
|
||||
p := path.Join(d, entry.Name())
|
||||
if entry.IsDir() {
|
||||
result = append(result, ls(p)...)
|
||||
} else {
|
||||
result = append(result, p)
|
||||
}
|
||||
}
|
||||
slices.Sort(result)
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue