executor.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // Go CLI tool to extract things from filenames and call external commands. Just because doing it with sed/awk/grep is too cumbersome!
  2. //
  3. // Licensed under a MIT license (https://gwyneth-llewelyn.mit-license.org/)
  4. package main
  5. import (
  6. "fmt"
  7. "regexp"
  8. "os/exec"
  9. // "strings"
  10. "github.com/karrick/godirwalk"
  11. "github.com/karrick/golf" // flag replacement library
  12. )
  13. // instantiate a few global variables, to avoid memory leaks.
  14. var (
  15. digit, artist, song int // indexes inside the searched string with matched content.
  16. dirPath string // path where the files are
  17. mp4tags string // path to mp4tags executable
  18. recursive bool // if set, walks through directories recursively
  19. help bool // if set, show usage
  20. )
  21. func main() {
  22. // read flags, if any
  23. golf.StringVarP(&dirPath, 'd', "dir", ".", "Directory to read from")
  24. golf.StringVarP(&mp4tags, 'm', "mp4tags", "/usr/local/bin/mp4tags", "Path to `mp4tags` executable")
  25. golf.BoolVarP(&recursive, 'r', "recursive", false, "Traverse directory structure recursively")
  26. golf.BoolVarP(&help, 'h', "help", false, "show command usage")
  27. golf.Parse()
  28. if help {
  29. golf.Usage()
  30. return
  31. }
  32. // check of we can run mp4tags:
  33. path, err := exec.LookPath(mp4tags) // also search under the $PATH shell
  34. if err != nil {
  35. panic(fmt.Sprintf("couldn't find executable `mp4tags` at %q, error was: %s", mp4tags, err))
  36. }
  37. fmt.Printf("`mp4tags` is available at %s\n", path)
  38. fmt.Printf("Now entering directory %q (recursive: %t)...\n\n", dirPath, recursive)
  39. // prepare a regexp
  40. re := regexp.MustCompile(`\/(?P<digit>\d\d)\s+(?P<artist>[^-]*)\s+-\s+(?P<song>[^-]*)\.m4a`)
  41. err = godirwalk.Walk(dirPath, &godirwalk.Options{
  42. Callback: func(osPathname string, de *godirwalk.Dirent) error {
  43. // skip directories/symlinks to directories (if not in recursive mode)
  44. isDir, dirErr := de.IsDirOrSymlinkToDir();
  45. if isDir && !recursive {
  46. if dirErr == nil {
  47. // return godirwalk.SkipThis
  48. return nil
  49. }
  50. fmt.Printf("error when trying to access directory/symlink %q: %s",
  51. osPathname, dirErr)
  52. return nil
  53. }
  54. matches := re.FindStringSubmatch(osPathname)
  55. howManyMatches := len(matches)
  56. //
  57. // if howManyMatches > 0 {
  58. // fmt.Printf("Attempting to parse %q, found %d matches\n",
  59. // osPathname,
  60. // howManyMatches)
  61. // }
  62. digit = re.SubexpIndex("digit")
  63. artist = re.SubexpIndex("artist")
  64. song = re.SubexpIndex("song")
  65. if howManyMatches > 2 && digit >= 0 && artist >= 0 && song >= 0 {
  66. fmt.Printf("Song %s --> Artist: %q Song: %q -->",
  67. matches[digit],
  68. matches[artist],
  69. matches[song])
  70. // Call mp4tags with the proper tags
  71. cmd := exec.Command(mp4tags, "-artist", matches[artist], "-song", matches[song], osPathname)
  72. stdoutStderr, err := cmd.CombinedOutput()
  73. if err != nil {
  74. fmt.Printf("error while running %q: %q \n", mp4tags, err)
  75. return nil
  76. }
  77. fmt.Printf("✅ %s\n", stdoutStderr)
  78. return nil
  79. }
  80. if howManyMatches <= 0 {
  81. fmt.Printf("The filename %q is not in the proper format (skipping)...\n", osPathname)
  82. return nil
  83. }
  84. fmt.Printf("The filename %q does not match anything at all!\n", osPathname)
  85. //return godirwalk.SkipThis
  86. return nil
  87. },
  88. Unsorted: false, // (optional) set true for faster yet non-deterministic enumeration (see godoc)
  89. })
  90. if err != nil {
  91. fmt.Printf("sorry, got error %s", err)
  92. }
  93. }