SYNOPSIS

  # Print the information in tags and autodeduced info
  mp3info2 *.mp3

  # In addition, set the year field to 1981
  mp3info2 -y 1981 *.mp3

  # Same without printout of info, recursively in the current directory
  mp3info2 -R -p "" -y 1981 .

  # Do not deduce any field, print (normalized) info from the tags only
  mp3info2 -C autoinfo=ID3v2,ID3v1 *.mp3

  # As above, but without normalization/autofill, the raw information in tags
  mp3info2 -N *.mp3

  # As above, but only with ID2v1 tag read
  mp3info2 -NC autoinfo=ID3v1 *.mp3

  # Get artist from CDDB_File, autodeduce other info, write it to tags
  mp3info2 -C artist=CDDB_File -u *.mp3

  # For title, prefer information from .inf file; autodeduce rest, update
  mp3info2 -C title=Inf,ID3v2,ID3v1,filename -u *.mp3

  # Same, and get the artist from CDDB file
  mp3info2 -C title=Inf,ID3v2,ID3v1,filename -C artist=CDDB_File -u *.mp3

  # Write a script for conversion of .wav to .mp3, autodeducing tags
  mp3info2 -p "lame -h --vbr-new --tt '%t' --tn %n --ta '%a' --tc '%c' --tl '%l' --ty '%y' '%f'\n" *.wav >xxx.sh

DESCRIPTION

The program prints a message summarizing tag info (obtained via MP3::Tag module) for specified files.

