Skip to content

Commit

Permalink
Merge pull request #7 from acgtools/feat-copy-subs
Browse files Browse the repository at this point in the history
Feat: add flag for copying subs to video directory
  • Loading branch information
dreamjz authored Nov 24, 2023
2 parents bf83897 + 12df706 commit 658adbd
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 39 deletions.
5 changes: 2 additions & 3 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (
)

type Config struct {
VidExt []string // video extension
SubExt []string // subtitle extension
Log *LogConfig
Copy bool
Log *LogConfig
}

type LogConfig struct {
Expand Down
4 changes: 3 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,16 @@ var rootCmd = &cobra.Command{
}))
slog.SetDefault(logger)

return episode.AutoRename(args[0], args[1]) //nolint:wrapcheck
return episode.AutoRename(args[0], args[1], config.Copy) //nolint:wrapcheck
},
}

func init() { //nolint:gochecknoinits
rootCmd.PersistentFlags().String("log-level", "info", "log level, options: debug, info, warn, error")
rootCmd.PersistentFlags().BoolP("copy", "c", false, "copy subtitles after renaming")

_ = viper.BindPFlag("log.level", rootCmd.PersistentFlags().Lookup("log-level"))
_ = viper.BindPFlag("copy", rootCmd.PersistentFlags().Lookup("copy"))
}

