At last, the main loop
For each file given on the command line (in
@ARGV), I get the
and create it if necessary. For files that can't abide an
for various reasons, I print an informative message and skip them.
There is a difference here between files that are simply not in
existence -- for instance, a directory name given as an MP3 file -- and
files that are not accessible, such as a file with
%discs_of_interest hash is a copy of
%discs. I tried using
modules for approximate (fuzzy) string matching to narrow the
selection of disks that are interesting. For instance, I tried
matching the album name fuzzily (with 50% to 90% precision), and no
setting worked well. The problem is that some words like "love" are
very common, while other words like "U2" are too short. There may be
a good algorithm to narrow choices, and I've left the
%discs_of_interest hash in place in case that algorithm comes about,
but it seems from my personal experience that the best thing to do is
let the human brain pick an option in 0.01 seconds. Sometimes, trying
to solve a problem with a computer is simply not as efficient as
a few million years of evolution.0
Now comes the
while(1) loop. This is an endless loop that reflects
how the user often will cycle between choices until he's made only
one. I could have written this loop with variable controls, but
last() in an endless loop seemed more natural.
I get a single album with the following loop:Listing 8. Choosing only one album
If only one album exists anyhow, I just take it. Otherwise, I get the
lists of disks of interest using the
ask4discurls() function. Note that I
print out the file tag info before that question with
print_tag_info(), so the user is reminded of the file's information.
Users are naturally forgetful, so every shortcut and reminder the
programmer can offer them is appreciated. Users are also imperfect,
so I don't assume that just because I told them to pick one album, that
they did so. With a GUI, similar selection rules can be enforced on
listboxes -- but in the text interface autotag.pl has, input validation
has to be done this way. Actually, that's not entirely true: There
are some CPAN modules that can help here, but the scale and scope of
autotag.pl did not seem to merit a text-mode UI framework.
If no album was chosen, I skip to the next file.
$disc contains the album that is specifically applicable to the
current file the user is examining.
Another endless loop: Until the user has picked a suitable track
number, I simply can't go on. The track number is like Moby Dick to
autotag.pl's Ahab. It must be found, or else all this work is
meaningless. I print out the tag info again to remind the user what
it is we're talking about, then print out the disk information with
outstd() function from
The default track number the user can enter is guessed from the file name or from the previously existing track. This is always just a suggestion, but if the user wants to accept it he can just hit Enter. Now that's service.
When the track number is found, and it's good, the tagging of the MP3 file is done:Listing 10. The tagging is done
First of all, recall I'm in an endless
while(1) loop here. The
last() at the end means that if I got this far, I should exit the loop.
I start with the
make_tag_from_freedb() function to make an
FreeDB tag. This is hidden into a function because it's not
a straightforward mapping.
Given the new tag and a final "yes" from the user, I proceed to the
tagging of the file. The user now has a chance to modify each
individual tag element. Each choice there is stored in the input
object history (read the
Term::Readline documentation for details).
That way, the user can press Up Arrow and grab the old inputs
instead of having to retype them. Finally, and this really makes the user's
life easier when tagging multiple files, I store the modified
information that should persist back in the
%$disc hash. Thus, if a
user modifies the artist of an album, on the next file the modified
name will now be the default name.