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

autotag.pl preliminaries

The autotag.pl application begins with some initialization routines.

Listing 3. Initialization

use constant SEARCH_ALL   => 'all';

my %freedb_searches = (
   artist  => { keywords => [], abbrev => 'I', tagequiv => 'TPE1' },
   title   => { keywords => [], abbrev => 'T', tagequiv => 'TALB' },
   track   => { keywords => [], abbrev => 'K', tagequiv => 'TIT2' },
   rest    => { keywords => [], abbrev => 'R', tagequiv => 'COMM' },
      );

# maps ID3 v2 tag info to WebService::FreeDB info
my %info2freedb = (
   TALB  => 'cdname',
   TPE1  => 'artist',
      );

my %supported_frames = (
   TIT1 => 1,
   TIT2 => 1,
   TRCK => 1,
   TALB => 1,
   TPE1 => 1,
   COMM => 1,
   WXXX => 1,
   TYER => 1,
      );

my @supported_frames = keys %supported_frames;

my $term = new Term::ReadLine 'Input> '; # global input

The SEARCH_ALL constant is what I use when the user wants to search for a word everywhere -- track names, artist names, etc. I made it a constant in case anyone wants to change it to something else, but it could have been hard-coded as "all" as well.

The %freedb_searches hash maps FreeDB fields to information about them, including ID3v2 tag elements. For instance, it says that what FreeDB calls "artist" is known as "TPE1" in an MP3 tag. The "abbrev" field in the hash entry is used to define command-line switches, so later I can define an -artist switch that can be abbreviated to -i based on the %freedb_searches information.

The %info2freedb hash maps FreeDB fields common across all tracks in a disc to ID3v2 fields. These are not the fields in %freedb_searches, this is a different mapping that says that "cdname" and "artists," also known as "TALB" and "TPE1," respectively, are the same for all tracks in an album.

The %supported_frames hash and the @supported_frames list will be used to figure out what ID3v2 tag elements I support. I could have generated the hash from the list instead of getting the list from the hash, but I feel the difference is irrelevant. The supported frames are used for mass tagging and when writing ID3v2 tags (I only modify the supported frames).

Finally, I create a Term::ReadLine object for user input throughout the application.

Next, I initialize the AppConfig options. Bear with me, this is useful.

Listing 4. AppConfig initialization


# {{{ set up AppConfig and process -help

my $config = AppConfig->new();

$config->define(
   DEBUG       =>
   { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0, ALIAS => 'D' },

   CONFIG_FILE       =>
   { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 0, ALIAS => 'F' },

   HELP        =>

   { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'H' },

   DUMP        =>
   { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0 },

   ACCEPT_ALL  =>
   { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'C' },

   DRYRUN      =>

   { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'N' },

   GUESS_TRACK_NUMBERS_ONLY  =>
   { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'G' },

    STRIP_COMMENT_ONLY =>

    { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'SC' },

   MASS_TAG_ONLY =>
   { ARGCOUNT => ARGCOUNT_HASH, ALIAS => 'M' },

   RENAME_ONLY =>
   { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => 0, ALIAS => 'RO' },

   RENAME_MAX_CHARS =>

   { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 30},

   RENAME_FORMAT =>
   { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => '%a-%t-%n-%c-%s.mp3'},

   RENAME_BADCHARS =>
   { ARGCOUNT => ARGCOUNT_LIST, ALIAS => 'RB' },

   RENAME_REPLACECHARS =>

   { ARGCOUNT => ARGCOUNT_LIST, ALIAS => 'RR' },

   RENAME_REPLACEMENT =>
   { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => '_' },

   FREEDB_HOST =>
   { ARGCOUNT => ARGCOUNT_ONE, DEFAULT => 'http://www.freedb.org', },

   OR =>

   { ARGCOUNT => ARGCOUNT_NONE, DEFAULT => '0', },

   SEARCH_ALL()  =>
   { ARGCOUNT => ARGCOUNT_LIST, ALIAS => 'A' },
      );

