// Go CLI tool to extract things from filenames and call external commands. Just because doing it with sed/awk/grep is too cumbersome! // // Licensed under a MIT license (https://gwyneth-llewelyn.mit-license.org/) package main import ( "fmt" "regexp" "os/exec" // "strings" "github.com/karrick/godirwalk" "github.com/karrick/golf" // flag replacement library ) // instantiate a few global variables, to avoid memory leaks. var ( digit, artist, song int // indexes inside the searched string with matched content. dirPath string // path where the files are mp4tags string // path to mp4tags executable recursive bool // if set, walks through directories recursively help bool // if set, show usage ) func main() { // read flags, if any golf.StringVarP(&dirPath, 'd', "dir", ".", "Directory to read from") golf.StringVarP(&mp4tags, 'm', "mp4tags", "/usr/local/bin/mp4tags", "Path to `mp4tags` executable") golf.BoolVarP(&recursive, 'r', "recursive", false, "Traverse directory structure recursively") golf.BoolVarP(&help, 'h', "help", false, "show command usage") golf.Parse() if help { golf.Usage() return } // check of we can run mp4tags: path, err := exec.LookPath(mp4tags) // also search under the $PATH shell if err != nil { panic(fmt.Sprintf("couldn't find executable `mp4tags` at %q, error was: %s", mp4tags, err)) } fmt.Printf("`mp4tags` is available at %s\n", path) fmt.Printf("Now entering directory %q (recursive: %t)...\n\n", dirPath, recursive) // prepare a regexp re := regexp.MustCompile(`\/(?P\d\d)\s+(?P[^-]*)\s+-\s+(?P[^-]*)\.m4a`) err = godirwalk.Walk(dirPath, &godirwalk.Options{ Callback: func(osPathname string, de *godirwalk.Dirent) error { // skip directories/symlinks to directories (if not in recursive mode) isDir, dirErr := de.IsDirOrSymlinkToDir(); if isDir && !recursive { if dirErr == nil { // return godirwalk.SkipThis return nil } fmt.Printf("error when trying to access directory/symlink %q: %s", osPathname, dirErr) return nil } matches := re.FindStringSubmatch(osPathname) howManyMatches := len(matches) // // if howManyMatches > 0 { // fmt.Printf("Attempting to parse %q, found %d matches\n", // osPathname, // howManyMatches) // } digit = re.SubexpIndex("digit") artist = re.SubexpIndex("artist") song = re.SubexpIndex("song") if howManyMatches > 2 && digit >= 0 && artist >= 0 && song >= 0 { fmt.Printf("Song %s --> Artist: %q Song: %q -->", matches[digit], matches[artist], matches[song]) // Call mp4tags with the proper tags cmd := exec.Command(mp4tags, "-artist", matches[artist], "-song", matches[song], osPathname) stdoutStderr, err := cmd.CombinedOutput() if err != nil { fmt.Printf("error while running %q: %q \n", mp4tags, err) return nil } fmt.Printf("✅ %s\n", stdoutStderr) return nil } if howManyMatches <= 0 { fmt.Printf("The filename %q is not in the proper format (skipping)...\n", osPathname) return nil } fmt.Printf("The filename %q does not match anything at all!\n", osPathname) //return godirwalk.SkipThis return nil }, Unsorted: false, // (optional) set true for faster yet non-deterministic enumeration (see godoc) }) if err != nil { fmt.Printf("sorry, got error %s", err) } }