#!/usr/bin/perl
#
# titlefix.pl
#
# Author: resident alian
#
# Date created: 06 Aug 2004
#
# Date modified: 16 Oct 2006
#
# Description: titlefix renames files, correcting titles with standard English
# title case -- all middle words in an artist or title are
# capitalized except: articles, prepositions, and conjunctions
# less than 5 letters long in the artist or title name
# When called as titleid3, it uses the formats described below to tag
# mp3s with ID3V2 and ID3V1 tag info, but does no renaming (for
# when case is to be preserved).
# When called as titlefixid3, it does both renaming and tagging.
# When called as single[fix][id3], it does the same as its title*
# counterpart without looking for a track number.
#
# License: GPL
#
# Homepage: http://titlefix.sourceforge.net/
#
# Revision history:
# 16 Oct 2006: Version 0.5.3: fixed '$' in filename when tagging; fixed help;
# fixed processing of files with extensions with lengths other than 3;
# added processing for flac (same as mp3)
# 20 Sep 2006: Version 0.5.2: added protection for "single"-style files (i.e.
# no 2-digit track number prefix in the filename) to be treated as
# "single" files even when title* is called; added bitrate/"alt" suffix
# removal for title tag (filename left unchanged); added "o'" to list;
# fixed renaming for directories and regular file with no extension
# 07 Sep 2006: Version 0.5.1: fixed singlefix renaming; added bitrate/"alt"
# 16 Aug 2006: Version 0.5: changed progname checking to use regexp (in case of
# .pl suffix); added singlefix functionality (no NN prefix); fixed
# tagging without renaming and help message -- d'oh!!; added test
# in Makefile (call by "make test" in source folder)
# 15 Aug 2006: Version 0.4: used symbolic links for specific functionality;
# disabled renaming for id3-only variant; made help message the default
# action with no arguments; created manpage
# 29 Jun 2006: used Perl builtin rename instead of shell command mv; made strict
# 16 Nov 2005: Version 0.3: removed .pl extension (duh on my part), changed
# example file name
# 04 Jun 2005: removed "but" from list
# 24 Feb 2005: fixed case where no artist is given (doesn't add fabricated
# artist)
# 11 Feb 2005: Version 0.2: fixed help text, fixed matching all middle words
# (i.e. all words not next to another word or number (except first word
# in artist name))
# 02 Feb 2005: added "As" to list
# 01 Feb 2005: replaces two single quotes in filename with double quote in tag
# 27 Jan 2005: expanded (so far) to allow three-song title (2 dashes in title)
# 25 Jan 2005: removed "yet" from list, assumed .mp3 extension, used -h for
# help and modularized (with subroutines)
# 21 Jan 2005: added "De", "Du", "En", "Por", "Y" to list
# 19 Jan 2005: removed "so" from list
#
use strict;
my $debug = 0;
my $do_stuff = 1;
my @lc_words = qw/a an the and for nor or as at by for from in o' of on to with de du en por y/;
my @uc_words = qw/A An The And For Nor Or As At By For From In O' Of On To With De Du En Por Y/;
my $fn_ex = "12 Front 242 - One - With The Fire.mp3";
my $fn_ex_mod = "12 Front 242 - One - With the Fire.mp3";
my $fn;
my @files;
my $progname = $0;
my $do_rename = 0;
my $do_tag = 0;
my $use_tn = 1;
my $file_to_process = "file_to_rename";
$progname =~ s/^.*\///g;
## customize functionality script's called filename ##
if (($progname =~ /^titlefixid3/) or ($progname =~ /^singlefixid3/))
{
$do_rename = 1;
$do_tag = 1;
$file_to_process = "mp3_file_to_rename_and_tag";
}
elsif (($progname =~ /^titleid3/) or ($progname =~ /^singleid3/))
{
$do_rename = 0;
$do_tag = 1;
$file_to_process = "mp3_file_to_tag";
}
else ## assume titlefix ##
{
$do_rename = 1;
$do_tag = 0;
$file_to_process = "file_to_rename";
}
if ($progname =~ /^single/)
{
## assume no track number prefix NN in filename ##
$use_tn = 0;
$fn_ex =~ s/^...//g;
$fn_ex_mod =~ s/^...//g;
}
### fixit start ###
sub fixit
{
my ($fn) = @_;
my $art;
my $tt;
my $tn;
my $fn_mod;
my $cmd;
my $i;
my $use_art = 1;
my $use_tn_this_file = $use_tn;
## position of (first) " - ":
my $dashpos = index($fn, " - ");
## position of (last) ".":
my $dotpos = rindex($fn, ".");
my $ext;
my $ttendpos;
if ($dotpos == -1)
{
$ext = "";
$ttendpos = -1;
}
else
{
$ext = substr($fn, $dotpos + 1);
$ttendpos = -1 - length($ext);
$ext = ".$ext";
}
## track number:
## protect accidental usage of "title" variants where "single" variant
## is more appropriate (i.e. no 2-digit track number prefix found):
if (!($fn =~ /^[0-9][0-9] /))
{
$use_tn_this_file = 0;
}
if ($use_tn_this_file)
{
$tn = substr($fn, 0, 2);
}
else
{
$tn = "";
}
## artist and track title:
if ($dashpos eq "-1")
{
$use_art = 0;
$art = "";
if ($use_tn_this_file)
{
if ($ext eq "")
{
$tt = substr($fn, 3);
}
else
{
$tt = substr($fn, 3, $ttendpos);
}
}
else
{
if ($ext eq "")
{
$tt = $fn;
}
else
{
$tt = substr($fn, 0, $ttendpos);
}
}
}
else
{
if ($use_tn_this_file)
{
$art = substr($fn, 3, $dashpos - 3);
}
else
{
$art = substr($fn, 0, $dashpos);
}
if ($ext eq "")
{
$tt = substr($fn, $dashpos + 3);
}
else
{
$tt = substr($fn, $dashpos + 3, $ttendpos);
}
}
## renaming code ##
if ($do_rename)
{
## do word replacement
for ($i = 0; $i <= $#lc_words; ++$i)
{
my $uw;
my $lw;
$uw = @uc_words[$i];
$lw = @lc_words[$i];
#if ($debug)
#{
#print("*** replacing \"$uw\" with \"$lw\"\n");
#}
$art =~ s/(\w )$uw( \w)/\1$lw\2/g;
$tt =~ s/(\w )$uw( \w)/\1$lw\2/g;
}
## check if no artist
if ($art eq "")
{
$tt =~ s/^ //g; ## don't remember why this is here!
if ($use_tn_this_file)
{
$art = substr($fn, 3, $dashpos - 3);
$fn_mod = "$tn $tt$ext";
}
else
{
$art = substr($fn, 0, $dashpos - 3);
$fn_mod = "$tt$ext";
}
}
else
{
if ($use_tn_this_file)
{
$fn_mod = "$tn $art - $tt$ext";
}
else
{
$fn_mod = "$art - $tt$ext";
}
}
if ($fn eq $fn_mod)
{
print("(file \"$fn\" OK)\n");
}
else
{
if ((-e $fn_mod) and (lc($fn) ne lc($fn_mod))) ## unlikely ##
{
print("*** destination $fn_mod exists!\n");
}
else
{
if ($do_stuff)
{
rename $fn, $fn_mod;
}
else
{
print("renaming \"$fn\" to \"$fn_mod\"\n");
}
}
}
}
## '' => " conversion
$art =~ s/\'\'/\\\"/g;
$tt =~ s/\'\'/\\\"/g;
if ($debug)
{
print(STDERR "DEBUG: dashpos: $dashpos
DEBUG: tn: $tn
DEBUG: art: $art
DEBUG: tt: $tt
");
}
## tagging code ##
## protect non-mp3s and non-flacs from tagging attempts ##
if (!(($fn =~ /\.[Mm][Pp]3$/) or ($fn =~ /\.[Ff][Ll][Aa][Cc]$/)))
{
$do_tag = 0;
}
if ($do_tag)
{
my $tag_art = "";
my $tag_tn = "";
my $tag_tt = $tt;
if (!$do_rename)
{
$fn_mod = $fn;
}
if ($use_art)
{
$tag_art = " -a \"$art\"";
}
if ($use_tn_this_file)
{
$tag_tn = " -T \"$tn\"";
}
else
{
## don't tag bitrates specified in files ##
## just to be sure, only do this for "single" mp3s ##
$tag_tt =~ s/ (112|128|160|192|224|256|320|vbr|alt)$//g;
}
$cmd = "id3v2$tag_tn$tag_art -t \"$tag_tt\" \"$fn_mod\"";
## escape any dollar signs (otherwise interpreted as variables) ##
$cmd =~ s/\$/\\\$/g;
if ($do_stuff)
{
print("$cmd\n");
system($cmd);
}
else
{
print("$cmd\n");
}
}
} ### fixit ###
### main start ###
## check for test option ##
if (($ARGV[0] =~ /^-t/) or ($ARGV[0] eq "--test"))
{
$do_stuff = 0;
if (($ARGV[0] eq "--test") or ($ARGV[0] eq "-t"))
{
shift @ARGV;
}
if ($ARGV[0] =~ /^-t/)
{
$ARGV[0] =~ s/^-t/-/g;
}
}
## check for help option or no arguments ##
if (($ARGV[0] =~ /^-h/) or ($ARGV[0] eq "--help") or ($ARGV[0] eq ""))
{
my $tnplaceholder = "NN ";
if (!$use_tn)
{
$tnplaceholder = "";
}
print("
usage: $progname [-t] [$file_to_process | -h | -l | -a] ...
for example:
\t$progname \"$fn_ex\"
results in this file name:
\t\"$fn_ex_mod\"
");
if ($do_tag)
{
print("
note: this format must be used for each file:
\t${tnplaceholder}Artist - Title.mp3
special note: if this format is used, no artist will be tagged:
\t${tnplaceholder}Title.mp3
");
}
print("
switches:
\t-t,
\t--test\tprints processing commands but doesn't execute them
\t-a,
\t--all\tprocesses *.mp3 in the current directory
\t-f,
\t--all-flac\tprocesses *.flac in the current directory
\t-h,
\t--help\tdisplays help and exits (ignores remaining arguments)
\t-l,
\t--list\tprints list of words to make lowercase and exits (ignores remaining arguments)
also see manpage:
\tman $progname
");
exit(0);
}
## check for list option ##
if (($ARGV[0] =~ /^-l/) or ($ARGV[0] eq "--list"))
{
print("
words to make lowercase:
@lc_words
");
exit(0);
}
## check for all-mp3 option ##
if (($ARGV[0] eq "-a") or ($ARGV[0] eq "--all"))
{
@files = glob("*.[Mm][Pp]3");
foreach $fn (@files)
{
&fixit($fn);
}
}
elsif (($ARGV[0] eq "-f") or ($ARGV[0] eq "--all-flac"))
{
@files = glob("*.[Ff][Ll][Aa][Cc]");
foreach $fn (@files)
{
&fixit($fn);
}
}
else
{
## file argument(s) => just do that/those file(s) ##
foreach $fn (@ARGV)
{
&fixit($fn);
}
}
### main end ###
syntax highlighted by Code2HTML, v. 0.9.1