foreach my $search (keys %freedb_searches)
{
 $config->define($search => {
      ARGCOUNT => ARGCOUNT_LIST,
      ALIAS => $freedb_searches{$search}->{abbrev},
      });
}
$config->args();

$config->file($config->CONFIG_FILE())
 if $config->CONFIG_FILE();

unless (scalar @{$config->RENAME_BADCHARS()})
{
 push @{$config->RENAME_BADCHARS()}, split(//, "\"`!'?&[]()/;\n\t");
}

unless (scalar @{$config->RENAME_REPLACECHARS()})
{
 push @{$config->RENAME_REPLACECHARS()}, split(//, " ");
}

if ($config->HELP())
{
 print <<EOHIPPUS;
$0 [options] File1.mp3 File2.mp3 ...

Options:
 -help (-h)          : print this help
 -config_file (-f) N : use this config file, see AppConfig module docs for format
 -debug (-d) N       : print debugging information (level N, 0 is lowest)
 -dump               : just dump the list of albums and tracks within them
 -dryrun (-n)        : do everything but modify the MP3 files
 -freedb_host H      : set the FreeDB host, default "www.freedb.org"
 -or                 : search for keyword A or keyword B, not A and B as usual

 -accept_all (c)     : accept all search results for consideration for each file,
                       also accept all renames without asking

 -rename_badchars (-rb) A -rb B     : characters A and B to remove when renaming

 -rename_replacechars (-rr) A -rr B : characters A and B to replace
                                      when renaming

 -rename_maxchars N : use at most this many characters from a tag
                      element when renaming, default: ${\$config->RENAME_MAX_CHARS()}

 -rename_replacement X : character to use when replacing,
                      default: [${\$config->RENAME_REPLACEMENT()}]

 -rename_format (-f) F : format for renaming; default "${\$config->RENAME_FORMAT()}"
                         %a -> Artist
                         %t -> Track number
                         %n -> Album name
                         %c -> Comment
                         %s -> Song title

 -guess_track_numbers_only (-g) : guess track numbers using the file
                     name, then exit

 -rename_only (-ro)  : rename tracks using the given format (see
                       -rename_format), then exit

 -mass_tag_only (-m) A=X -m B=Y : mass-tag files (tag element A is X,
                                  B is Y), then exit (tag elements
                                  available: @supported_frames)

 -strip_comment_only (-sc) : strip comments and URLs, then exit

Repeatable options (you can specify them more than once, K is the keyword):

 -all (-a)    K : search everywhere
 -artist (-i) K : search for these artists
 -title (-t)  K : search for these titles
 -track (-k)  K : search for these tracks
 -rest (-r)   K : search for these keywords everywhere else

Note that the repeatable options are cumulative, so artist A and title
B will produce matches for A and B, not A or B. In the same way,
artist A and artist B will produce matches for A and B, not A or B.
If you want to match A or B terms, use -or, for instance:

$0 -or -artist "pink floyd" -artist "fred flintstone"

EOHIPPUS

 exit;
}

# }}}

Yes, all that code just initialized the command-line options. With AppConfig, those options can be used and modified throughout the program; there are many benefits to using AppConfig that are outside the scope of this article (see Resources for more information on AppConfig).

Also, I use the entries in the %freedb_searches hash to create the appropriate configuration options, which makes life easier for the user and for the programmer. I can also use the entries in the %freedb_searches hash to create the appropriate configuration options.

After loading a configuration file, if the user specified it, I populate the character replacement and bad character arrays with a sensible default.

Finally, I handle the -help switch. Note how the default values for various options are printed inside the help text, using variable interpolation. This makes for a very readable help message. I always update my help message right after I added a feature, and sometimes even before. I believe that help should be synchronized with the functionality of the program, otherwise the program is confusing and the help is misleading. The autotag.pl program in particular needs more documentation -- a POD-style documentation would be nice, and that may be in place by the time you read this article. POD documentation is a part of the script, so downloading autotag.pl (see Resources) will include the POD documentation if I have written it already.



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

Page:  1 2 3 4 5 6 Next Page: ID3v2 tag-related functions

First published by IBM developerWorks


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