diff options
author | Franck Cuny <franck@fcuny.net> | 2024-03-06 06:29:24 -0800 |
---|---|---|
committer | Franck Cuny <franck@fcuny.net> | 2024-03-06 06:29:24 -0800 |
commit | 1e4a5aa09c1c8f43722c9c260f011398799a8e8f (patch) | |
tree | cd73e0fb8ba53bd21cee6ccf2dcc85639bbbb93f /tools/music-organizer | |
parent | set correct git email in the profiles (diff) | |
download | world-1e4a5aa09c1c8f43722c9c260f011398799a8e8f.tar.gz |
rename `tools` to `packages` to follow convention
The convention is to use `pkgs` or `packages` for overlays and definition of custom packages. Since I'm already using `pkg` for go, I prefer to use `packages` for my scripts.
Diffstat (limited to 'tools/music-organizer')
-rw-r--r-- | tools/music-organizer/README.org | 21 | ||||
-rw-r--r-- | tools/music-organizer/default.nix | 15 | ||||
-rw-r--r-- | tools/music-organizer/go.mod | 5 | ||||
-rw-r--r-- | tools/music-organizer/go.sum | 4 | ||||
-rw-r--r-- | tools/music-organizer/main.go | 271 |
5 files changed, 0 insertions, 316 deletions
diff --git a/tools/music-organizer/README.org b/tools/music-organizer/README.org deleted file mode 100644 index a42a196..0000000 --- a/tools/music-organizer/README.org +++ /dev/null @@ -1,21 +0,0 @@ -#+TITLE: music organizer - -the tool takes a couple of arguments: -- ~-dest~: where will the music be stored -- a list of directories to scan - -all files that have tags that can be read will be processed and moved to the specified destination. - -files are organized like this: ={artist}/{album}/{track number} {track title}.{track format}= - -the tool ensures that files are not already present in the destination. if there's already a file with the same name, it checks that the md5 sum of the files are identical. if they are not, it logs a message. - -* build -#+BEGIN_SRC sh -go build -#+END_SRC - -* install -#+BEGIN_SRC sh -go install -#+END_SRC diff --git a/tools/music-organizer/default.nix b/tools/music-organizer/default.nix deleted file mode 100644 index 1242e34..0000000 --- a/tools/music-organizer/default.nix +++ /dev/null @@ -1,15 +0,0 @@ -{ pkgs, ... }: - -pkgs.buildGoModule rec { - name = "music-organizer"; - src = ./.; - vendorSha256 = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo="; - nativeBuildInputs = with pkgs; [ go ]; - - meta = with pkgs.lib; { - description = "CLI to organize my music in folders."; - license = licenses.mit; - platforms = platforms.linux; - maintainers = [ ]; - }; -} diff --git a/tools/music-organizer/go.mod b/tools/music-organizer/go.mod deleted file mode 100644 index ba9a1b8..0000000 --- a/tools/music-organizer/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module golang.fcuny.org/music-organizer - -go 1.17 - -require github.com/dhowden/tag v0.0.0-20220617232555-e66a190c9f5b diff --git a/tools/music-organizer/go.sum b/tools/music-organizer/go.sum deleted file mode 100644 index 3383f0e..0000000 --- a/tools/music-organizer/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/dhowden/itl v0.0.0-20170329215456-9fbe21093131/go.mod h1:eVWQJVQ67aMvYhpkDwaH2Goy2vo6v8JCMfGXfQ9sPtw= -github.com/dhowden/plist v0.0.0-20141002110153-5db6e0d9931a/go.mod h1:sLjdR6uwx3L6/Py8F+QgAfeiuY87xuYGwCDqRFrvCzw= -github.com/dhowden/tag v0.0.0-20220617232555-e66a190c9f5b h1:TG8R5ZZgd1Sj7iFWnkk5dNy94RG8fP8M4l24UYR8/HY= -github.com/dhowden/tag v0.0.0-20220617232555-e66a190c9f5b/go.mod h1:Z3Lomva4pyMWYezjMAU5QWRh0p1VvO4199OHlFnyKkM= diff --git a/tools/music-organizer/main.go b/tools/music-organizer/main.go deleted file mode 100644 index 253afef..0000000 --- a/tools/music-organizer/main.go +++ /dev/null @@ -1,271 +0,0 @@ -package main - -import ( - "archive/zip" - "crypto/md5" - "encoding/hex" - "flag" - "fmt" - "io" - "io/ioutil" - "log" - "os" - "path/filepath" - "strings" - - "github.com/dhowden/tag" -) - -const ( - // the max lenght for a track can only be 255 characters minus 3 for the - // track number (followed by a space), and 4 for the format. The limit of - // 255 is coming from HFS+. - TrackTitleMaxLenght = 255 - 3 - 4 -) - -var musicDest = flag.String("dest", fmt.Sprintf("%s/media/music", os.Getenv("HOME")), "where to store the music") - -// replace slashes with dashes -func stripSlash(s string) string { - return strings.ReplaceAll(s, "/", "-") -} - -// return the name of the artist, album and the title of the track -// the title of the track has the following format: -// -// {track #} {track title}.{track format} -func generatePath(m tag.Metadata) (string, string, string) { - var artist, album, title string - var track int - - // if there's no artist, let's fallback to "Unknown Artists" - if len(m.Artist()) == 0 { - artist = "Unknown Artists" - } else { - artist = stripSlash(m.Artist()) - } - - // if there's no album name, let's fallback to "Unknown Album" - if len(m.Album()) == 0 { - album = "Unknown Album" - } else { - album = stripSlash(m.Album()) - } - - track, _ = m.Track() - - // ok, there must be a better way - format := strings.ToLower(string(m.FileType())) - - title = fmt.Sprintf("%02d %s.%s", track, stripSlash(m.Title()), format) - if len(title) > TrackTitleMaxLenght { - r := []rune(title) - title = string(r[0:255]) - } - - return artist, album, title -} - -// create all the required directories. if we fail to create one, we die -func makeParents(path string) error { - if err := os.MkdirAll(path, 0o777); err != nil { - return fmt.Errorf("failed to create %s: %v", path, err) - } - return nil -} - -func md5sum(path string) (string, error) { - var sum string - f, err := os.Open(path) - if err != nil { - return sum, err - } - - defer f.Close() - - h := md5.New() - if _, err := io.Copy(h, f); err != nil { - return sum, err - } - sum = hex.EncodeToString(h.Sum(nil)[:16]) - return sum, nil -} - -func makeCopy(src, dst string) error { - f, err := os.Open(src) - if err != nil { - return err - } - defer f.Close() - - t, err := os.OpenFile(dst, os.O_RDWR|os.O_CREATE, 0o666) - if err != nil { - return err - } - defer t.Close() - - _, err = io.Copy(t, f) - if err != nil { - return err - } - log.Printf("copied %s → %s\n", src, dst) - return nil -} - -// ensure the file is named correctly and is moved to the correct destination -// before we can do that, we need to: -// 1. check if the track already exists, if it does, does it have the same md5 ? -// if they are similar, we skip them. if they are not, we log and don't do -// anything -// 2. we can move the file to the destination -// 3. we can delete the original file -func renameFile(originalPath string, artist, album, title string) error { - directories := filepath.Join(*musicDest, artist, album) - destination := filepath.Join(directories, title) - - // check if the file is present - _, err := os.Stat(destination) - if err == nil { - var originalSum, destinationSum string - if originalSum, err = md5sum(originalPath); err != nil { - return err - } - if destinationSum, err = md5sum(destination); err != nil { - return err - } - - if destinationSum != originalSum { - log.Printf("md5 sum are different: %s(%s) %s(%s)", originalPath, originalSum, destination, destinationSum) - } - return nil - } - - if err := makeParents(directories); err != nil { - return err - } - - if err := makeCopy(originalPath, destination); err != nil { - return err - } - - // TODO delete original file - // os.Remove(originalPath) - return nil -} - -// we try to open any files and read the metadata. -// if the file has metadata we can read, we will try to move the file to the -// correct destination -func processFile(path string) error { - f, err := os.Open(path) - if err != nil { - return err - } - - defer f.Close() - m, err := tag.ReadFrom(f) - if err != nil { - // this is fine, this might not be a music file - log.Printf("SKIP failed to read tags from %s: %v", path, err) - return nil - } - - var artist, album, title string - artist, album, title = generatePath(m) - if err := renameFile(path, artist, album, title); err != nil { - return fmt.Errorf("failed to move %s: %v", path, err) - } - return nil -} - -func processPath(path string, f os.FileInfo, err error) error { - if stat, err := os.Stat(path); err == nil && !stat.IsDir() { - if err := processFile(path); err != nil { - return err - } - } - return nil -} - -// unzip takes two paths, a source and destination. The source is the -// name of the archive and we will extract the content into the -// destination directory. The destination directory has to already -// exists, we are not going to create it here or delete it at the end. -func unzip(src, dst string) error { - r, err := zip.OpenReader(src) - if err != nil { - return err - } - - defer r.Close() - - for _, f := range r.File { - fpath := filepath.Join(dst, f.Name) - outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - return err - } - - rc, err := f.Open() - if err != nil { - return err - } - - _, err = io.Copy(outFile, rc) - if err != nil { - log.Printf("failed to copy %s: %s", outFile.Name(), err) - } - - outFile.Close() - rc.Close() - } - return nil -} - -func main() { - flag.Parse() - - if *musicDest == "" { - log.Fatal("-dest is required") - } - - paths := make([]string, flag.NArg()) - - // For our temp directory, we use what ever the value of - // XDG_RUNTIME_DIR is. If the value is unset, we will default to - // the system default temp directory. - tmpDir := os.Getenv("XDG_RUNTIME_DIR") - - for i, d := range flag.Args() { - if filepath.Ext(d) == ".zip" { - // If we have an extension and it's '.zip', we consider the - // path to be an archive. In this case we want to create a new - // temporary directory and extract the content of the archive - // in that path. The temporary directory is removed once we're - // done. - out, err := ioutil.TempDir(tmpDir, "music-organizer") - if err != nil { - log.Printf("failed to create a temp directory to extract %s: %v", d, err) - continue - } - defer os.RemoveAll(out) - - if err := unzip(d, out); err != nil { - log.Printf("failed to extract %s: %v", d, err) - continue - } - paths[i] = out - } else { - paths[i] = d - } - } - - for _, d := range paths { - // XXX deal with filenames that are too long - // scan the directory and try to find any file that we want to move - err := filepath.Walk(d, processPath) - if err != nil { - log.Fatalf("error while processing files: %v", err) - } - } -} |