It may also update the information in \s-1ID3\s0 tags. This happens in three different cases.

  • If the information supplied in command-line options \*(C`t a l y g c n\*(C' differs from the content of the corresponding \s-1ID3\s0 tags (or there is no corresponding \s-1ID3\s0 tags).

  • If options \*(C`-d\*(C' or \*(C`-F\*(C' were given.

  • if \*(C`MP3::Tag\*(C' obtains the info from other means than \s-1MP3\s0 tags, and \*(C`-u\*(C' forces the update of the \s-1ID3\s0 tags.

(All these ways are disabled by \*(C`-D\*(C' option.) ID3v2 tag is written if needed, or if \*(C`-2\*(C' option is given. (Automatic fill-in of deduceable fields (via the method id3v2_frames_autofill()) is performed unless \*(C`-d\*(C' or \*(C`-N\*(C' options are given.)

The option \*(C`-u\*(C' writes (\*(C`u\*(C'pdates) the fetched information to the \s-1MP3\s0 \s-1ID3\s0 tags. This option is assumed if there are command-line options which explicitly set tag elements (\*(C`-a\*(C', \*(C`-t\*(C' etc., and \*(C`-F\*(C', \*(C`-d\*(C'). (Effects of this option may be overridden by giving \*(C`-D\*(C' option.) If \*(C`-2\*(C' option is also given, forces write of ID3v2 tag even if the info fits the ID3v1 tag (in addition, this option enables auto-update of \*(L"personal name\*(R" fields, and corresponding titles according to values of \*(C`translate_person\*(C', \*(C`person_frames\*(C' etc. configuration settings; see \*(L"Normalization of fields\*(R"). This option is ignored if no change to tags is detected; however, one can force an update by repeating this option (useful if you expect the change the \*(L"format\*(R" of the tag, as opposed to its \*(L"content\*(R").

The option \*(C`-p\*(C' prints a message using the next argument as format (by default \*(C`\\\*(C', \*(C`\t\*(C', \*(C`\n\*(C' are replaced by backslash, tab and newline; governed by the value of \*(C`-E\*(C' option); see \*(L"interpolate\*(R" in MP3::Tag for details of the format of sprintf()-like escapes. If no option \*(C`-p\*(C' is given, message in default format will be emitted. The value of option \*(C`-e\*(C' is the encoding used for the output; if the value is a number, system-specific encoding is guessed (and used for the output if bit 0x1 is set); if bit 0x2 is set, then, command line options are assumed to be in the guessed encoding; if bit 0x4 is set, then, command line arguments are assumed to be in the guessed encoding. Use the value \*(C`binary\*(C' to do binary output.

With option \*(C`-D\*(C' (dry run) no update is performed, no matter what the other options are. With this option, no parsing of tags is performed unless needed.

Use options

t a l y g c n

to overwrite the information (title artist album year genre comment track-number) obtained via \*(C`MP3::Tag\*(C' heuristics (\*(C`-u\*(C' switch is implied if any one of these arguments differs from what would be found otherwise; use \*(C`-D\*(C' switch to disable auto-update). By default, the values of these options are not \*(C`%\*(C'-interpolated; this may be changed by \*(C`-E\*(C' option.

The option \*(C`-d\*(C' should contain the comma-separated list of ID3v2 frames to delete. A frame specification is the same as what might be given to "%{...}" frame interpolation command, e.g., \*(C`TIT3\*(C', \*(C`COMM03\*(C', \*(C`COMM(fra)[short title]\*(C'; the difference with modify-access is that \s-1ALL\s0 (and not the first of) matching frames are deleted. (Option -d may be repeated.)

For example, \*(C`-d APIC\*(C' would remove all picture frames. In addition, if the list contains \*(C`ID3v1\*(C' or \*(C`ID3v2\*(C', whole tags will be deleted.

Likewise, the option \*(C`-F\*(C' allows setting of arbitrary \*(C`ID3v2\*(C' frames: if one needs to set one frame, use the directive \*(C`FRAME_spec=VALUE\*(C':

-F TIT2=The_new_Title

Again, on modify, \s-1ALL\s0 matching frames are deleted first, so be carefull with

-F COMM=MyComment

Option \*(C`-F\*(C' may be repeated to set more than one frame. If configuration variable \*(C`empty-F-deletes\*(C' is \s-1TRUE\s0 (default), empty arguments will delete the frame.

One can replace \*(C`FRAME_spec=VALUE\*(C' by \*(C`FRAME_spec < FILE\*(C'; in this case the value to set is read from the file named \s-1FILE\s0; if the frame is text-only (meaning: at most \*(C`[encoded]Text URL Language Description\*(C' fields are present), the file is read in text mode (and with starting/trailing whitespace stripped), otherwise it is read in binary mode. (Whitespace is required about the \*(C`<\*(C' signs.) If \*(C`<\*(C' is replaced by \*(C`?<\*(C', the value is set only if frame is not yet present, and if the file exists; if replaced by \*(C`>\*(C', the value (if present) is written to \s-1FILE\s0 (creation of intermediate directories is controlled by configuration option \*(C`frames_write_creates_dirs\*(C', the default is \s-1FALSE\s0).

Additionally, \*(C`FRAME_spec\*(C' may be one of \*(C`ID3v1\*(C' or \*(C`ID3v2\*(C' or \*(C`TAGS\*(C'; in this case, whole tags are written or read. For example, for \*(C`TAGS < FILE\*(C', \*(C`title artist album year genre comment track\*(C' info is calculated from \s-1FILE\s0, which may be raw tags, as produced with \*(C`>\*(C', or a valid \s-1MP3\s0 file; if Image::ExifTool is present, the data may be read from arbitrary multimedia file. (Likewise, for \*(C`ID3v1 < FILE\*(C', the same info is extracted from \*(C`ID3v1\*(C' tag only.) After this, in case of \*(C`ID3v2\*(C' or \*(C`TAGS\*(C', \*(C`ID3v2\*(C' frames are copied from the \*(C`ID3v2\*(C' tag one-by-one. (With suitable modifications for \*(C`?<\*(C'.)

By default, the \*(L"\s-1VALUE\s0\*(R" for \*(C`-F\*(C' is \*(C`%\*(C'-interpolated; this can be changed by option \*(C`-E\*(C'. For user convenience, human-friendlier forms \*(C`composer, text_by, orchestra, conductor, disk_n\*(C' can be used instead of \*(C`TCOM, TEXT, TPE2, TPE3, TPOS\*(C'.

The option \*(C`-P RECIPE\*(C' is a very powerful generalization of what can be done by options \*(C`-F\*(C', \*(C`-d\*(C', and \*(C`-t -a -l -y -g -c -n\*(C'. It may be repeated; the values should contain the parse recipes. They become the configuration item \*(C`parse_data\*(C' of \*(C`MP3::Tag\*(C'; eventually this information is processed by MP3::Tag::ParseData module (if the latter is present in the chain of heuristics; see option \*(C`-C\*(C'). The \*(C`RECIPE\*(C' is split into \*(C`$flags, $string, @patterns\*(C' on its first non-alphanumeric character; the first of @patterns which matches $string is going to be executed (for side effects). (See examples: \*(L"\s-1EXAMPLES:\s0 parse rules\*(R".)

If option \*(C`-G\*(C' is specified, the file names on the command line are considered as glob patterns. This may be useful if the maximal command-line length is too low. With the option \*(C`-R\*(C' arguments can be directories, which are searched recursively for audio (default *.mp3) files to process; use option \*(C`-r\*(C' to reset the regular expression to look for (the default is \*(C`(?i:\.mp3$)\*(C').

The option \*(C`-E\*(C' controls expansion of escape characters. It should contain the letters of the command-line options where \*(C`\\, \n, \t\*(C' are interpolated; one can append the letters of \*(C`t a l y g c n F\*(C' options requiring \*(C`%\*(C'-interpolation after the separator \*(C`/i:\*(C' (for \*(C`-F\*(C', only the values are interpolated). The default value is \*(C`p/i:Fp\*(C': only \*(C`-p\*(C' is \*(C`\\*(C'-interpolated, and only \*(C`-F\*(C' and \*(C`-p\*(C' are subject to \*(C`%\*(C'-interpolation. If all one wants is to add to the defaults, preceed the value of \*(C`-E\*(C' (containing added options) by "+". (Some parts of the value of option \*(C`-P\*(C' are interpolated, but this should be governed by flags, not \*(C`-E\*(C'; do \s-1NOT\s0 put \*(C`P\*(C' into the \*(C`%\*(C'-interpolated part of \*(C`-E\*(C'.)

If the option \*(C`-@\*(C' is given, all characters \*(C`@\*(C' in the options are replaced by \*(C`%\*(C'. This may be convenient if the shell treats \*(C`%\*(C' specially (e.g., \s-1DOSISH\s0 shells).

If option \*(C`-I\*(C' is given, no guessworking for artist field is performed on typeout.

The option \*(C`-C CONFIG_OPT=VALUE1,VALUE2...\*(C' sets \*(C`MP3::Tag\*(C' configuration data the same way as \*(C`MP3::Tag-\*(C'config()> would do (recall that the value is an array; separate elements by commas if more than one). The option may be repeated to set more than one value. Note that since \*(C`ParseData\*(C' is used to process \*(C`-P\*(C' parse recipes, it should be better be kept in the \*(C`autoinfo\*(C' configuration (and related fields \*(C`author\*(C' etc) in presence of \*(C`-P\*(C'.

If the option \*(C`-x\*(C' is given, the technical information about the audio file is printed (\s-1MP3\s0 level, duration, number of frames, padding, copyright, and the list of ID3v2 frame names in format suitable to \*(C`%{...}\*(C' escapes). If \*(C`-x\*(C' is repeated, content of frames is also printed out (may output non-printable chars, if it is repeated more than twice).

If option \*(C`-N\*(C' is given, all the \*(L"smarts\*(R" are disabled - no normalization of fields happens, and (by default) no attempt to deduce the values of fields from non-ID3 information is done. This option is (currently) equivalent to having \*(C`-C autoinfo=ParseData,ID3v2,ID3v1\*(C' as the first directive, to having no Normalize::Text::Music_Fields.pm present on @INC path, and not calling autofill() method.

Normalization of fields

(The loading of normalization module and all subsequent operations may be disabled by the option \*(C`-N\*(C', or by setting the environment variable \*(C`MP3TAG_NORMALIZE_FIELDS\*(C' to be \s-1FALSE\s0. If not prohibited, the module is attempted to be loaded if directory ~/.music_fields is present, or \*(C`MP3TAG_NORMALIZE_FIELDS\*(C' is set and \s-1TRUE\s0.)

If loading of the module \*(C`Normalize::Text::Music_Fields\*(C' is successful, the following is applicable:

If the value of \*(C`MP3TAG_NORMALIZE_FIELDS\*(C' is defined and not 1, this value is broken into directories as a \s-1PATH\s0, and load path of \*(C`Normalize::Text::Music_Fields\*(C' is set to be this list of directories. Then MP3::Tag is instructed (via corresponding configuration settings) to use \*(C`normalize_artist\*(C' (etc.) methods defined by this module. These methods may normalize certain tag data. The current version defines methods for \*(L"normalization\*(R" of personal names, and titles (based on the composer). This normalization is driven through user-editable configuration tables.

In addition to automatical normalization of \s-1MP3\s0 tag data, one can use \*(L"fake \s-1MP3\s0 files\*(R" to manually access some features of this module. For this, use an empty file name, and \*(C`-D\*(C' option. E.g,

mp3info2 -D -a beethoven -p "%a\n" "" mp3info2 -D -a beethoven -p "%{shP[%a]}\n" "" mp3info2 -D -a beethoven -t "sonata #28" -p "%t\n" "" mp3info2 -D -a beethoven -t "allegretto, Bes" -@p "@t\n" "" mp3info2 -D -a beethoven -t "op93" -@p "@t\n" ""

will print the normalized person-name for \*(C`beethoven\*(C', the corresponding normalized short person-name, and the normalized title for \*(C`sonata #28\*(C' of composer \*(C`beethoven\*(C'. E.g., with the shipped normalization tables, it will print

Ludwig van Beethoven (1770-1827) L. van Beethoven Piano Sonata No. 28 in A major; Op. 101 (1816) Allegretto for Piano Trio in B flat major; WoO 39 (1812) Symphony No. 8 in F major; Op. 93 (comp. 1812, f.p. Vienna, 1814-02-27, cond. Beethoven; pubd. 1816)

The order of operation

Currently, the operations are done in the following order

  • Deletion of ID3v1 or ID3v2 as a whole via \*(C`-d\*(C' option;

  • Recipies of \*(C`-P\*(C' option are set up (to be triggered by interpolation);

  • The setting done via \*(C`-a/-t/-l/-y/-g/-c/-n\*(C' options;

  • The settings done via \*(C`-F\*(C' option;

  • Deletion of individual frames via \*(C`-d\*(C' option;

  • autofill of ID3v2 (id) frames;

  • Emit info based on \*(C`-p\*(C' and \*(C`-x\*(C' options;

  • Trigger recipies of \*(C`-P\*(C' (if not triggered by interpolation);

  • Update tags if needed.

Usage strategy: escalation of complexity

The purpose of this script is to to make handling of \s-1ID3\s0 tags as simple as possible.

On one end of the scale, one can perform arbitrarily complex manipulations with tags using \*(C`MP3::Tag\*(C' Perl module.

On the other end, it is much more convenient to handle simplest manipulations with tags using this script's options \*(C`-t -a -l -y -g -c -n\*(C' and \*(C`-p -F -d\*(C'. For slightly more complicated tasks, one may need to use the more elaborate method of parse rules, provided to this script by the option \*(C`-P\*(C'; the rules depend heavily on interpolation, see \*(L"interpolate\*(R" in MP3::Tag, \*(L"interpolate_with_flags\*(R" in MP3::Tag.

To simplify upgrade from \*(L"simplest manipulations\*(R" to \*(L"more elaborate ones\*(R", here we provide \*(L"parse rule\*(R" synonyms to the simplest options. So if you start with \*(C`-t -a -l -y -g -c -n\*(C' and \*(C`-p -F -d\*(C' options which \*(L"almost work\*(R" for you, you have a good chance to be able to fully achieve your aim by modifying the synonyms described below.

(Below we assume that \*(C`-E\*(C' option is set to its default value, so \*(C`-F -p\*(C' are \*(C`%\*(C'-interpolated, other options are not. Note also that if your \s-1TTY\s0's encoding is recognized by Perl, it is highly recommended to set \*(C`-e 3\*(C' option; on \s-1DOSISH\s0 shells, better use \*(C`-@\*(C', and replace \*(C`%\*(C''s by \*(C`@\*(C''s below.) -P "mz/VALUE/%t" Likewise. -P "mzi/VALUE/%{TIT2}" -F "APIC[myDescr]=%{I(fimbB)FILE}" or -P "mzi/%{I(fimbB)FILE}/%{APIC[myDescr]}" (remove \*(C`bB\*(C' for text-only frames). -P "bOi,%{APIC[myDescr]},FILE" (remove \*(C`b\*(C' for text-only frames); or use \*(C`-e binary -p "%{APIC[myDescr]}"\*(C' with redirection, see \*(L"\s-1EXAMPLES:\s0 parse rules\*(R". -P "m//%{TIT2}" Very tricky. This won't set distinguish empty file and non-existing one: -P "mzi/%{TIT2:1}0%{I(fFim)FILE}/10/10%{TIT2}/0%{U1}" (add \*(C`bB\*(C' to \*(C`fFim\*(C' for non-text-only frames); the last part may be omitted if one omits the flag \*(C`m\*(C' - it is present to catch misprints only.

For details on \*(L"parse rules\*(R", see \*(L"\s-1EXAMPLES:\s0 parse rules\*(R" and \*(L"\s-1DESCRIPTION\s0\*(R" in MP3::Tag::ParseData.

EXAMPLES: parse rules

Only the \*(C`-P\*(C' option is complicated enough to deserve comments... For full details on parse rules, see \*(L"\s-1DESCRIPTION\s0\*(R" in MP3::Tag::ParseData; for full details on interpolation, see \*(L"interpolate\*(R" in MP3::Tag, \*(L"interpolate_with_flags\*(R" in MP3::Tag.

For a (silly) example, one can replace \*(C`-a Homer -t Iliad\*(C' by

-P mz=Homer=%a -P mz=Iliad=%t

A less silly example is forcing a particular way of parsing a file name via

-P "im=%{d0}/%f=%a/%n %t.%e"

It is broken into

flags string pattern1 "im" "%{d0}/%f" "%a/%n %t.%e"

The flag letters stand for interpolate, must_match. This interpolates the string "%{d0}/%f" and parses the result (which is the file name with one level of the directory part preserved) using the given pattern; thus the directory name becomes the artist, the leading numeric part - the track number, and the rest of the file name (without extension) - the title. Note that since multiple patterns are allowed, one can similarly allow for multiple formats of the names, e.g.

-P "im=%{d0}/%f=%a/%n %t.%e=%a/%t (%y).%e"

allows for the file basename to be also of the form \*(L"\s-1TITLE\s0 (\s-1YEAR\s0)\*(R". An alternative way to obtain the same results is

-P "im=%{d0}=%a" -P "im=%f=%n %t.%e=%t (%y).%e"

which corresponds to two recipies:

flags string pattern1 pattern2 "im" "%{d0}" "%a" "im" "%f" "%n %t.%e" "%t (%y).%e"

Of course, one could use

"im" "%B" "%n %t" "%t (%y)"

as a replacement for the second one.

Note that it may be more readable to set artist to \*(C`%{d0}\*(C' by an explicit asignment, with arguments similar to

-E "p/i:Fpa" -a "%{d0}"

(this value of \*(C`-E\*(C' requests \*(C`%\*(C'-interpolation of the option \*(C`-a\*(C' in addition to the default \*(C`\\*(C'-interpolation of \*(C`-p\*(C', and \*(C`%\*(C'-interpolation of \*(C`-F\*(C' and \*(C`-p\*(C'; one can shortcut it with \*(C`-E +/i:a\*(C').

To give more examples,

-P "if=%D/.comment=%c"

will read comment from the file .comment in the directory of the audio file;

-P "ifn=%D/.comment=%c"

has similar effect if the file .comment has one-line comments, one per track (this assumes the the track number can be found by other means).

Suppose that a file Parts in a directory of \s-1MP3\s0 files has the following format: it has a preamble, then has a short paragraph of information per audio file, preceded by the track number and dot:

...

12. Rezitativ. (Pizarro, Rocco)

13. Duett: jetzt, Alter, jetzt hat es Eile, (Pizarro, Rocco)

...

The following command puts this info into the title of the \s-1ID3\s0 tag (provided the audio file names are informative enough so that MP3::Tag can deduce the track number):

mp3info2 -u -C parse_split='\n(?=\d+\.)' -P 'fl;Parts;%=n. %t'

If this paragraph of information has the form \*(C`TITLE (COMMENT)\*(C' with the \*(C`COMMENT\*(C' part being optional, then use

mp3info2 -u -C parse_split='\n(?=\d+\.)' -P 'fl;Parts;%=n. %t (%c);%=n. %t'

If you want to remove a dot or a comma got into the end of the title, use

mp3info2 -u -C parse_split='\n(?=\d+\.)' \ -P 'fl;Parts;%=n. %t (%c);%=n. %t' -P 'iR;%t;%t[.,]$'

The second pattern of this invocation is converted to

['iR', '%t' => '%t[.,]$']

which essentially applies the substitution \*(C`s/(.*)[.,]$/$1/s\*(C' to the title.

Now suppose that in addition to Parts, we have a text file Comment with additional info; we want to put this info into the comment field after what is extracted from \*(C`TITLE (COMMENT)\*(C'; separate these two parts of the comment by an empty line:

mp3info2 -E C -C 'parse_split=\n(?=\d+\.)' -C 'parse_join=\n\n' \ -P 'f;Comment;%c' -P 'fl;Parts;%=n. %t' \ -P 'i;%t///%c;%t (%c)///%c' -P 'iR;%t;%t[.,]$'

This assumes that the title and the comment do not contain '///' as a substring. Explanation: the first pattern of \*(C`-P\*(C',

['f', 'Comment' => '%c'],

reads comment from the file \*(C`Comment\*(C' into the comment field; the second,

['fl', 'Parts' => '%=n. %t'],

reads a chunk of \*(C`Parts\*(C' into the title field. The third one

['i', '%t///%c' => '%t (%c)///%c']

rearranges the title and comment provided the title is of the form \*(C`TITLE (COMMENT)\*(C'. (The configuration option \*(C`parse_join\*(C' takes care of separating two chunks of comment corresponding to two occurences of %c on the right hand side.)

Finally, the fourth pattern is the same as in the preceding example; it removes spurious punctuation at the end of the title.

More examples: removing string \*(L"with violin\*(R" from the start of the comment field (removing comment altogether if nothing remains):

mp3info2 -u -P 'iz;%c;with violin%c' *.mp3

setting the artist field without letting auto-update feature deduce other fields from other sources;

mp3info2 -C autoinfo=ParseData -a "A. U. Thor" *.mp3

setting a comment field unless it it already present:

mp3info2 -u -P 'i;%c///with piano;///%c' *.mp3

The last example shows how to actually write \*(L"programs\*(R" in the language of the \*(C`-P\*(C' option: the example gives a conditional assignment. With user variables (as in \*(C`%{U8}\*(C') for temporaries, and a possibility to use regular expressions, one could provide arbitrary programmatic logic. Of course, at some level of complexity one should better switch to direct interfacing with \*(C`MP3::Tag\*(C' Perl module (use the code of this Perl script as an example!).

Here is a typical task setting \*(L"advanced\*(R" id3v2 frames: composer (\*(C`TCOM\*(C'), orchestra (\*(C`TPE2\*(C'), conductor (\*(C`TPE3\*(C'). We assume a directory tree which contains \s-1MP3\s0 files tagged with the following conventions: \*(C`artist\*(C' is actually a composer; \*(C`comment\*(C' is of one of two forms:

Performers; Orchestra; Conductor Orchestra; Conductor

To set the specific \s-1MP3\s0 frames via \*(C`-P\*(C' rules, use

mp3info2 -@P "mi/@a/@{TCOM}" \ -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R .

With \*(C`-F\*(C' options, this can be simplified as

mp3info2 -@F "TCOM=@a" -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R .

or

mp3info2 -@F "composer=@a" -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R .

To copy \s-1ID3\s0 tags of \s-1MP3\s0 files in the current directory to files in directory /tmp/mp3 with the extension .tag (and print \*(L"progress report\*(R"), use

mp3info2 -p "@N@E\n" -@P "bODi,@{ID3v2}@{ID3v1},/tmp/mp3/@N.tag" -DNR .

Since we did not use \*(C`z\*(C' flag, \s-1MP3\s0 files without tags are skipped.

Now suppose that there are two parallel file hierarchies of audio files, and of lyrics: audio files are in audio/dir_name/audio_name.mp3 with corresponding lyrics file in text/dir_name/audio_name.mp3. To attach lyrics to \s-1MP3\s0 files (in \*(C`COMM\*(C' frame with description \*(C`lyrics\*(C' in language \*(C`eng\*(C' - this is a non-standard location, see below!), call

mp3info2 -@P "fim;../text/@{d0}/@B.txt;@{COMM(eng)[lyrics]}" -Ru .

inside the directory audio. (Change \*(C`fim\*(C' to \*(C`Ffim\*(C' to ignore the audio files for which the corresponding text file does not exist.) (Of course, to follow the specifications, one should have used the field "%{USLT(eng)[]}" instead of "%{COMM(eng)[lyrics]}"; see below for variations).

Finish by a very simple example: all what the pattern

-P 'i;%t;%t'

does is removal of trailing and leading blanks from the title (which is deduced by other means).

More examples

With \*(C`-F\*(C' option, one could set the \*(C`USLT\*(C' frame as

mp3info2 -@F "USLT(eng)[] < ../text/@{d0}/@B.txt" -Ru .

Print out such a frame (in any language) with

mp3info2 -@p "@{USLT[]}\n" file.mp3

Similarly, to print out the \s-1APIC\s0 frame with empty description, use

mp3info2 -e binary -@p "@{APIC[]}" file.mp3 > output_picture_file

or (with description \*(L"cover\*(R")

mp3info2 -@P "bOi,@{APIC[cover]},output_picture_file.jpg" audio_07.mp3

To set such a frame from file xxx.gif (with the default \*(C`Picture Type\*(C', "Cover (front)", and empty description), do one of

mp3info2 -F "APIC < xxx.gif" file.mp3 mp3info2 -@F "APIC[]=@{I(fimbB)xxx.gif}" file.mp3

The difference of \*(C`APIC\*(C' and \*(C`APIC[]\*(C' is that the first removes all \*(C`APIC\*(C' frames first, and the second removes only all \*(C`APIC\*(C' frames with empty description - but arbitrary image type. So it may be more suitable to use the full specification, as in \*(C`APIC(Cover (front))[]\*(C'.

To remove \*(C`APIC\*(C' frames with empty descriptions, arbitrary \*(C`Picture Type\*(C's (and \*(C`MIME type\*(C's which may be correctly calculated by mp3info2, e.g., \*(C`TIFF/JPEG/GIF/PNG\*(C'), use

mp3info2 -d "APIC[]" file.mp3

(note that this wouldn't free disk space, unless \*(L"shrink\*(R" is forced by configuration variables). To do the same with the \*(L"Conductor\*(R" picture type only, do

mp3info2 -d "APIC(Conductor)[]" file.mp3

To scan through subdirectories, and add file cover.jpg from the directory of the file as a \*(L"default\*(R" \*(C`APIC\*(C' frame, but only if there is no \*(C`APIC\*(C' frame, and a file exists, do

mp3info2 -@F "APIC ?< @D/cover.jpg" -R .

This deletes empty frames for date, \*(C`TCOP, TENC, WXXX[], COMM(eng)[]\*(C', and removes the leading 0 from track number from \s-1MP3\s0 file in current directory:

mp3info2 -@ -E +/i:y -F "TCOP=@{TCOP}" -F "TENC=@{TENC}" -F "WXXX[]=@{WXXX[]}" -F "COMM(eng)[]=@{COMM(eng)[]}" -y "@y" -P "mi/@n/0@n/@n" *.mp3

Examples on dealing with broken encodings

One of principal weaknesses of \s-1ID3\s0 specification was that it required that data is provided in \*(C`latin-1\*(C' encoding. Since most languages in the world are not expressible in \*(C`latin-1\*(C', this lead to (majority?) of \s-1ID3\s0 tags being not standard-conforming. Newer versions of the specs fixed this shortcoming, but the damage was already done. Fortunately, this script can use abilities of \*(C`MP3::Tag\*(C' to convert from non-conforming content to a conforming one.

The following example converts ID3v2 tags which were written in (non-standard-conforming) encoding \*(C`cp1251\*(C' to be in standard-conforming encoding. For the purpose of this example, assume that ID3v1 tags are in the same encoding (and that one wants to leave them in the encoding \*(C`cp1251\*(C'); the files to process are found in the current directory and (recursively) in its subdirectories (\*(C`set\*(C' syntax for \s-1DOSISH\s0 shells):

set MP3TAG_DECODE_V1_DEFAULT=cp1251 set MP3TAG_DECODE_V2_DEFAULT=cp1251 mp3info2 -C id3v2_fix_encoding_on_write=1 -u2R .

For more information, see \*(L"\s-1ENVIRONMENT\s0\*(R" in MP3::Tag, \*(L"config\*(R" in MP3::Tag, and \*(L"\s-1CUSTOMIZATION\s0\*(R" in MP3::Tag.

INCOMPATIBILITIES with \fImp3info\fP

This tool is loosely modeled on the program mp3info; it is \*(L"mostly\*(R" backward compatible (especially when in \*(L"naive\*(R" mode via \*(C`-N\*(C'), and allows a very significant superset of functionality. Known backward incompatibilities are:

-G -h -r -d -x

Missing functionality:

-f -F -i

Incompatible \*(C`%\*(C'-escapes:

%e %E - absolutely different semantic %v - has no trailing 0s %q - has fractional part %r - is a number, not a word "Variable" for VBR %u - is one less (in presence of descriptor frame only?)

Missing \*(C`%\*(C'-escapes:

%b %G

Backslash escapes: only \*(C`\\\*(C', \*(C`\n\*(C', \*(C`\t\*(C' supported.

\*(C`-x\*(C' prints data in a different format, not all fields are present, and ID3v2 tag names are output.

ENVIRONMENT

With \*(C`-e\*(C' 1, 2 or 3, this script may consult environment variables \*(C`LC_CTYPE, LC_ALL, LANG\*(C' to deduce the current encoding. No other environment variables are directly read by this script.

Note however, that MP3::Tag module has a rich set of defaults for encoding settings settable by environment variables; see \*(L"\s-1ENVIRONMENT\s0\*(R" in MP3::Tag. So these variables affect (indirectly) how this script works.

OBSOLETE INTERFACE

If you do not understand what it is about, it is safe to ignore this announcement:

The old, pre-version=1.05 way (by triplication of a separator, without repetition of options) to provide multiple commands to \*(C`-F\*(C' and <-P> options is still supported, but is strongly discouraged. (It does not conflict with the current interface.)

AUTHOR

Ilya Zakharevich <[email protected]>.

Utilities to create CDDB file

Good \s-1CD\s0 reapers (e.g., cdda2wav with option \*(C`cddb=0\*(C') create a \s-1CDDB\s0 file with fetched information - as far as an Internet connection is present. However, if not available, other options exist.

The scripts (supplied with the distribution in ./examples) can create a \*(L"stub\*(R" \s-1CDDB\s0 file basing on:

fulltoc2fake_cddb.pl

a dump of a full \s-1TOC\s0 of a \s-1CD\s0; create one, e.g., by readcd -fulltoc dev=0,1,0 -f=audiocd

inf2fake_cddb.pl

directory of *.inf files (e.g., created by cdda2wav without Internet connection);

dir_mp3_2fake_cddb.pl

a directory of \s-1MP3\s0 files ripped from a \s-1CD\s0 (via some guesswork).

Passing this stub to the script cddb2cddb.pl, it can be transformed to a \*(L"filled\*(R" \s-1CDDB\s0 file via a connection to some online database. Use \*(C`-r\*(C' option if multiple records in the database match the \s-1CD\s0 signature.

fulltoc2fake_cddb audiocd.toc | cddb2cddb > audio.cddb inf_2fake_cddb | cddb2cddb > audio.cddb dir_mp3_2fake_cddb | cddb2cddb -r3 > audio.cddb # 3rd record

When such a \s-1CDDB\s0 file is present, it will be used by MP3::Tag module to deduce the information about an audio file. This information is (by default, transparently) used by this script.

RELATED TO mp3info2…

MP3::Tag, MP3::Tag::ParseData, audio_rename, typeset_audio_dir