Developer Forums | About Us | Site Map
Search  
HOME > TUTORIALS > SERVER SIDE CODING > PERL TUTORIALS > CULTURED PERL: FUN WITH MP3 AND PERL, PART 1


Sponsors





Useful Lists

Web Host
site hosted by netplex

Online Manuals

Cultured Perl: Fun with MP3 and Perl, Part 1
By Teodor Zlatanov - 2004-01-13 Page:  1 2 3 4 5 6

Mass tagging, mass renaming

The main functionality of autotag.pl is to identify MP3 files. In the course of that process, however, minor adjustments often need to be made to large groups of files. Enter the Four Autotagging Horsemen.

Stripping comments is a very simple process. I get a hash tag with get_tag(), empty the COMM and WXXX fields, and write it back with set_tag(). In fact, comment stripping could have been done through mass tagging, but it's used so often that I felt I needed a separate option for it.

Guessing track numbers is also quite simple. Get the hash tag, use guess_track_number() on the file and the hash tag, ask for confirmation, and write the tag back to the file.

Mass tagging operates on multiple keys (e.g. TALB) on a series of files. You say, for instance,

autotag.pl -mt "TALB=Best" *.mp3

and all the files that have the mp3 extension will be assigned that TALB value in their ID3v2 tag. Mass-tagging is very nice when, for example, you have a directory full of music by an artist and want to tag all that music with the artist's name. Only supported tag elements can be mass-tagged. Again, I get the hash tag, make my changes, and write it back. The goal is to make it simple and easy to maintain.

Listing 9. Mass tagging, comment stripping, and guessing track numbers


# {{{ handle the one-shot options
if ($config->GUESS_TRACK_NUMBERS_ONLY() ||
    $config->STRIP_COMMENT_ONLY() ||
    scalar keys %{$config->MASS_TAG_ONLY()})
{
 foreach my $file (@ARGV)
 {
  my $tag = get_tag($file, 1);
  unless (defined $tag)
  {
   warn "No ID3 TAG info in '$file', skipping";
   next;
  }

  next if $config->DRYRUN();

  # delegate stripping comments to the mass tagging function
  if ($config->STRIP_COMMENT_ONLY())
  {
   $config->MASS_TAG_ONLY()->{COMM} = '';
   $config->MASS_TAG_ONLY()->{WXXX} = '';
  }

  if (scalar keys %{$config->MASS_TAG_ONLY()})
  {
   foreach (keys %{$config->MASS_TAG_ONLY()})
   {
    unless (exists $supported_frames{$_})
    {
     warn "Unsupported tag element $_ requested for mass tagging, skipping";
     next;
    }
    $tag->{$_} = $config->MASS_TAG_ONLY()->{$_};
   }
   set_tag($file, $tag);
  }
  else
  {
   my $track_number_guess = guess_track_number($file, $tag);

   next if $config->DRYRUN();

   if (defined $track_number_guess &&

              read_yes_no("Is track number $track_number_guess OK for '$file'?", 1))
   {
    $tag->{TRCK} = $track_number_guess;
    set_tag ($file, $tag);
   }
   else
   {
    warn "Could not guess a track number for file $file, sorry";
   }
  }
 }

 exit 0;
}
# }}}

Ah, the mass renaming option. I left it for last because it's the most complex one. For each renaming parameter, I make each "%" in the tag value appear as "{{{%}}}" because otherwise, those "%" characters, when followed by one of the special renaming parameters, could be misinterpreted. Take "100%true" for instance, for the track name, and see how it would become "100%TRACKNAMErue" instead, where TRACKNAME is the track name I get from the hash tag.

Mass renaming also eliminates bad characters, and replaces certain characters with "_" to ensure a reasonable file name. Finally, unless the -c (accept_all) option is given from the command line, autotag.pl will ask if it's okay to rename the file.

Listing 10. Mass renaming

# {{{ handle the -rename_only option
if ($config->RENAME_ONLY())
{
 foreach my $file (@ARGV)
 {
  my $tag = get_tag($file, 1);
                 # the extra parameter will ask us about upgrading V1 to V2
  unless (defined $tag)
  {
   warn "No ID3 TAG info in '$file', skipping";
   next;
  }

  my %map = (
     '%c' => 'COMM',
     '%s' => 'TIT2',
     '%a' => 'TPE1',
     '%t' => 'TALB',
     '%n' => 'TRCK',
    );

  my $name = $config->RENAME_FORMAT();

  foreach my $key (keys %map)
  {
   my $tagkey = $map{$key};
   my $replacement = '';
   if (exists $tag->{$tagkey})
   {
    $replacement = substr $tag->{$tagkey}, 0, $config->RENAME_MAX_CHARS();
                    # limit to N characters
    if ($tagkey eq 'TRCK' && $replacement =~ m/^\d$/)
    {
     $replacement = "0$replacement";
    }
   }

   $replacement =~ s/%/{{{%}}}/g;
                    # this is how we preserve %a in the fields, for example

   $name =~ s/$key/$replacement/;
  }

  $name =~ s/{{{%}}}/%/g;   # turn the {{{%}}} back into % in the fields

  print "The name after % expansion is $name\n" if $config->DEBUG();

  foreach my $char (map { quotemeta } @{$config->RENAME_BADCHARS()})
  {
   $name =~ s/$char//g;
  }

  print "The name after character removals is $name\n" if $config->DEBUG();

  my $newchar = quotemeta $config->RENAME_REPLACEMENT();

  foreach my $char (map { quotemeta } @{$config->RENAME_REPLACECHARS()})
  {
   $name =~ s/$char/$newchar/eg;
  }

  print "The name after character replacements is $name\n" if $config->DEBUG();


  if ($name eq $file)
  {
   # do nothing
   print "Renaming $file is unnecessary, it already answers to our high standards\n"
    if $config->DEBUG();
  }
  elsif (-e $name)
  {
   warn "Could not use name $name, it's already taken by an existing
                        file or directory $file";
  }
  elsif ($config->ACCEPT_ALL() || read_yes_no("Is name $name OK for '$file'?", 1))
  {
   next if $config->DRYRUN();
   print "Renaming $file -> $name\n";
   rename($file, $name);
  }
  else
  {
   # do nothing
  }
 }

 exit 0;
}
# }}}



View Cultured Perl: Fun with MP3 and Perl, Part 1 Discussion

Page:  1 2 3 4 5 6 Next Page: Conclusion & Resources

First published by IBM developerWorks


Copyright 2004-2017 GrindingGears.com. All rights reserved.
Article copyright and all rights retained by the author.