1#!/usr/bin/perl -w
2
3use FindBin;
4use lib "$FindBin::Bin";
5
6use MP3::Tag 1.12;		# Need conditional %L; %{mP}
7use Getopt::Std 'getopts';
8use Config;
9use File::Path;
10
11$VERSION = '1.12';
12use strict;
13
14$Getopt::Std::STANDARD_HELP_VERSION = 1;
15my %opt;
16sub MULTIV::TIEHASH {bless \my $a, 'MULTIV'}
17sub MULTIV::STORE {shift; my $k = shift; $opt{$k} ||= []; push @{$opt{$k}}, shift}
18
19my %opt_d = (r => '(?i:\.mp3$)', E => 'p/i:Fp');
20my @oARGV = @ARGV;
21my $opts = 'c:a:t:l:n:g:y:uDp:C:P:E:G@Rr:I2e:d:F:xN';
22my %o;
23tie %o, 'MULTIV';
24exec 'perldoc', '-F', $0 unless @ARGV;
25
26sub massage_o {
27  getopts($opts, \%o);
28  for my $o (keys %opt) {
29    if (-1 == index $opts, "$o:") {
30      $opt{$o} = @{$opt{$o}};		# Number of occurences
31    } elsif ($o =~ /[PFCd]/) {		# Keep as is
32    } else {
33      die "Multiple option `-$o' not supported" if @{$opt{$o}} > 1;
34      $opt{$o} = $opt{$o}[0];
35    }
36  }
37  %opt = (%opt_d, %opt);
38}
39massage_o();
40
41sub my_decode($$) {	# If file names are utf-ized, glob fails???
42  # De-utf-ize if possible...
43  join '', map chr ord, split //, &Encode::decode;
44}
45
46sub my_decode_deep($$);
47sub my_decode_deep($$) {
48  my($e,$t) = (shift, shift);
49  if (ref $t eq 'ARRAY') {
50    return [map my_decode_deep($e, $_), @$t];
51  } elsif (ref $t) {
52    die "panic: reference of type `$t' unexpected"
53  }
54  # De-utf-ize if possible...
55  join '', map chr ord, split //, Encode::decode($e, $t);
56}
57
58# if ($opt{e} and exists $opt{p} ? 0 == length $opt{p} : 1) {
59if ($opt{e}) {
60  my $skip;
61  if ($opt{e} =~ /^[1-7]$/) {
62    require Encode;
63    my $locale = $ENV{LC_CTYPE} || $ENV{LC_ALL} || $ENV{LANG};
64    if ($^O eq 'os2' and not eval {Encode::resolve_alias($locale)} ) {
65      require OS2::Process;
66      $locale = 'cp' . OS2::Process::out_codepage();
67    }
68    $skip = !($opt{e} & 1);
69    # Reinterpret @ARGV
70    @ARGV = map my_decode($locale, $_), @ARGV if $opt{e} & 4;
71    # Reinterpret opts
72    @opt{keys %opt} = map my_decode_deep($locale, $_), values %opt
73      if $opt{e} & 2;
74    $opt{e} = $locale;
75  } elsif ($opt{e} eq 'binary') {
76    binmode STDOUT;
77    $skip = 1;
78  }
79  binmode STDOUT, ":encoding($opt{e})" unless $skip;
80}
81
82my $e_opt = MP3::Tag->get_config('extra_config_keys');
83MP3::Tag->config('extra_config_keys', @$e_opt, qw(empty-F-deletes frames_write_creates_dirs));
84MP3::Tag->config('empty-F-deletes', 1)
85  unless defined MP3::Tag->get_config1('empty-F-deletes');
86
87# keys of %opt to the MP3::Tag keywords:
88my %trans = (	't' => 'title',
89		'a' => 'artist',
90		'l' => 'album',
91		'y' => 'year',
92		'g' => 'genre',
93		'c' => 'comment',
94		'n' => 'track'  );
95
96# Interprete Escape sequences:
97my %r = ( 'n' => "\n", 't' => "\t", '\\' => "\\"  );
98my ($e_backsl, $e_interp);
99if ($opt{E} =~ s/^\+//) {
100  ($e_backsl, $e_interp) = ((split m(/i:), $opt{E}, 2), '');
101  $e_backsl .= 'p' unless $e_backsl =~ /p/;
102  $e_interp =~ s/[Fp]//g;
103  $e_interp .= 'Fp';
104} else {
105  ($e_backsl, $e_interp) = ((split m(/i:), $opt{E}, 2), '');
106}
107for my $e (split //, $e_backsl) {
108  $opt{$e} =~ s/\\([nt\\])/$r{$1}/g if defined $opt{$e};
109}
110$e_interp = {map +($_, 1), split //, $e_interp};
111
112if ($opt{'@'}) {
113  for my $k (keys %opt) {
114    if (ref $opt{$k}) {
115      s/\@/%/g for @{ $opt{$k} };
116    } else {
117      $opt{$k} =~ s/\@/%/g;
118    }
119  }
120}
121
122my %F_human = qw( composer	TCOM
123		  text_by	TEXT
124		  orchestra	TPE2
125		  conductor	TPE3
126		  disk_n	TPOS);	# Only most useful, and not -l etc...
127
128my $FNAME = qr/(?:		# 1: Whole specifier
129		 \w{4}		# 2: Frame name
130		 (?:
131		   \d\d		# 3: Frame number
132		 |
133		   (?: \( [^()]* (?:\([^()]+\)[^()]*)* \) )? # 4: Language part
134		   (?: \[ (?: \\. | [^]\\] )* \] )? # 5: Description part
135		 )?
136	       )
137	      /x;
138my $FNAME_human = join '|', keys %F_human;
139
140my @set_f;
141my %textish = map +($_, 1), qw( _encoding Text Language Description URL );
142for my $F (@{ $opt{F} }) {
143  my ($lead, @s) = ($F =~ /^(\W)/);
144  if (defined $lead) {
145    @s = split /\Q$lead$lead$lead/, substr $F, 1;
146  } else {
147    @s = $F;
148  }
149  for my $s (@s) {
150    $s =~ /^($FNAME|$FNAME_human|(?:TAGS|ID3v[12])(?=\s+[\?<>]))(?:=|\s+(\??<|>)\s+)(.*)/so
151      or die "unrecognized part of -F option: `$s'";
152    my $FF = $F_human{$1} || $1;
153    push @set_f, [$FF, $3, ($2 || '')];
154  }
155}
156
157my (@del, @del_tag);
158for my $o (@{ $opt{d} }) {
159  my @D;
160  push @D, $1 while $o =~ s/^ ( $FNAME | ID3v[12] ) (,|$) //xo;
161  die "Unrecognized part of -d option: `$o'" if length $o;
162  push @del_tag, grep  /^ID3v[12]$/, @D;
163  push @del,     grep !/^ID3v[12]$/, @D;
164}
165
166# Configure stuff...
167MP3::Tag->config(autoinfo => qw(ParseData ID3v2 ID3v1)) if $opt{N}; # Naive algo
168
169for my $C (@{ $opt{C} || [] }) {
170  my ($c) = ($C =~ /^(\W)/);
171  $c = quotemeta $c if defined $c;
172  $c = '(?!)' unless defined $c;		# Never match
173  my @opts = split /$c/, $C;
174  shift @opts if @opts > 1;
175  for $c (@opts) {
176    $c =~ s/^(\w+)=/$1,/;
177    MP3::Tag->config(split /,/, $c);
178  }
179}
180
181unless ($opt{N}) {{
182  my $cfg = $ENV{MP3TAG_NORMALIZE_FIELDS};
183  last if defined $cfg and not $cfg;
184  last unless defined $cfg or $ENV{HOME} and -d "$ENV{HOME}/.music_fields";
185  no strict 'refs';
186  eval 'require Normalize::Text::Music_Fields';
187  for my $elt ( qw( title track artist album comment year genre
188		    title_track artist_collection person ) ) {
189    MP3::Tag->config("translate_$elt", \&{"Normalize::Text::Music_Fields::normalize_$elt"})
190	if defined &{"Normalize::Text::Music_Fields::normalize_$elt"};
191  }
192  MP3::Tag->config("short_person", \&Normalize::Text::Music_Fields::short_person)
193      if defined &Normalize::Text::Music_Fields::short_person;
194  $cfg = '' if not defined $cfg or $cfg =~ /^[01]$/;
195  my @d = split /$Config{path_sep}/, $cfg;
196  Normalize::Text::Music_Fields::set_path(@d)
197   if @d and defined &Normalize::Text::Music_Fields::set_path;
198}}
199
200my @parse_data;
201die 'Option -P requires ParseData in autoinfo'
202  if $opt{P} and not grep $_ eq 'ParseData', @{ MP3::Tag->get_config('autoinfo') };
203for my $o (@{ $opt{P} }) {
204  my ($c) = ($o =~ /^\w*(\W)/s);
205  $c = quotemeta $c if defined $c;
206  $c = '(?!)' unless defined $c;		# Never match
207  push @parse_data, map [split /$c/, $_, -1], split /$c$c$c/, $o;
208}
209for my $c (@parse_data) {
210  die "Two few parts in parse directive `@$c'.\n" if @$c < 3;
211}
212
213# E.g., to make Inf overwrite existing title, do
214# mp3info2.pl -C title,Inf,ID3v2,ID3v1,filename -u *.mp3
215
216sub new_tag_object ($) {
217  my $fname = shift;
218  return MP3::Tag->new($fname) unless $fname eq '';
219  MP3::Tag->new_fake('settable');
220}
221
222sub process_file ($) {
223    my $f = shift;
224    my $mp3 = new_tag_object($f); # BUGXX Can't merge into if(): extra refcount
225    if ($mp3) {
226      print $mp3->interpolate(<<EOC) unless exists $opt{p};
227File: %F
228EOC
229      for my $tag (@del_tag) {	# delete whole tags
230				  $mp3->delete_tag($tag);
231				}
232      $mp3 = new_tag_object($f) if @del_tag;
233
234      #$mp3->get_tags;
235	# XXXX won't copy ID3v1/2 tags otherwise...
236      my $need_data = ($opt{u} or not $opt{D} or $opt{2} or @set_f or @del or 1);
237      for my $k (keys %trans) {	# if not -D, id3v2 may be modified
238	$need_data = 1 if exists $opt{$k};
239      }			# If $need_data FALSE, $modify will be FALSE
240      my $data;			# XXXX May be needed by interpolate()???
241      $data = $mp3->autoinfo('from') if $need_data;
242      my $modify = $opt{2};
243      my (@args, @set_v);
244      for my $k (keys %trans) {
245	if (exists $opt{$k}) {
246	  my $i = ($e_interp->{$k} ? 'i' : '');
247	  push @set_v, [$trans{$k}, $opt{$k}, $e_interp->{$k}];
248	  #push @args, ["mz$i", $opt{$k}, "%$k"];
249	  if (exists $data->{$trans{$k}}) {
250	    # If the autocalculated value differs, or comes from non-ID3-tag
251	    # write to a tag
252	    if ( $data->{$trans{$k}}->[0] ne $opt{$k}
253		 or $data->{$trans{$k}}->[1] !~ /^id3/i ) {
254	      warn "Need to change $trans{$k}\n";
255	      $data->{$trans{$k}} = [$opt{$k}, 'cmd'];
256	      $modify = 1;
257	    }
258	  } else {
259	    warn "Need to add $trans{$k}\n"
260	      unless $f eq '';
261	    $data->{$trans{$k}} = [$opt{$k}, 'cmd'];
262	    $modify = 1;
263	  }
264	}
265      }
266      if ($opt{u} and not $modify) { # Update
267	for my $k (keys %$data) {
268	  next if $k eq 'song'; # Alias for title (otherwise double warn)
269	  next if $data->{$k}->[1] =~ /^(ID3|cmd)/;
270	  next unless defined $data->{$k}->[0];
271	  next unless length  $data->{$k}->[0];
272	  $modify = 1;
273	  warn "Need to propagate $k from $data->{$k}->[1]\n";
274	}
275      }
276
277      my $odata = $data;
278      # Now, when we know what should be updated, retry with arguments
279
280      if (@args or @set_v or @parse_data or @set_f) {
281	$mp3 = new_tag_object($f);
282	$mp3->config('parse_data', @parse_data, @args);
283	for my $set (@set_v) {
284	  my $v = $set->[1];
285	  $v = $mp3->interpolate($v) if $set->[2];
286	  my $meth = $set->[0] . '_set';
287	  $mp3->$meth($v);
288	}
289	for my $set (@set_f) {
290	  my($have, $b, $whole, $e);
291	  if ($set->[2] =~ /^(?:\?<|(>))$/) {
292	    my $write = $1;
293	    $have = ($set->[0] =~ /^(TAGS|ID3v[12])$/
294		     or $mp3->have_id3v2_frame_by_descr($set->[0]));
295	    next if $write and not $have;
296	  }
297	  my $v = $set->[1];
298	  $v = $mp3->interpolate($v) if $e_interp->{F};
299	  next if $set->[2] eq '?<' and not -e $v;
300
301	  unless ($whole = ($set->[0] =~ /^(TAGS|ID3v[12])$/)) {
302	    my($FF) = MP3::Tag::ID3v2->what_data(substr $set->[0], 0, 4);
303	    $b = grep !$textish{$_}, @$FF;
304	  }
305
306	  if ($set->[2] eq '>') { # we know frame exists
307	    my $o;
308	    if ($whole) {
309	      $mp3->get_tags;
310	      if ($set->[0] eq 'TAGS') {
311		next unless exists $mp3->{ID3v2} or exists $mp3->{ID3v1};
312		$o = $mp3->interpolate("%{ID3v2}%{ID3v1}");
313	      } else {
314		next unless exists $mp3->{$set->[0]};
315		$o = $mp3->interpolate("%{$set->[0]}");
316	      }
317	    } else {
318	      $o = $mp3->select_id3v2_frame_by_descr($set->[0]);
319	    }
320	    next unless defined $o; # Should not happen???
321	    die "An attempt to extract `non-simple' frame `$set->[0]' to a file"
322	      if ref $o;
323	    unless (open FF, "> $v") {
324	      my $rc;
325	      if (MP3::Tag->get_config1('frames_write_creates_dirs')) {
326		my ($dir) = ($v =~ m,^(.*)[\\/],s);
327		if (defined $dir and not -d $dir) {
328		  mkpath $dir;		# would die on error
329		  $rc = open FF, "> $v"
330		}
331	      }
332	      die "Can't open `$v' for write: $!" unless $rc;
333	    }
334	    binmode FF if $b;
335	    syswrite FF, $o, length $o or die "syswrite to `$v': $!"
336	      if length $o;
337	    close FF  or die "Can't close `$v' for read: $!";
338	    next;
339	  }
340	  if ($set->[2]) {	# < or ?<
341	    my $cond = ($set->[2] eq '?<');
342	    next if $cond and not -e $v;
343	    if ($whole) {
344	      my $from = MP3::Tag->new($v) or die "Can't create tags for `$v'";
345	      $from->get_tags;
346	      if ($set->[0] =~ /^(TAGS|(ID3v1)?)$/) { # Process "simple" fields
347		my $from1 = ($2 ? $from->{ID3v1} : $from);	# $2: ID3v1
348		for my $field (values %trans) {	# Use "named method" for access
349		  my $v = ($from1 and $from1->$field());
350		  next unless defined $v and length $v;
351		  my $check_v = (not $cond or $mp3->$field);
352		  next unless defined $check_v and length $check_v;
353		  my $ff = $field .= '_set';
354		  $mp3->$ff($v);
355		  $modify++;
356		}
357	      }
358	      $modify +=
359		$from->copy_id3v2_frames($mp3, ($cond ? '' : 'delete'), 'flags')
360		  if $set->[0] =~ /^(TAGS|ID3v2)$/;
361	      next;
362	    }
363	    open FF, "< $v" or die "Can't open `$v' for read: $!";
364	    if ($b) { binmode FF }
365	    elsif ($e = $mp3->get_config1('decode_encoding_files')) {
366	      eval "binmode FF, ':encoding($e)'"; # old binmode won't compile...
367	      warn $@ if $@ and $] >= 5.008;
368	    }
369	    undef $/;
370	    $v = <FF>;
371	    close FF  or die "Can't close `$v' for read: $!";
372	    $v =~ s/^\s+//, $v =~ s/^\s+// unless $b;
373	  }
374	  undef $v if not length $v and $mp3->get_config1('empty-F-deletes');
375	  $mp3->select_id3v2_frame_by_descr($set->[0], $v);
376	  $modify++;
377	}
378	$mp3->get_tags;
379	$data = $mp3->autoinfo('from') if $need_data;
380      }
381      for my $del (@del) {	# delete
382	my $c = $mp3->select_id3v2_frame_by_descr($del, undef);
383	warn "No frames found for $del.\n" unless $c;
384	$modify++ if $c;
385      }
386
387      # Recheck whether we need to update
388      if (not $modify and $opt{u} and @parse_data) {
389	for my $k (keys %$data) {
390	  $modify = 1, last
391	    if defined $data->{$k} and
392	      (not defined $odata->{$k} or $data->{$k} ne $odata->{$k});
393	}
394      }
395      $mp3->id3v2_frames_autofill()
396	unless @{$opt{d}} or $opt{N} or $f eq ''
397	  or not ($modify or $opt{u} or $mp3->is_id3v2_modified);
398      $opt{u} and warn "No update needed\n" unless $modify or $mp3->is_id3v2_modified;
399
400      my ($com,$lyr,$p) = map $mp3->interpolate("%{$_}"), qw(TCOM TEXT TPE1);
401      my ($perf,$_p) = 'Artist:  %a'; # Fallback; otherwise print "Performer:"
402      unless (exists $opt{p} or $opt{I}) {
403	if (defined $p and not $mp3->{ID3v1}	# No forward propagation problems
404	    and length($_p = $mp3->interpolate('%{TPE1}'))) {
405	  $perf = "Performer: $_p";
406	} elsif (length($_p = $mp3->interpolate('%{TXXX[TPE1]}'))) {
407	  $perf = "Performer: $_p";
408	} elsif ($p and defined $com and defined $lyr
409		 and $p ne $com and $p ne $lyr) { # So we know it is different
410	  $perf = "Performer: $p";
411	}
412      }
413      print $mp3->interpolate(exists $opt{p} ? $opt{p} : <<EOC);
414Title:   %-50t Track: %n
415%{TCOM:Composer: %{TCOM}
416}%{TEXT:Text: %{TEXT}
417}$perf
418%{TPE2:Orchestra (etc): %{TPE2}
419}%{TPE3:Conductor (etc): %{TPE3}
420}Album:   %{TPOS:%-46l}%{!TPOS:%-50l} Year:  %y%{TPOS:  Disk: %{TPOS}}
421Comment: %-50c Genre: %g
422EOC
423
424      if ($opt{x}) {
425	print $mp3->interpolate(<<EOC); # Aligned for MPEG2 L3
426%{mP}:      %-12{T[?Hh,?{mL}m,{SML}s]} %{w:%wx%h%{bD:x%{bD}} }%{L:MPEG %v Layer %L    %r KB/s, %qKHz (%o)}
427%{C:Copyright:  %-4C         Frames Padded: %-4p Frames:   %-7u }%{ID3v1:ID3v1: present}%{ID3v2:
428ID3v2: %{ID3v2-modified:modified}%{!ID3v2-modified:%{ID3v2-stripped}+%{ID3v2-pad}pad=%{ID3v2-size} Bytes}; frames present: %{frames}}
429EOC
430      }
431      if (($opt{x} || 0) > 1) {
432	my $binary = $opt{x} > 2 ? '_' : '';
433	print $mp3->interpolate("%{ID3v2:%{${binary}out_frames[<<//>>]}\n}");
434      }
435      return unless ($modify or $opt{u} and ($opt{u}>1 or $mp3->is_id3v2_modified))
436	and not $opt{D};	# Dry run
437      $mp3->frames_translate if $opt{2};
438      $mp3->update_tags($data, $opt{2});
439    } else {
440      print "Not found...\n";
441    }
442}
443
444my @f = @ARGV;
445if ($opt{G}) {
446  require File::Glob;			# "usual" glob() fails on spaces...
447  @f = map File::Glob::bsd_glob($_), @f;
448}
449if ($opt{R}) {
450  require File::Find;
451  File::Find::find({wanted => sub {return unless -f and /$opt{r}/o; process_file $_},
452		    no_chdir => 1}, @f);
453} else {
454  my $f;
455  for $f (@f) {
456    process_file $f;
457  }
458}
459
460=head1 NAME
461
462mp3info2 - get/set MP3 tags; uses L<MP3::Tag> to get default values.
463
464=head1 SYNOPSIS
465
466  # Print the information in tags and autodeduced info
467  mp3info2 *.mp3
468
469  # In addition, set the year field to 1981
470  mp3info2 -y 1981 *.mp3
471
472  # Same without printout of info, recursively in the current directory
473  mp3info2 -R -p "" -y 1981 .
474
475  # Do not deduce any field, print (normalized) info from the tags only
476  mp3info2 -C autoinfo=ID3v2,ID3v1 *.mp3
477
478  # As above, but without normalization/autofill, the raw information in tags
479  mp3info2 -N *.mp3
480
481  # As above, but only with ID2v1 tag read
482  mp3info2 -NC autoinfo=ID3v1 *.mp3
483
484  # Get artist from CDDB_File, autodeduce other info, write it to tags
485  mp3info2 -C artist=CDDB_File -u *.mp3
486
487  # For title, prefer information from .inf file; autodeduce rest, update
488  mp3info2 -C title=Inf,ID3v2,ID3v1,filename -u *.mp3
489
490  # Same, and get the artist from CDDB file
491  mp3info2 -C title=Inf,ID3v2,ID3v1,filename -C artist=CDDB_File -u *.mp3
492
493  # Write a script for conversion of .wav to .mp3, autodeducing tags
494  mp3info2 -p "lame -h --vbr-new --tt '%t' --tn %n --ta '%a' --tc '%c' --tl '%l' --ty '%y' '%f'\n" *.wav >xxx.sh
495
496=head1 DESCRIPTION
497
498The program prints a message summarizing tag info (obtained via
499L<MP3::Tag|MP3::Tag> module) for specified files.
500
501It may also update the information in ID3 tags.  This happens in three
502different cases.
503
504=over
505
506=item *
507
508If the information supplied in command-line options C<t a l y g c n>
509differs from the content of the corresponding ID3 tags (or there is no
510corresponding ID3 tags).
511
512=item *
513
514If options C<-d> or C<-F> were given.
515
516=item *
517
518if C<MP3::Tag> obtains the info from other means than MP3 tags, and
519C<-u> forces the update of the ID3 tags.
520
521=back
522
523(All these ways are disabled by C<-D> option.)  ID3v2 tag is written
524if needed, or if C<-2> option is given.  (Automatic fill-in of
525deduceable fields (via the method id3v2_frames_autofill()) is
526performed unless C<-d> or C<-N> options are given.)
527
528The option C<-u> writes (C<u>pdates) the fetched information to the
529MP3 ID3 tags.  This option is assumed if there are command-line options
530which explicitly set tag elements (C<-a>, C<-t> etc., and C<-F>, C<-d>).
531(Effects of this option may be overridden by giving C<-D>
532option.)  If C<-2> option is also given, forces write of ID3v2 tag
533even if the info fits the ID3v1 tag (in addition, this option enables
534auto-update of "personal name" fields, and corresponding titles
535according to values of C<translate_person>, C<person_frames> etc.
536configuration settings; see L<"Normalization of fields">).  This option
537is ignored if no change to tags is detected; however, one can force an
538update by repeating this option (useful if you expect the change the
539"format" of the tag, as opposed to its "content").
540
541The option C<-p> prints a message using the next argument as format
542(by default C<\\>, C<\t>, C<\n> are replaced by backslash, tab and
543newline; governed by the value of C<-E> option); see
544L<MP3::Tag/"interpolate"> for details of the format of sprintf()-like
545escapes.  If no option C<-p> is given, message in default format will
546be emitted.  The value of option C<-e> is the encoding used for the
547output; if the value is a number, system-specific encoding is guessed
548(and used for the output if bit 0x1 is set); if bit 0x2 is set, then,
549command line options are assumed to be in the guessed encoding; if bit
5500x4 is set, then, command line arguments are assumed to be in the
551guessed encoding.  Use the value C<binary> to do binary output.
552
553With option C<-D> (dry run) no update is performed, no matter what the
554other options are.  With this option, no parsing of tags is performed unless
555needed.
556
557Use options
558
559  t a l y g c n
560
561to overwrite the information (title artist album year genre comment
562track-number) obtained via C<MP3::Tag> heuristics (C<-u> switch is
563implied if any one of these arguments differs from what would be found
564otherwise; use C<-D> switch to disable auto-update).  By default, the
565values of these options are not C<%>-interpolated; this may be changed by
566C<-E> option.
567
568The option C<-d> should contain the comma-separated list of ID3v2
569frames to delete.  A frame specification is the same as what might be
570given to C<"%{...}"> frame interpolation command, e.g., C<TIT3>,
571C<COMM03>, C<COMM(fra)[short title]>; the difference with modify-access
572is that B<ALL> (and not the B<first> of) matching frames are deleted.
573(Option -d may be repeated.)
574
575For example, C<-d APIC> would remove all picture frames.  In addition, if the
576list contains C<ID3v1> or C<ID3v2>, whole tags will be deleted.
577
578Likewise, the option C<-F> allows setting of arbitrary C<ID3v2>
579frames: if one needs to set one frame, use the directive C<FRAME_spec=VALUE>:
580
581  -F TIT2=The_new_Title
582
583Again, on modify, B<ALL> matching frames are deleted first, so be carefull with
584
585  -F COMM=MyComment
586
587Option C<-F> may be repeated to set more than one frame.  If configuration
588variable C<empty-F-deletes> is TRUE (default), empty arguments will delete
589the frame.
590
591One can replace C<FRAME_spec=VALUE> by C<FRAME_spec E<lt> FILE>; in
592this case the value to set is read from the file named F<FILE>; if the
593frame is text-only (meaning: at most C<[encoded]Text URL Language
594Description> fields are present), the file is read in text mode (and
595with starting/trailing whitespace stripped), otherwise it is read in
596binary mode.  (Whitespace is required about the C<E<lt>> signs.)  If
597C<E<lt>> is replaced by C<?E<lt>>, the value is set only if frame is
598not yet present, and if the file exists; if replaced by C<E<gt>>, the
599value (if present) is written to F<FILE> (creation of intermediate directories
600is controlled by configuration option C<frames_write_creates_dirs>, the
601default is FALSE).
602
603Additionally, C<FRAME_spec> may be one of C<ID3v1> or C<ID3v2> or C<TAGS>;
604in this case, whole tags are written or read.  For example, for C<TAGS E<lt>
605FILE>, C<title artist album year genre comment track> info is calculated from
606F<FILE>, which may be raw tags, as produced with C<E<gt>>, or a valid MP3
607file; if L<Image::ExifTool|Image::ExifTool> is present, the data may be
608read from arbitrary multimedia file.  (Likewise,  for C<ID3v1 E<lt> FILE>,
609the same info is extracted from
610C<ID3v1> tag only.) After this, in case of C<ID3v2> or C<TAGS>, C<ID3v2>
611frames are copied from the C<ID3v2> tag one-by-one.  (With suitable
612modifications for C<?E<lt>>.)
613
614By default, the "VALUE" for C<-F> is C<%>-interpolated; this can be
615changed by option C<-E>.  For user convenience, human-friendlier forms
616C<composer, text_by, orchestra, conductor, disk_n> can be used instead of
617C<TCOM, TEXT, TPE2, TPE3, TPOS>.
618
619The option C<-P RECIPE> is a very powerful generalization of what can be done
620by options C<-F>, C<-d>, and C<-t -a -l -y -g -c -n>.  It may be
621repeated; the values should contain the parse recipes.  They become the
622configuration item C<parse_data> of C<MP3::Tag>; eventually this information
623is processed by L<MP3::Tag::ParseData|MP3::Tag::ParseData> module (if the
624latter is present in the chain of heuristics; see option C<-C>).  The
625C<RECIPE> is split into C<$flags, $string, @patterns> on its first
626non-alphanumeric character; the first of @patterns which matches
627$string is going to be executed (for side effects).  (See examples:
628L<EXAMPLES: parse rules>.)
629
630If option C<-G> is specified, the file names on the command line are
631considered as glob patterns.  This may be useful if the maximal
632command-line length is too low.  With the option C<-R> arguments can
633be directories, which are searched recursively for audio (default
634F<*.mp3>) files to process; use option C<-r> to reset the regular
635expression to look for (the default is C<(?i:\.mp3$)>).
636
637The option C<-E> controls expansion of escape characters.  It should
638contain the letters of the command-line options where C<\\, \n, \t>
639are interpolated; one can append the letters of C<t a l y g c n F>
640options requiring C<%>-interpolation after the separator C</i:> (for
641C<-F>, only the values are interpolated).  The default value is
642C<p/i:Fp>: only C<-p> is C<\>-interpolated, and only C<-F> and C<-p>
643are subject to C<%>-interpolation.  If all one wants is to I<add> to
644the defaults, preceed the value of C<-E> (containing added options) by
645C<"+">.  (Some parts of the value of option C<-P> are interpolated,
646but this should be governed by flags, not C<-E>; do I<NOT> put C<P>
647into the C<%>-interpolated part of C<-E>.)
648
649If the option C<-@> is given, all characters C<@> in the options are
650replaced by C<%>.  This may be convenient if the shell treats C<%>
651specially (e.g., DOSISH shells).
652
653If option C<-I> is given, no guessworking for I<artist> field is performed
654on typeout.
655
656The option C<-C CONFIG_OPT=VALUE1,VALUE2...> sets C<MP3::Tag> configuration
657data the same way as C<MP3::Tag->config()> would do (recall that the value
658is an array; separate elements by commas if more than one).  The option may
659be repeated to set more than one value.  Note that since C<ParseData> is used
660to process C<-P> parse recipes, it should be better be kept in the
661C<autoinfo> configuration (and related fields C<author> etc) in presence of C<-P>.
662
663If the option C<-x> is given, the technical information about the audio
664file is printed (MP3 level, duration, number of frames, padding, copyright,
665and the list of ID3v2 frame names in format suitable to C<%{...}> escapes).
666If C<-x> is repeated, content of frames is also printed out (may output
667non-printable chars, if it is repeated more than twice).
668
669If option C<-N> is given, all the "smarts" are disabled - no
670normalization of fields happens, and (by default) no attempt to deduce the
671values of fields from non-ID3 information is done.  This option is
672(currently) equivalent to having C<-C autoinfo=ParseData,ID3v2,ID3v1>
673as the first directive, to having no F<Normalize::Text::Music_Fields.pm>
674present on @INC path, and not calling autofill() method.
675
676=head1 Normalization of fields
677
678(The loading of normalization module and all subsequent operations may be
679disabled by the option C<-N>, or by setting the environment variable
680C<MP3TAG_NORMALIZE_FIELDS> to be FALSE.  If not prohibited,
681the module is attempted to be loaded if directory F<~/.music_fields>
682is present, or C<MP3TAG_NORMALIZE_FIELDS> is set and TRUE.)
683
684If loading of the module C<Normalize::Text::Music_Fields> is successful,
685the following is applicable:
686
687If the value of C<MP3TAG_NORMALIZE_FIELDS> is defined and not 1, this value
688is broken into directories as a PATH, and load path of
689C<Normalize::Text::Music_Fields> is set to be this list of directories.
690Then L<MP3::Tag> is instructed (via corresponding configuration settings) to
691use C<normalize_artist> (etc.) methods defined by this module.  These methods
692may normalize certain tag data.  The current version defines methods for
693"normalization" of personal names, and titles (based on the composer).  This
694normalization is driven through user-editable configuration tables.
695
696In addition to automatical normalization of MP3 tag data, one can use
697"fake MP3 files" to manually access some features of this module.
698For this, use an empty file name, and C<-D> option.  E.g,
699
700  mp3info2 -D -a beethoven                       -p "%a\n"         ""
701  mp3info2 -D -a beethoven                       -p "%{shP[%a]}\n" ""
702  mp3info2 -D -a beethoven -t "sonata #28"       -p "%t\n"         ""
703  mp3info2 -D -a beethoven -t "allegretto, Bes" -@p "@t\n"         ""
704  mp3info2 -D -a beethoven -t "op93"            -@p "@t\n"         ""
705
706will print the normalized person-name for C<beethoven>, the
707corresponding normalized short person-name, and the normalized title
708for C<sonata #28> of composer C<beethoven>.  E.g., with the shipped
709normalization tables, it will print
710
711  Ludwig van Beethoven (1770-1827)
712  L. van Beethoven
713  Piano Sonata No. 28 in A major; Op. 101 (1816)
714  Allegretto for Piano Trio in B flat major; WoO 39 (1812)
715  Symphony No. 8 in F major; Op. 93 (comp. 1812, f.p. Vienna, 1814-02-27, cond. Beethoven; pubd. 1816)
716
717=head1 The order of operation
718
719Currently, the operations are done in the following order
720
721=over 2
722
723=item
724
725Deletion of ID3v1 or ID3v2 as a whole via C<-d> option;
726
727=item
728
729Recipies of C<-P> option are set up (to be triggered by interpolation);
730
731=item
732
733The setting done via C<-a/-t/-l/-y/-g/-c/-n> options;
734
735=item
736
737The settings done via C<-F> option;
738
739=item
740
741Deletion of individual frames via C<-d> option;
742
743=item
744
745autofill of ID3v2 (id) frames;
746
747=item
748
749Emit info based on C<-p> and C<-x> options;
750
751=item
752
753Trigger recipies of C<-P> (if not triggered by interpolation);
754
755=item
756
757Update tags if needed.
758
759=back
760
761=head1 Usage strategy: escalation of complexity
762
763The purpose of this script is to to make handling of ID3 tags as simple
764I<as possible>.
765
766On one end of the scale, one can perform arbitrarily
767complex manipulations with tags using L<C<MP3::Tag>|MP3::Tag> Perl module.
768
769On the other end, it is much more convenient to handle simplest manipulations
770with tags using this script's options C<-t -a -l -y -g -c -n> and C<-p
771-F -d>.  For slightly more complicated tasks, one may need to use the
772more elaborate method of I<parse rules>, provided to this script by
773the option C<-P>; the rules depend heavily on I<interpolation>, see
774L<MP3::Tag/interpolate>, L<MP3::Tag/interpolate_with_flags>.
775
776To simplify upgrade from "simplest manipulations" to "more elaborate
777ones", here we provide "parse rule" I<synonyms> to the simplest
778options.  So if you start with C<-t -a -l -y -g -c -n> and C<-p -F -d>
779options which "almost work" for you, you have a good chance to be able
780to fully achieve your aim by modifying the synonyms described below.
781
782(Below we assume that C<-E> option is set to its default value, so
783C<-F -p> are C<%>-interpolated, other options are not.  Note also that
784if your TTY's encoding is recognized by Perl, it is highly recommended
785to set C<-e 3> option; on DOSISH shells, better use C<-@>, and replace
786C<%>'s by C<@>'s below.)
787
788=over 14
789
790=item C<-t VALUE>
791
792  -P "mz/VALUE/%t"
793
794=item C<-a -l -y -g -c -n>
795
796Likewise.
797
798=item C<-F> "TIT2=VALUE"
799
800  -P "mzi/VALUE/%{TIT2}"
801
802=item C<-F> "APIC[myDescr] < FILE"
803
804  -F "APIC[myDescr]=%{I(fimbB)FILE}"
805
806or
807
808  -P "mzi/%{I(fimbB)FILE}/%{APIC[myDescr]}"
809
810(remove C<bB> for text-only frames).
811
812=item C<-F> "APIC[myDescr] > FILE"
813
814  -P "bOi,%{APIC[myDescr]},FILE"
815
816(remove C<b> for text-only frames); or use C<-e binary -p
817"%{APIC[myDescr]}"> with redirection, see L<"EXAMPLES: parse rules">.
818
819=item C<-d> TIT2
820
821  -P "m//%{TIT2}"
822
823=item C<-F> "TIT2 ?< FILE"
824
825Very tricky.  This won't set distinguish empty file and non-existing one:
826
827  -P "mzi/%{TIT2:1}0%{I(fFim)FILE}/10/10%{TIT2}/0%{U1}"
828
829(add C<bB> to C<fFim> for non-text-only frames); the last part may be
830omitted if one omits the flag C<m> - it is present to catch misprints
831only.
832
833=back
834
835For details on "parse rules", see L<EXAMPLES: parse rules> and
836L<MP3::Tag::ParseData/DESCRIPTION>.
837
838=head1 EXAMPLES: parse rules
839
840Only the C<-P> option is complicated enough to deserve comments...
841For full details on I<parse rules>, see
842L<MP3::Tag::ParseData/DESCRIPTION>; for full details on interpolation,
843see L<MP3::Tag/interpolate>, L<MP3::Tag/interpolate_with_flags>.
844
845For a (silly) example, one can replace C<-a Homer -t Iliad> by
846
847  -P mz=Homer=%a -P mz=Iliad=%t
848
849A less silly example is forcing a particular way of parsing a file name via
850
851  -P "im=%{d0}/%f=%a/%n %t.%e"
852
853It is broken into
854
855 flags		string	 	pattern1
856 "im"		"%{d0}/%f"	"%a/%n %t.%e"
857
858The flag letters stand for I<interpolate>, I<must_match>.  This
859interpolates the string C<"%{d0}/%f"> and parses the result (which is
860the file name with one level of the directory part preserved) using
861the given pattern; thus the directory name becomes the artist, the
862leading numeric part - the track number, and the rest of the file name
863(without extension) - the title.  Note that since multiple patterns
864are allowed, one can similarly allow for multiple formats of the
865names, e.g.
866
867  -P "im=%{d0}/%f=%a/%n %t.%e=%a/%t (%y).%e"
868
869allows for the file basename to be also of the form "TITLE (YEAR)".  An
870alternative way to obtain the same results is
871
872  -P "im=%{d0}=%a" -P "im=%f=%n %t.%e=%t (%y).%e"
873
874which corresponds to two recipies:
875
876 flags		string	 	pattern1	pattern2
877 "im"		"%{d0}"		"%a"
878 "im"		"%f"		"%n %t.%e"	"%t (%y).%e"
879
880Of course, one could use
881
882 "im"		"%B"		"%n %t"		"%t (%y)"
883
884as a replacement for the second one.
885
886Note that it may be more readable to set I<artist> to C<%{d0}> by an
887explicit asignment, with arguments similar to
888
889  -E "p/i:Fpa" -a "%{d0}"
890
891(this value of C<-E> requests C<%>-interpolation of the option C<-a>
892in addition to the default C<\>-interpolation of C<-p>, and
893C<%>-interpolation of C<-F> and C<-p>; one can shortcut it with C<-E +/i:a>).
894
895To give more examples,
896
897  -P "if=%D/.comment=%c"
898
899will read comment from the file F<.comment> in the directory of the audio file;
900
901  -P "ifn=%D/.comment=%c"
902
903has similar effect if the file F<.comment> has one-line comments, one per
904track (this assumes the the track number can be found by other means).
905
906Suppose that a file F<Parts> in a directory of MP3 files has the following
907format: it has a preamble, then has a short paragraph of information per
908audio file, preceded by the track number and dot:
909
910   ...
911
912   12. Rezitativ.
913   (Pizarro, Rocco)
914
915   13. Duett: jetzt, Alter, jetzt hat es Eile, (Pizarro, Rocco)
916
917   ...
918
919The following command puts this info into the title of the ID3 tag (provided
920the audio file names are informative enough so that MP3::Tag can deduce the
921track number):
922
923 mp3info2 -u -C parse_split='\n(?=\d+\.)' -P 'fl;Parts;%=n. %t'
924
925If this paragraph of information has the form C<TITLE (COMMENT)> with the
926C<COMMENT> part being optional, then use
927
928 mp3info2 -u -C parse_split='\n(?=\d+\.)' -P 'fl;Parts;%=n. %t (%c);%=n. %t'
929
930If you want to remove a dot or a comma got into the end of the title, use
931
932 mp3info2 -u -C parse_split='\n(?=\d+\.)' \
933   -P 'fl;Parts;%=n. %t (%c);%=n. %t' -P 'iR;%t;%t[.,]$'
934
935The second pattern of this invocation is converted to
936
937  ['iR', '%t' => '%t[.,]$']
938
939which essentially applies the substitution C<s/(.*)[.,]$/$1/s> to the title.
940
941Now suppose that in addition to F<Parts>, we have a text file F<Comment> with
942additional info; we want to put this info into the comment field I<after>
943what is extracted from C<TITLE (COMMENT)>; separate these two parts of
944the comment by an empty line:
945
946 mp3info2 -E C -C 'parse_split=\n(?=\d+\.)' -C 'parse_join=\n\n' \
947  -P 'f;Comment;%c'           -P 'fl;Parts;%=n. %t'              \
948  -P 'i;%t///%c;%t (%c)///%c' -P 'iR;%t;%t[.,]$'
949
950This assumes that the title and the comment do not contain C<'///'> as a
951substring.  Explanation: the first pattern of C<-P>,
952
953  ['f', 'Comment' => '%c'],
954
955reads comment from the file C<Comment> into the comment field; the second,
956
957  ['fl', 'Parts'  => '%=n. %t'],
958
959reads a chunk of C<Parts> into the title field.  The third one
960
961  ['i', '%t///%c' => '%t (%c)///%c']
962
963rearranges the title and comment I<provided> the title is of the form C<TITLE
964(COMMENT)>.  (The configuration option C<parse_join> takes care of separating
965two chunks of comment corresponding to two occurences of C<%c> on the right
966hand side.)
967
968Finally, the fourth pattern is the same as in the preceding example; it
969removes spurious punctuation at the end of the title.
970
971More examples: removing string "with violin" from the start of the
972comment field (removing comment altogether if nothing remains):
973
974  mp3info2 -u -P 'iz;%c;with violin%c' *.mp3
975
976setting the artist field without letting auto-update feature deduce
977other fields from other sources;
978
979  mp3info2 -C autoinfo=ParseData -a "A. U. Thor" *.mp3
980
981setting a comment field unless it it already present:
982
983  mp3info2 -u -P 'i;%c///with piano;///%c' *.mp3
984
985The last example shows how to actually write "programs" in the
986language of the C<-P> option: the example gives a conditional
987assignment.  With user variables (as in C<%{U8}>) for temporaries, and
988a possibility to use regular expressions, one
989could provide arbitrary programmatic logic.  Of course, at some level
990of complexity one should better switch to direct interfacing with
991C<MP3::Tag> Perl module (use the code of this Perl script as an example!).
992
993Here is a typical task setting "advanced" id3v2 frames: composer (C<TCOM>),
994orchestra (C<TPE2>), conductor (C<TPE3>).  We assume a directory tree which
995contains MP3 files tagged with the following conventions: C<artist> is
996actually a composer; C<comment> is of one of two forms:
997
998  Performers; Orchestra; Conductor
999  Orchestra; Conductor
1000
1001To set the specific MP3 frames via C<-P> rules, use
1002
1003  mp3info2 -@P "mi/@a/@{TCOM}" \
1004    -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R .
1005
1006With C<-F> options, this can be simplified as
1007
1008  mp3info2 -@F "TCOM=@a" -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R .
1009
1010or
1011
1012  mp3info2 -@F "composer=@a" -P "mi/@c/@{U1}; @{TPE2}; @{TPE3}/@{TPE2}; @{TPE3}" -R .
1013
1014To copy ID3 tags of MP3 files in the current directory to files in directory
1015F</tmp/mp3> with the extension F<.tag> (and print "progress report"), use
1016
1017  mp3info2 -p "@N@E\n" -@P "bODi,@{ID3v2}@{ID3v1},/tmp/mp3/@N.tag" -DNR .
1018
1019Since we did not use C<z> flag, MP3 files without tags are skipped.
1020
1021Now suppose that there are two parallel file hierarchies of audio files,
1022and of lyrics: audio files are in F<audio/dir_name/audio_name.mp3> with
1023corresponding lyrics file in F<text/dir_name/audio_name.mp3>.  To attach
1024lyrics to MP3 files (in C<COMM> frame with description C<lyrics> in language
1025C<eng> - I<this is a non-standard location, see below!>), call
1026
1027  mp3info2 -@P "fim;../text/@{d0}/@B.txt;@{COMM(eng)[lyrics]}" -Ru .
1028
1029inside the directory F<audio>.  (Change C<fim> to C<Ffim> to ignore
1030the audio files for which the corresponding text file does not exist.)
1031(Of course, to follow the specifications, one should have used the
1032field C<"%{USLT(eng)[]}"> instead of C<"%{COMM(eng)[lyrics]}">; see below
1033for variations).
1034
1035Finish by a very simple example: all what the pattern
1036
1037  -P 'i;%t;%t'
1038
1039does is removal of trailing and leading blanks from the title (which
1040is deduced by other means).
1041
1042=head1 More examples
1043
1044With C<-F> option, one could set the C<USLT> frame as
1045
1046  mp3info2 -@F "USLT(eng)[] < ../text/@{d0}/@B.txt" -Ru .
1047
1048Print out such a frame (in any language) with
1049
1050  mp3info2 -@p "@{USLT[]}\n" file.mp3
1051
1052Similarly, to print out the APIC frame with empty description, use
1053
1054  mp3info2 -e binary -@p "@{APIC[]}" file.mp3 > output_picture_file
1055
1056or (with description "cover")
1057
1058  mp3info2 -@P "bOi,@{APIC[cover]},output_picture_file.jpg" audio_07.mp3
1059
1060To set such a frame from file F<xxx.gif> (with the default C<Picture Type>,
1061C<"Cover (front)">, and empty description), do one of
1062
1063  mp3info2 -F  "APIC  <          xxx.gif"  file.mp3
1064  mp3info2 -@F "APIC[]=@{I(fimbB)xxx.gif}" file.mp3
1065
1066The difference of C<APIC> and C<APIC[]> is that the first removes all
1067C<APIC> frames first, and the second removes only all C<APIC> frames with
1068empty description - but arbitrary image type.  So it may be more suitable
1069to use the full specification, as in C<APIC(Cover (front))[]>.
1070
1071To remove C<APIC> frames with empty descriptions, arbitrary C<Picture Type>s
1072(and C<MIME type>s which may be correctly calculated by F<mp3info2>, e.g.,
1073C<TIFF/JPEG/GIF/PNG>), use
1074
1075  mp3info2 -d "APIC[]" file.mp3
1076
1077(note that this wouldn't free disk space, unless "shrink" is forced by
1078configuration variables).  To do the same with the "Conductor" picture type
1079only, do
1080
1081  mp3info2 -d "APIC(Conductor)[]" file.mp3
1082
1083To scan through subdirectories, and add file F<cover.jpg> from the
1084directory of the file as a "default" C<APIC> frame, but only if there
1085is no C<APIC> frame, and a file exists, do
1086
1087  mp3info2 -@F "APIC ?< @D/cover.jpg" -R .
1088
1089This deletes empty frames for date, C<TCOP, TENC, WXXX[], COMM(eng)[]>, and
1090removes the leading 0 from track number from MP3 file in current directory:
1091
1092  mp3info2 -@ -E +/i:y -F "TCOP=@{TCOP}" -F "TENC=@{TENC}"
1093    -F "WXXX[]=@{WXXX[]}" -F "COMM(eng)[]=@{COMM(eng)[]}"
1094    -y "@y" -P "mi/@n/0@n/@n" *.mp3
1095
1096=head1 Examples on dealing with broken encodings
1097
1098One of principal weaknesses of ID3 specification was that it required that
1099data is provided in C<latin-1> encoding.  Since most languages in the world
1100are not expressible in C<latin-1>, this lead to (majority?) of ID3 tags being
1101not standard-conforming.  Newer versions of the specs fixed this shortcoming,
1102but the damage was already done.  Fortunately, this script can use abilities
1103of L<C<MP3::Tag>|MP3::Tag/ENVIRONMENT> to convert from non-conforming content
1104to a conforming one.
1105
1106The following example converts ID3v2 tags which were written in
1107(non-standard-conforming) encoding C<cp1251> to be in
1108standard-conforming encoding.  For the purpose of this example, assume that
1109ID3v1 tags are in the same encoding (and that one wants to leave them in the
1110encoding C<cp1251>); the files to process are found in the current directory
1111and (recursively) in its subdirectories (C<set> syntax for DOSISH shells):
1112
1113  set MP3TAG_DECODE_V1_DEFAULT=cp1251
1114  set MP3TAG_DECODE_V2_DEFAULT=cp1251
1115  mp3info2 -C id3v2_fix_encoding_on_write=1 -u2R .
1116
1117For more information, see L<MP3::Tag/ENVIRONMENT>, L<MP3::Tag/config>,
1118and L<MP3::Tag/CUSTOMIZATION>.
1119
1120=head1 INCOMPATIBILITIES with F<mp3info>
1121
1122This tool is loosely modeled on the program F<mp3info>; it is "mostly"
1123backward compatible (especially when in "naive" mode via C<-N>), and
1124allows a very significant superset of functionality.  Known backward
1125incompatibilities are:
1126
1127  -G -h -r -d -x
1128
1129Missing functionality:
1130
1131  -f -F -i
1132
1133Incompatible C<%>-I<escapes>:
1134
1135  %e %E 	- absolutely different semantic
1136  %v		- has no trailing 0s
1137  %q		- has fractional part
1138  %r		- is a number, not a word "Variable" for VBR
1139  %u		- is one less (in presence of descriptor frame only?)
1140
1141Missing C<%>-I<escapes>:
1142
1143  %b %G
1144
1145Backslash escapes: only C<\\>, C<\n>, C<\t> supported.
1146
1147C<-x> prints data in a different format, not all fields are present, and
1148ID3v2 tag names are output.
1149
1150=head1 ENVIRONMENT
1151
1152With C<-e> 1, 2 or 3, this script may consult environment variables
1153C<LC_CTYPE, LC_ALL, LANG> to deduce the current encoding.  No other
1154environment variables are directly read by this script.
1155
1156Note however, that L<MP3::Tag> module has a rich set of defaults for
1157encoding settings settable by environment variables; see
1158L<MP3::Tag/"ENVIRONMENT">.  So these variables affect (indirectly) how
1159this script works.
1160
1161=head1 OBSOLETE INTERFACE
1162
1163If you do not understand what it is about, it is safe to ignore this
1164announcement:
1165
1166The old, pre-version=C<1.05> way (by triplication of a separator, without
1167repetition of options) to provide multiple commands to C<-F> and <-P>
1168options is still supported, but is strongly discouraged.  (It does not
1169conflict with the current interface.)
1170
1171=head1 AUTHOR
1172
1173Ilya Zakharevich <cpan@ilyaz.org>.
1174
1175=head1 Utilities to create CDDB file
1176
1177Good CD reapers (e.g., F<cdda2wav> with option C<cddb=0>) create a
1178CDDB file with fetched information - as far as an Internet connection is
1179present.  However, if not available, other options exist.
1180
1181The scripts (supplied with the distribution in
1182F<./examples>) can create a "stub" CDDB file basing on:
1183
1184=over 23
1185
1186=item F<fulltoc2fake_cddb.pl>
1187
1188a dump of a full TOC of a CD; create one, e.g., by
1189
1190  readcd -fulltoc dev=0,1,0 -f=audiocd
1191
1192=item F<inf2fake_cddb.pl>
1193
1194directory of F<*.inf> files (e.g., created by F<cdda2wav> without
1195Internet connection);
1196
1197=item F<dir_mp3_2fake_cddb.pl>
1198
1199a directory of MP3 files ripped from a CD (via some guesswork).
1200
1201=back
1202
1203Passing this stub to the script F<cddb2cddb.pl>, it can be transformed
1204to a "filled" CDDB file via a connection to some online database.  Use
1205C<-r> option if multiple records in the database match the CD
1206signature.
1207
1208  fulltoc2fake_cddb audiocd.toc | cddb2cddb     > audio.cddb
1209  inf_2fake_cddb	        | cddb2cddb     > audio.cddb
1210  dir_mp3_2fake_cddb	        | cddb2cddb -r3 > audio.cddb # 3rd record
1211
1212When such a CDDB file is present, it will be used by L<MP3::Tag>
1213module to deduce the information about an audio file.  This information
1214is (by default, transparently) used by this script.
1215
1216=head1 SEE ALSO
1217
1218MP3::Tag, MP3::Tag::ParseData, audio_rename, typeset_audio_dir
1219
1220=cut
1221