Teodor Zlatanov
(tzz@bu.edu) programmer, Gold Software Systems 2004 February
Every self-intoxicated computer and music enthusiasts need to be able to manipulate the factual standards for MP3-entertaining digital music. In this article, TED describes several ways to use autotag.pl applications to manage and manipulate (search, tag, rename, and notes, etc.) MP3. The TED describes this app in detail to the reader, describing how the CPAN module enables the application.
For music enthusiasts now understand the computer, manipulating the MP3 file is a must have skill. Although other music file formats have existed and flourished, this article is mainly discussed with MP3 format, as well known, it is the most popular format today. However, the general methods described herein can also be used to process other music file formats that allow the label (TAG). In fact, many file formats using labels can benefit from the autotag.pl program in this article. Welcome to make recommendations.
This article will generalize the issue of Perl, especially paying attention to the manipulation of MP3 files, and detail the autotag.pl application.
Although there is already MP3 :: Info, Mp3 :: id3lib, Musicbrainz :: Client and Audiofile :: Identify :: Musicbrainz module, and these modules may be useful, but I only use the main reason for MP3 :: ID3lib because it needs ID31IB software (see Resources). Although MP3 :: Info is written in pure Perl and is also very simple, I found MP3 :: TAG function more powerful. The reason did not use Musicbrainz :: Client and Audiofile :: Identify :: MusicBrainz because MusicBrainz appears to be a database that is more incomplete than Freedb. At the end of this article, the reader will introduce the ID3 label fusion module and the track information module. The experience I have experienced and failed, indicating that MP3 :: Tag and WebService :: freedb are the best module.
Although the CDDB (Gracenote) disk library is very comprehensive, I still didn't choose to use it. GraceNote is a proprietary database with a list of CD tracks (only companies that allow search for databases, not available to download). Only with the early days of CDDB in GraceNote, volunteers contributed a considerable part of the contents of these databases. FreeDB is a free, unlimited CD track database provided by volunteers through organized efforts. The entire content of the FREEDB database can be downloaded, no copyright restrictions - therefore, if you like, you can build your own FREEDB server.
The module I don't use is not because these modules are certain, so if you like it, you can use them. Based on personal experience and the above reasons, I just prefer MP3 :: Tag and WebService :: Freedb. The actual read and write tag is abstract in a function, so if you read and write the MP3 tag with different modules, you don't need to change a lot.
I should also mention that in the XTERM and ETERM terminal simulator inside Linux, Term :: readline :: GNU module is better than the default module Term :: readline :: Perl can be better. If you notice some strange behavior when prompted to enter the desired text, you may be installed on Term :: Readline.
MP3 label is a brief introduction to music. The computer appears. The computer speed is very slow and only a beep can be issued. Even with a very sad tool such as a PC speaker (, I really want to be Apple and Amiga users), we also write programs to generate music, use in games and entertainment. After that, the sound card is getting better and better, and the deafening sounds from the audio and THX-certified speakers are issued everywhere. At the same time of hardware development, there is also a lot of sound formats. .MID is suitable for MIDI tones, .voc, .mod, .wav, etc. The proprietary MP3 format (involving many patents owned by the German Fraunhofer Academy) Over time - it provides good compression and performance. There are also many formats in addition to MP3, and the famous Ogg Vorbis, but today MP3 seems to be the best choice for music storage formats. One advantage of the MP3 file is that you can use the ID3 tag to fill the label. The inside of the file is related to it - commonly referred to as metadata. Record set, artist, track name, annotation (using ID3 version 1.1) or even the number of tracks can be stored in the ID3 tag, as long as it does not exceed the specific number of characters. The subsequent version of ID3 version 1.1 is ID3 version 2 (referred to as ID3V2), except for simplicity, the latter has exceeded the former almost all aspects. ID3v2 can handle multiple languages, store anyging data in each tag element, or even store the picture as part of the label. But unfortunately, using ID3v2 To learn that Talb is the name of the album, Tit2 is the number of tracks. Ogg Vorbis format takes a long time to identify, with artists' elements called ... Wait it ... Artist! (Said fairly, this is just a convention - Ogg Vorbis annotation is formatted). Unfortunately, existing billion MP3 files cannot be converted to Ogg Vorbis format or any other format without loss of quality, so at least in the next five years, you may find that we will not only use The next "hot" format, but also use the MP3 file. I have been very trying to take a label as content from the actual ID3 tag. When the time is coming, modifying autotag.pl will be easy, so in addition to ID3, it also handles other filling labels. Basic autotag.pl function I put autotag.pl in different functions. First, contains_word_char () is a function that determines if some text contains a word (in / w in perl in Perl). This function will also process undefined values correctly, although the conventional expression outputs warning information when the unfined value is matched when the warning is opened. This function is extremely useful because it does not display warning information; in order not to use functions, you must check if the string is defined each time.
Listing 1. Contains_word_char () function
# {{{limited_word_char: return 1 if The text Contains a Word Character
Sub contains_word_char
{
MY $ text = shift @_;
RETURN $ TEXT & & LENGTH $ TEXT && $ TEXT = ~ m // w /;
}
#}}}
Next is the input routine. These procedures are quite long, and they try to handle most of the user interaction required by the program.
Listing 2. Get_tag () function
# {{{get_tag: get a id3 v2 tag, using v1 if neserySub get_tag
{
MY $ file = shift @_;
MY $ upgrade = shift @_;
MY $ mp3 = mp3 :: tag-> new ($ file);
RETURN Undef Unless $ MP3;
$ MP3-> GET_TAGS ();
MY $ TAG = {};
IF (EXISTS $ MP3 -> {ID3V2})
{
MY $ ID3V2 = $ mp3 -> {id3v2};
MY $ frames = ID3V2-> supported_frames ();
While ($ FNAME, $ longname) = Each% $ frames)
{
# Only grab the frames we know
NEXT UNSS $ supported_frames {$ fname};
$ TAG -> {$ fname} = $ ID3V2-> get_frame ($ fname);
DELETE $ TAG -> {$ fname} unless $ tag -> {$ fname};
$ TAG -> {$ fname} = $ tag -> {$ fname} -> {text} if $ fname EQ 'Comm';
$ TAG -> {$ fname} = $ tag -> {$ fname} -> {url} if $ fname eq 'wxxx';
$ TAG -> {$ fname} = 'unless $ TAG -> {$ fname};
}
}
Elsif (EXISTS $ MP3 -> {ID3V1})
{
WARN "No ID3 V2 Tag Info in $ File, Using The V1 TAG";
MY $ ID3V1 = $ mp3 -> {id3v1};
$ TAG -> {Comm} = $ ID3V1-> Comment ();
$ TAG -> {Tit2} = $ ID3V1-> Song ();
$ TAG -> {TPE1} = $ ID3V1-> Artist ();
$ TAG -> {Talb} = $ ID3V1-> Album ();
$ TAG -> {tyer} = $ ID3V1-> year ();
$ TAG -> {TRCK} = $ ID3V1-> Track ();
$ TAG -> {Tit1} = $ ID3V1-> Genre ();
IF ($ UPGRADE && Read_YES_NO ("UPGRADE ID3V1 TAG TO ID3V2 for $ file?", 1))
{
SET_TAG ($ FILE, $ TAG);
}
}
Else
{
WARN "No ID3 Tag Info in $ File, Creating IT";
$ TAG = {
Tit2 => '',
TPE1 => '',
Talb => '',
Tyer => 9999,
COMM => '',
}
}
Print "Got Tag", Dumper $ TAG
IF $ config-> debug ();
Return $ TAG;
}
#}}}
The only one slightly different function is READ_YES_NO (), which can give it a default parameter of Y or 1 to make the default value true, any other parameters make the default value false. This way, when the user presses the Enter key or space bar, I can let the read_yes_no () function accept different default values. In addition, the BACKSPACE button or the Delete key will reverse the default value. This code is not gorgeous, but it is very practical. Autotag.pl's starting part of the application autotag.pl starts in 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
Earch_all is a constant, when the user wants to search for a word anywhere, such as track name, artist name, etc., it will use it. In order to prevent someone from being changed to another value, I set it to constant, but it may have been hard coded as "all".
% freedb_searches hash map the FREEDB field to the information about them, including ID3v2 tag elements. For example, it illustrates how FreedB is called "Artist" that is called "TPE1" in the MP3 tag. The "Abbrev" field in this hash entry is used to define the command line switch, so that I can define a -Artist switch based on the% freedb_search information, which can be abbreviated as -i.
% info2freedb hash map all track Freedb fields in the disc to ID3V2 fields. They are not the fields in% freedb_search, which is a different mapping that indicates that "cdname" and "artists" (also known as "Talb" and "TPE1" for all tracks of a disc set. Are the same. I will use the% supported_frames to indicate which ID3v2 label elements I support. I generated this hash from this list instead of obtaining this list from this hash (explaining the difference between the differences between the two, no further details). When you get the label, you will use the Id3v2 tag, you have to use the supported framework (I just modify the supported framework).
Finally, in order to enable users to enter data across the app, I created a Term :: ReadLine object.
Below, I initially initialize the AppConfig option, so although I have added my burden, it is beneficial.
Listing 4. Initialization of AppConfig
# {{{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_search {$ 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-> rent_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 TRASE TRACKS
-REST (-R) K: Search for these Keywords Everywhere Else
Note That The Repeatable Options Are Cumulative, SO Artist A and Titleb 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 of these code is to initialize the command line option. These options can be used and modified throughout the program by using AppConfig. There are also many benefits to use Appconfig, but these contents are exceeded in this article (see Resources) for more information on AppConfig.
In addition, I also use the entries in the% freedb_searches to create the appropriate configuration options so that users and programmers can easily.
After loading the configuration file, if the user specifies it, use a meaningful default value to implant the word changing group and bad character array.
Finally, the -help switch is handled. Note that the default value inserted into different options through the variable is how to print out in the help text. This forms a very strong help information that readability. I always update help information immediately after adding new features (but sometimes it is before). I think that helping text should synchronize with the functionality of the program, otherwise people will not understand the procedure, and I don't know what to help text said. Autotag.pl programs require more document descriptions, and POD-style documents should be relatively appropriate. This document may already have it when you read this article. The POD document is part of the script, so the downloaded autotag.pl (see Resources) will include a POD document (if I have written it).
Functions associated with ID3v2 tags are the basic functions of autotag.pl. Give an MP3 file name, which will build a hash tag based on the file. If the label is just the ID3v1 tag, the get_tag () function will upgrade it to the ID3 tab free of charge (how good transaction!). If there is no ID3 tag, the get_tag () function will create one. Moreover, GET_TAG () knows the text and URL child elements of the Comm and WXXX elements, respectively.
Listing 5. Get_tag () function
# {{{get_tag: get a id3 v2 tag, using v1 if nesery
SUB GET_TAG
{
MY $ file = shift @_;
MY $ upgrade = shift @_;
MY $ mp3 = mp3 :: tag-> new ($ file);
RETURN Undef Unless $ MP3;
$ MP3-> GET_TAGS ();
MY $ TAG = {};
IF (EXISTS $ MP3 -> {ID3V2})
{
MY $ ID3V2 = $ mp3 -> {id3v2};
MY $ frames = ID3V2-> supported_frames ();
While ($ FNAME, $ longname) = Each% $ frames)
{
# Only Grab The Frames We knowd unless exists $ supported_frames {$ fname};
$ TAG -> {$ fname} = $ ID3V2-> get_frame ($ fname);
DELETE $ TAG -> {$ fname} unless $ tag -> {$ fname};
$ TAG -> {$ fname} = $ tag -> {$ fname} -> {text} if $ fname EQ 'Comm';
$ TAG -> {$ fname} = $ tag -> {$ fname} -> {url} if $ fname eq 'wxxx';
$ TAG -> {$ fname} = 'unless $ TAG -> {$ fname};
}
}
Elsif (EXISTS $ MP3 -> {ID3V1})
{
WARN "No ID3 V2 Tag Info in $ File, Using The V1 TAG";
MY $ ID3V1 = $ mp3 -> {id3v1};
$ TAG -> {Comm} = $ ID3V1-> Comment ();
$ TAG -> {Tit2} = $ ID3V1-> Song ();
$ TAG -> {TPE1} = $ ID3V1-> Artist ();
$ TAG -> {Talb} = $ ID3V1-> Album ();
$ TAG -> {tyer} = $ ID3V1-> year ();
$ TAG -> {TRCK} = $ ID3V1-> Track ();
$ TAG -> {Tit1} = $ ID3V1-> Genre ();
IF ($ UPGRADE && Read_YES_NO ("UPGRADE ID3V1 TAG TO ID3V2 for $ file?", 1))
{
SET_TAG ($ FILE, $ TAG);
}
}
Else
{
WARN "No ID3 Tag Info in $ File, Creating IT";
$ TAG = {
Tit2 => '',
TPE1 => '',
Talb => '',
Tyer => 9999,
COMM => '',
}
}
Print "Got Tag", Dumper $ TAG
IF $ config-> debug ();
Return $ TAG;
}
#}}}
The set_tag () function is a brother of a GET_TAG () function. It writes ID3V2 tags, view child elements from the COMM and WXXX framework. It accepts hashing references, such as those that may be generated by the GET_TAG () function.
Listing 6. SET_TAG () function
# {{{set_tag: set a id3 v2 Tag on a file
SUB set_tag
{
MY $ file = shift @_;
MY $ TAG = Shift @_;
MY $ mp3 = mp3 :: tag-> new ($ file);
Print Dumper $ TAG;
MY $ tags = $ mp3-> get_tags ();
MY $ ID3V2;
IF (Ref $ Tags EQ 'Hash' && EXISTS $ Tags -> {ID3V2})
{
$ ID3V2 = $ tags -> {id3v2};
Else
{
$ ID3V2 = MP3-> New_TAG ("ID3v2");
}
MY% OLD_FRAMES =% {$ ID3V2-> get_frame_ids ()};
Foreach MY $ FNAME (Keys% $ TAG)
{
$ ID3V2-> Remove_Frame ($ FNAME)
IF exists $ old_frames {$ fname};
IF ($ FNAME EQ 'WXXX')
{
$ ID3V2-> Add_frame ('wxxx', 'eNG', 'FREEDB URL', $ TAG -> {wxxx});
}
Elsif ($ FNAME EQ 'Comm')
{
$ ID3V2-> Add_frame ('Comm', 'ENG', 'Comment', $ Tag -> {Comm});
}
Else
{
$ ID3V2-> Add_frame ($ FNAME, $ TAG -> {$ FNAME});
}
}
$ ID3V2-> Write_tag ();
Return 0;
}
#}}}
The print_tag_info () function simply prints a summary of the output label. Unlike the Data :: Dumper function used in other parts of the autotag.pl program (must be pointed out, sometimes there is no need to use), the print_tag_info () function can provide a print output for the user's hash tag element. Note that this function accepts a hash reference, not the actual file name.
Give the file name and some possible ID3 tag information, the guess_track_number () function and the guess_artist_and_track () function do their best. Note that the guest_track_number () function knows that the number of tracks is very larger than 30.
Listing 7. Print_tag_info (), guest_track_number (), and guess_artist_and_track () functions
# {{{print_tag_info: Print The Tag Info
SUB Print_Tag_info
{
MY $ filename = shift @_;
MY $ TAG = Shift @_;
MY $ extra = shift @_ || 'TRACK INFO';
# argument checking
RETURN UNSS REF $ TAG EQ 'Hash';
Print "$ extra for '$ filename': / n";
Foreach (Keys% $ TAG)
{
Printf "% 10s:% s / n", $ _, $ tag -> {$ _};
}
}
#}}}
# {{{guess_track_number: Guess TRACK NUMBER from ID3 Tag and File Name
SUB GUESS_TRACK_NUMBER
{
MY $ filename = shift @_;
MY $ TAG = Shift @_ || Return Undef;
$ filename = basename ($ filename); # Directories Can Contain Confusing Data
# first try to guess the track number from the old tag
IF (exists $ tag -> {trck }&unds_word_char ($ tag -> {trck}))
{
MY $ N = $ TAG -> {trck} 0; # fix tracks like 1/10
Return $ N;
}
Elsif ($ filename = ~ m / ([012]? / d). * /. [^.] $ /)
# Now Look for Numbers in the filename (0 through 29)
{
Print "Guessed TRACK NUMBER $ 1 from filename '$ filename' / n"
IF $ config-> debug ();
Return $ 1;
}
Return Undef; # ix unlsef
}
#}}}
# {{{guess_artist_and_track: Guess Artist and TRAM FILE NAME
SUB GUESS_ARTIST_AND_TRACK
{
MY $ filename = shift @_;
MY $ artist;
MY $ track;
$ filename = basename ($ filename); # Directories Can Contain Confusing Data
IF ($ filename = ~ m / ([^ -_] {3,}) / s * - / s * (. {3,}) / s * /. [^.] $ /)
{
Print "Guessed Artist $ 1 from filename '$ filename' / n"
IF $ config-> debug ();
$ artist = $ 1;
$ track = $ 2;
}
Return ($ Artist, $ Track);
}
#}}}
I use the data returned from the FREEDB search to generate an anonymous hashes with the appropriate element. Although the webservice :: Freedb field and ID3v2 tag elements are experimental, it works well.
Listing 8. Make_tag_from_freedb () function
# {{{make_tag_from_freedb: make the id3 tag info from a freedb entry
SUB Make_tag_from_freedb
{
MY $ disc = shift @_;
MY $ track = shift @_;
# argument checking
Return Undef Unless $ TRACK = ~ m / ^ / d $ /;
# Note That The User Inputs TRACK "1" But WebService :: Freedb Gives US That
# TRACK AT POSITION 0, SO WE DECREMENT $ TRACK
$ track -;
Return Undef Unless EXISTS $ DISC -> {TrackInfo};
RETURN Undef Unless Exists $ Disc -> {TrackInfo} -> [$ TRACK];
MY $ track_data = $ disc -> {trackinfo} -> [$ track];
Return {
Tit1 => $ disc -> {genre},
Tit2 => $ track_data -> [0],
TRCK => $ track 1,
TPE1 => $ disc -> {artist}, talb => $ disc -> {cdname},
TYER => $ disc -> {year},
WXXX => $ disc -> {url},
Comm => $ disc -> {rest} || ',
}
}
#}}}
Large-scale filling labels, large-scale renames, stripping annotations, and guessing the number of tracks autotag.pl are the identification of the MP3 file. However, in this process, there is often a small adjustment to many group files. Enter four autotagging horsemen.
Stripping annotations are very simple procedures. I use GET_TAG () to get the hash tag, clear the COMM and WXXX fields, and write the label back with set_tag (). In fact, annotation peeling may have been completed through large-scale label, but this function is very frequent, so that I feel that it is necessary to set a separate option for it.
Guess the number of tracks is quite simple. Get a hash tag, use the guest_track_number () function on the file and hash tag, request acknowledge, and then write the tab back to the file.
Multiple keys (such as TALB) are loaded with multiple keys (such as TALB) on a series of files. E.g:
Autotadag.pl -mt "talb = best" * .mp3
Thus, all files with an MP3 extension specify a TALB value in its ID3v2 tag. When you have a directory of a full piece of artists, and when you want to mark all these music with the name of this artist, it is very suitable. Only supported label elements can be labeled in large-scale. Once again, this process is performed: Get the hash tag, modify it, and write it back. This is the purpose of making it easy for its maintenance.
Listing 9. Large-scale filling label, annotation stripping and guessing track quantity
# {{{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 $ 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_frames {$ _})
{
WARN "Unsupported Tag Element $ _ Requested for Mass Taging, 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;
}
#}}}
Oh, this introduces large-scale rename options. The reason why I stayed in this question is because this problem is most complicated. For each renaming parameter, I expressed each "%" in the tag value as "{{{%}}}", because it is not this, when followed by a special rename parameter, " % "Characters may be missed. For example, use "100% true" as a track name, let's take a look at how it becomes "100% TRACKNAMERUE", here TRACKNAME is the name I have obtained from the hash tag.
Large-scale renaming can also eliminate bad characters, in order to some characters with "_" to ensure a reasonable file name. Finally, unless the -c (accept_all) option is given by the command line, autotAG.PL will ask if the file can be renamed.
Listing 10. Large-scale rename
# {{{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 $ 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 character
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 Innecessary, 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 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;
}
#}}}
The Part 2 of the Conclusion will discuss the primary loop of AutoTag.pl and introduce the general usual usage of the program.
Reference
Read all the Perl articles in the "feature-rich Perl" series on developerWorks. Download AutotAG Application (To run the file, rename it as autotag.pl). The CPan Module Archive contains many useful Perl programs. TED Select free FREEDB Project as a backend database on proprietary CDDB / GraceNote. You can find resources about many different audio formats (including MIDI, MP3, and OGG VORBIS) in Open Directory. If you are using Term: Readline: Perl, you have encountered trouble, please try using Term: Readline: GNU. ID3LIB Library is used to read, write, and manipulate ID3v1 and ID3v2 tags. MP3 :: Tag Cpan Module is a label for reading the MP3 audio file. WebService :: freedb cpan module Gets an entry from FreedB by searching keywords. MP3 :: id3lib cpan module makes you edit and add ID3 tags in the MP3 file. The CPAN bundle installs the MusicBrainz client and the required module. Audiofile-Identify-MusicBrainz is another CPAN interface for the Musicbrainz service, which is a MusicBrainz client for pure Perl. IBM DeveloperWorks article "Thinking XML: Manage Metadata with MusicBrainz" discusses metadata issues for MusicBrainz services. AppConfig is an PerL5 module for managing application configuration information. You can learn more about AppConfig in the TED's Column "Application Configuration with Perl". Like to play audio? In IBM DeveloperWorks Articles "Introducing XHTML Voice - IBM's ProPosal To The W3C On Development", you can learn more about starting sound devices. You may want to read the article Voice Toolkit Preview on IBMalphaworks. Oh, try Music Sketcher, this is a graphical composing tool, which is written by the multimedia expert of IBM Research. About the author Teodor Zlatanov acquires a master's degree in computer engineering in Boston University in 1999. He began to make programmers since 1992, using Perl, Java, C and C . His interest is to open source work, dedicated to text analysis, 3-storey client-server database architecture, UNIX system management, CORBA, and project management. You can contact him via tzz@bu.edu.