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