func Execute() {
Expand Down
84 changes: 74 additions & 10 deletions pkg/episode/episode.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package episode
import (
"errors"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
Expand All @@ -17,19 +18,19 @@ const (
minFileNum = 2
)

func AutoRename(vidDir, subDir string) error {
func AutoRename(vidDir, subDir string, cpy bool) error {
var err error

if !filepath.IsAbs(vidDir) {
vidDir, err = filepath.Abs(vidDir)
if err != nil {
return fmt.Errorf("failed to convert video path %q to absolute path: %w", vidDir, err)
return fmt.Errorf("convert video path %q to absolute path: %w", vidDir, err)
}
}
if !filepath.IsAbs(subDir) {
subDir, err = filepath.Abs(subDir)
if err != nil {
return fmt.Errorf("failed to convert subtitle path %q to absolute path: %w", subDir, err)
return fmt.Errorf("convert subtitle path %q to absolute path: %w", subDir, err)
}
}

Expand All @@ -39,15 +40,33 @@ func AutoRename(vidDir, subDir string) error {
slog.Info("Getting episode info...")
vidMap, err := parseEpisodes(vidDir)
if err != nil {
return fmt.Errorf("failed to parse video episode: %w", err)
return fmt.Errorf("parse video episode: %w", err)
}

subMap, err := parseEpisodes(subDir)
if err != nil {
return fmt.Errorf("failed to parse subtitle episode: %w", err)
return fmt.Errorf("parse subtitle episode: %w", err)
}

slog.Info("Renaming...")
err = renamesSubs(subDir, vidMap, subMap)
if err != nil {
return err
}

if cpy {
slog.Info("Copying subs...")

if err := copySubs(vidDir, subDir); err != nil {
return fmt.Errorf("copy subs: %w", err)
}
}

slog.Info("Success!")
return nil
}

func renamesSubs(subDir string, vidMap, subMap map[int]string) error {
for ep, vidName := range vidMap {
subName, ok := subMap[ep]
if !ok {
Expand All @@ -60,20 +79,19 @@ func AutoRename(vidDir, subDir string) error {

slog.Debug("Rename subtitles", "old_path", oldSubPath, "new_path", newSubPath)

err = os.Rename(oldSubPath, newSubPath)
err := os.Rename(oldSubPath, newSubPath)
if err != nil {
return fmt.Errorf("failed to rename subtitle file: %w", err)
return fmt.Errorf("rename subtitle file: %w", err)
}
}

slog.Info("Success!")
return nil
}

func parseEpisodes(dir string) (map[int]string, error) {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("failed to read directory: %q, %w", dir, err)
return nil, fmt.Errorf("read directory: %q, %w", dir, err)
}

if len(entries) < minFileNum {
Expand All @@ -87,7 +105,7 @@ func parseEpisodes(dir string) (map[int]string, error) {

epStartIndex, err := getEpPosInName(filteredEntries[0].Name(), filteredEntries[1].Name())
if err != nil {
return nil, fmt.Errorf("failed to get episode position in file name: %q, %w", filteredEntries[0].Name(), err)
return nil, fmt.Errorf("get episode position in file name: %q, %w", filteredEntries[0].Name(), err)
}

nameEpMap := make(map[int]string, len(filteredEntries))
Expand Down Expand Up @@ -150,3 +168,49 @@ func filterFiles(entries []os.DirEntry) []os.DirEntry {

return filteredEntries
}

func copyFile(dstPath, srcPath string) error {
src, err := os.Open(srcPath)
if err != nil {
return fmt.Errorf("open file %q: %w", srcPath, err)
}
defer fClose(src)

dst, err := os.Create(dstPath)
if err != nil {
return fmt.Errorf("create file: %q: %w", dstPath, err)
}
defer fClose(dst)

_, err = io.Copy(dst, src)
if err != nil {
return fmt.Errorf("copy %q to %q: %w", srcPath, dstPath, err)
}

return nil
}

func fClose(f *os.File) {
_ = f.Close()
}

func copySubs(vidDir, subDir string) error {
entries, err := os.ReadDir(subDir)
if err != nil {
return fmt.Errorf("read directory %q: %w", subDir, err)
}

for _, e := range entries {
if e.IsDir() {
continue
}

name := e.Name()
err := copyFile(filepath.Join(vidDir, name), filepath.Join(subDir, name))
if err != nil {
return err
}
}

return nil
}
65 changes: 40 additions & 25 deletions pkg/episode/episode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"log"
"log/slog"
"os"
"path/filepath"
"testing"

"github.com/acgtools/sub-renamer/pkg/episode"
Expand All @@ -16,15 +17,15 @@ const (

testDir = rootDir + "test/"
exampleDir = testDir + "example/"
originSub = exampleDir + "origin-subDir/"
vidDir = exampleDir + "vid/"
subDir = exampleDir + "subDir/"

originSub = "origin-subDir"
vidDir = "vid"
subDir = "sub"
)

func TestMain(m *testing.M) {
initLogger()
clean()
genVidSubFiles()

code := m.Run()
os.Exit(code)
Expand All @@ -40,30 +41,34 @@ func clean() {
_ = os.RemoveAll(exampleDir)
}

func genVidSubFiles() {
func genVidSubFiles(caseName string) {
const (
vidNameFormat = "[VCB-Studio] FAIRY TAIL [%03d][Ma10p_1080p][x265_flac].mkv"
subNameFormat = "[YYDM-11FANS][FAIRY_TAIL][%03d][BDRIP][720P][X264-10bit_AAC][B721D247].tc.ass"
)

_ = os.MkdirAll(vidDir, os.ModePerm)
_ = os.MkdirAll(originSub, os.ModePerm)
_ = os.MkdirAll(subDir, os.ModePerm)
vidPath := filepath.Join(exampleDir, caseName, vidDir)
originSubPath := filepath.Join(exampleDir, caseName, originSub)
subPath := filepath.Join(exampleDir, caseName, subDir)

_ = os.MkdirAll(vidPath, os.ModePerm)
_ = os.MkdirAll(originSubPath, os.ModePerm)
_ = os.MkdirAll(subPath, os.ModePerm)

for i := 1; i <= 175; i++ {
vid, err := os.Create(vidDir + fmt.Sprintf(vidNameFormat, i))
vid, err := os.Create(filepath.Join(vidPath, fmt.Sprintf(vidNameFormat, i)))
if err != nil {
log.Fatalf("failed to generate video file: %v", err)
}
_ = vid.Close()

osub, err := os.Create(originSub + fmt.Sprintf(subNameFormat, i))
osub, err := os.Create(filepath.Join(originSubPath, fmt.Sprintf(subNameFormat, i)))
if err != nil {
log.Fatalf("failed to generate original subDir file: %v", err)
}
_ = osub.Close()

sub, err := os.Create(subDir + fmt.Sprintf(subNameFormat, i))
sub, err := os.Create(filepath.Join(subPath, fmt.Sprintf(subNameFormat, i)))
if err != nil {
log.Fatalf("failed to generate subDir file: %v", err)
}
Expand All @@ -72,44 +77,44 @@ func genVidSubFiles() {

// some other dir or files
dir1, dir2 := "tmp1/", "tmp2/"
_ = os.Mkdir(vidDir+dir1, os.ModePerm)
_ = os.Mkdir(vidDir+dir2, os.ModePerm)
_ = os.Mkdir(subDir+dir1, os.ModePerm)
_ = os.Mkdir(subDir+dir2, os.ModePerm)
_ = os.Mkdir(filepath.Join(vidPath, dir1), os.ModePerm)
_ = os.Mkdir(filepath.Join(vidPath, dir2), os.ModePerm)
_ = os.Mkdir(filepath.Join(subPath, dir1), os.ModePerm)
_ = os.Mkdir(filepath.Join(subPath, dir2), os.ModePerm)

tf1, err := os.Create(vidDir + dir1 + "tmp.txt")
tf1, err := os.Create(filepath.Join(vidPath, dir1, "tmp.txt"))
if err != nil {
log.Fatalf("failed to generate other file: %v", err)
}
_ = tf1.Close()

tf2, err := os.Create(subDir + dir1 + "tmp.txt")
tf2, err := os.Create(filepath.Join(subPath, dir1, "tmp.txt"))
if err != nil {
log.Fatalf("failed to generate other file: %v", err)
}
_ = tf2.Close()

file1, file2 := "file1.mkv", "file2.txt"
f1, err := os.Create(vidDir + file1)
f1, err := os.Create(filepath.Join(vidPath, file1))
if err != nil {
log.Fatalf("failed to generate other file: %v", err)
}
_ = f1.Close()

f2, err := os.Create(vidDir + file2)
f2, err := os.Create(filepath.Join(vidPath, file2))
if err != nil {
log.Fatalf("failed to generate other file: %v", err)
}
_ = f2.Close()

file3, file4 := "file3.ass", "file4.txt"
f3, err := os.Create(subDir + file3)
f3, err := os.Create(filepath.Join(subPath, file3))
if err != nil {
log.Fatalf("failed to generate other file: %v", err)
}
_ = f3.Close()

f4, err := os.Create(subDir + file4)
f4, err := os.Create(filepath.Join(subPath, file4))
if err != nil {
log.Fatalf("failed to generate other file: %v", err)
}
Expand All @@ -123,20 +128,30 @@ func TestAutoRename(t *testing.T) {
name string
vidDir string
subDir string
cpy bool
wantErr bool
}{
{
name: "Case 01",
vidDir: vidDir,
subDir: subDir,
name: "Not copy subs",
cpy: false,
wantErr: false,
},
{
name: "Copy subs",
cpy: true,
wantErr: false,
},
} {
tc := tc
tc.vidDir = filepath.Join(exampleDir, tc.name, vidDir)
tc.subDir = filepath.Join(exampleDir, tc.name, subDir)

genVidSubFiles(tc.name)

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

err := episode.AutoRename(tc.vidDir, tc.subDir)
err := episode.AutoRename(tc.vidDir, tc.subDir, tc.cpy)

if tc.wantErr {
require.Error(t, err)
Expand Down

0 comments on commit 658adbd

Please sign in to comment.