tagtoname
Renames audio or video files using their metadata tags
- Renames to names safe for URLs and filenames by default, by transliterating or deleting unsafe characters
- Allows configuring the tags, their case, and the separator characters to use when renaming
- Has a dry run option (noop), so you can see the results before renaming the files
- Uses ffprobe, so it supports most formats and containers, and also it lets you specify the ffprobe binary and its flags
Installing
Make sure you have ffprobe installed (it comes with FFmpeg), and run:
npm install tagtoname
CLI
Usage: tagtoname [-k] [-m number] [-n] [-o option]... [-p path] [-s separator]
[-t tag]... path...
Renames the files at path(s) to a URL-safe name using the metadata tag(s).
Options:
-k, --keep-case Keep the case from the tags when renaming
-m, --max=MAX Run at most MAX ffprobe tasks concurrently;
defaults to -m 32
-n, --noop Dry run, show new paths without renaming the files
-o, --option=OPTION Read metadata with ffprobe OPTION(s);
defaults to -o-show_format -o-show_streams
-p, --path=PATH Read metadata with the ffprobe binary at PATH;
defaults to -p ffprobe
-s, --separator=SEPARATOR Split tags with SEPARATOR;
defaults to -s-
-t, --tag=TAG Append TAG(s) to the new name;
defaults to -t ARTIST -t artist -t TITLE -t title
--help Show help
--version Output the version number
For example, by default a file with the "mp3" ext, the ARTIST tag "Beethoven",
and the TITLE tag "Ode to Joy" is renamed to "beethoven-ode-to-joy.mp3".
Examples
tagtoname file.mp3
tagtoname folder
tagtoname -k file.mp3
tagtoname -m 16 folder
tagtoname -n folder
tagtoname -o-show_streams -o-select_streams -ov file.mp4
tagtoname -p bin/ffprobe-4.1.1 file.mp3
tagtoname -s _ file.mp3
tagtoname -t title -t year file.mp4
API
tagtoname(paths, options)
Renames the audio or video files using their metadata tags.
The given paths
can be files or directories, in which case it recursively
traverses them.
The second argument is an options object with the following properties:
keepCase
: Keep the case from the tags when renaming, defaults to false
max
: The maximum amount of concurrent ffprobe tasks to spawn, defaults to 32
noop
: Whether to perform a dry run and not rename files, defaults to false
options
: An array of ffprobe options to pass to ffprobe, defaults to ["-show_streams", "-show_format"]
path
: The path of the ffprobe binary used to read metadata, defaults to "ffprobe"
separator
: The separator used to split the tags in the name, defaults to "-"
tags
: An array of tags to use in the new name of the file, defaults to ["ARTIST", "artist", "TITLE", "title"]
dest
: A custom function, which can be either sync or async, that returns the destination path of a rename (the new path), with signature (oldPath, ffprobeJSON, tags, keepCase, separator) => newPath
, where oldPath
is the old path (string) of the file, ffprobeJSON
is the JSON object returned by ffprobe, the above options tags
, keepCase
, and separator
are the rest of arguments, the returned newPath
is the new path (string) of the file, and if it throws or rejects an "error"
event is emitted with the thrown error or rejection value
Returns an EventEmmiter
object with the following events:
"rename"
, emitted when the new path is different from the old path, passing the new path (string) to the callback"same"
, emitted when the new path is the same as the old path, passing the old path (string) to the callback"error"
, emitted when a file cannot be renamed, passing the Error
object to the callback"done"
, emitted when all files have been processed
Examples
import tagtoname from "tagtoname";
tagtoname(["/path/to/folder/"], { tags: ["artist", "title"] })
.on("rename", newPath => console.error(newPath))
.on("same", oldPath => console.log(`${oldPath} kept as is`))
.on("error", error => console.error(error.message))
.on("done", () => console.log("Done"));
The API has the same options as the CLI, except that the API has an additional option: dest
.
The dest
option is a hook that runs before the rename and returns the new path that will be the destination of the rename. You may also use it to do other work before the rename, such as creating directories or copying files.
Note that the dest
function can be either sync or async, and if it throws or
rejects an "error"
event with the error will be emitted.
Hooking into the renaming logic with the dest
option:
import { mkdir } from "fs";
import { promisify } from "util";
import { dirname, join } from "path";
import tagtoname from "tagtoname";
const mkdirPromise = promisify(mkdir);
tagtoname(["/path/to/folder/"], {
dest: (oldPath, { format: { tags: { ARTIST, TITLE } = {} } = {} }) => {
const dir = join(dirname(oldPath), ARTIST.replace(/\s+/giu, "-"));
const newPath = join(dir, `${TITLE.replace(/\s+/giu, "-")}.flac`);
return mkdirPromise(dir).then(
() => newPath,
error => (error.code === "EEXIST" ? newPath : Promise.reject(error))
);
}
});
License
MIT License