#!/usr/bin/perl -w # Perl script to rip, encode, tag and burn audio cds # (C) 2004 S. B. # requires perl-modules CDDB_get, MP3::Tag and Getopt::Long. # consider using: # $ perl -MCPAN -e shell # cpan> install # for `--mp3' the encoder `gogo' is needed # http://homepage1.nifty.com/herumi/gogo_e.html # for `--aac' (m4a) the encoder `faac' with MP4 support is needed # http://www.audiocoding.com/ # `cdda2wav' is needed for ripping the `.wav'-files: # http://www.cdda2wav.de/ # `cdrecord' is needed for burning audio-roms # http://www.fokus.gmd.de/research/cc/glone/employees/joerg.schilling/private/cdrecord.html # The tool count, a little c-programm used to determine the number # of tracks, can be found at: # http://user.cs.tu-berlin.de/~borstel use CDDB_get qw( get_cddb ); use MP3::Tag; use Getopt::Long; use strict; my (%cddb_config, %my_config, %cd, $artist, $album, $directory, $count, @tracklist, @tracknumbers, @def_tracknumbers); # following settings may be changed :)) $cddb_config{CDDB_HOST}="freedb.freedb.org"; # set cddb host $cddb_config{CDDB_PORT}=8880; # set cddb port $cddb_config{CDDB_MODE}="cddb"; # set cddb mode: cddb or http $cddb_config{CD_DEVICE}="/dev/hdb"; # set cd device $cddb_config{CD_WRITER}="1,0,0"; # set writer device as scsibus,target,lun $cddb_config{input}=1; # user interaction to choose the right cddb entry $my_config{mp3}=0; # turn on/off default mp3 encoding $my_config{aac}=1; # turn on/off default aac encoding $my_config{burn}=0; # turn on/off default burning $my_config{net}=1; # turn on/off default netlookup $my_config{clean}=1; # turn on/off default cleaning $my_config{tag}=1; # turn on/off default tagging $my_config{help}=0; # turn on/off help and exit as default behaviour $my_config{fb_artist}="unknown"; # fallback artist-name $my_config{fb_album}="unknown"; # fallback album-titel $my_config{fb_trackname}="track"; # fallback base-trackname $my_config{wav_basename}="track"; # default name for *.wav and *.inf files @def_tracknumbers=("1-"); # the default value for the tracks to consider sub cddb_or_manuel { if ($my_config{net}){ print "\n\n\n\ntrying to querry cddb-server\n"; %cd=get_cddb(\%cddb_config); defined($cd{title}) or die "no cddb entry found"; $artist=&rename($cd{artist}); $album=&rename($cd{title}); @tracklist=@{$cd{track}}; $directory=$artist."--".$album; } else { my $n; $artist=$my_config{fb_artist}; $album=$my_config{fb_album}; $directory=$artist."--".$album; for ($n=0; $n<$count; $n++) {$tracklist[$n]=$my_config{fb_trackname};} } } sub cdda2wav { my $tracknumber=$_[0]; my $wavbasename=$my_config{wav_basename}.$tracknumber; my $command="cdda2wav -I cooked_ioctl -q -Q -O wav -t ".$tracknumber." -D ".$cddb_config{CD_DEVICE}." ".$wavbasename.".wav >/dev/null 2>&1"; !system $command or die "something went wrong running the following command:\n$command"; return $wavbasename.".wav"; } sub rename { my $string=$_[0]; # replace '/' by '-' $string=~s/\//-/g; # remove everything but wordchars,spaces,braces and a bunch other $string=~s/[^\w\' \(\)-]//g; # replace ' '+ with '_' $string=~s/ +/_/g; return $string; } sub gogo_tag { my $wavbasename=$_[0]; my $trackname=$_[1]; my $tracknumber=$_[2]; my $directory=$_[3]; my ($filename,$path,$mp3,$id3v2,$tracknumberstring,$command); if ($tracknumber<10){ $tracknumberstring="0".$tracknumber; } else { $tracknumberstring=$tracknumber; } $filename=$tracknumberstring."--".$trackname.".mp3"; $path=$directory."/".$filename; $command="gogo -silent ".$wavbasename.$tracknumber.".wav \"".$path."\" >/dev/null 2>&1"; !system $command or die "something went wrong running the following command:\n$command"; if ($my_config{tag}){ $mp3 = MP3::Tag->new($path); $id3v2 = $mp3->new_tag("ID3v2"); # $id3v2->add_frame("TIT2",$trackname); $id3v2->add_frame("TIT2",$tracklist[$tracknumber-1]); # $id3v2->add_frame("TPE1",$artist); $id3v2->add_frame("TPE1",$cd{artist}); # $id3v2->add_frame("TALB",$album); $id3v2->add_frame("TALB",$cd{title}); # $id3v2->add_frame("TRCK",$tracknumberstring); $id3v2->add_frame("TRCK",$tracklist[$tracknumber-1]); $id3v2->write_tag(); } return $filename; } sub do_faac { my $wavbasename=$_[0]; my $trackname=$_[1]; my $tracknumber=$_[2]; my $directory=$_[3]; my ($filename,$path,$mp3,$id3v2,$tracknumberstring,$command); if ($tracknumber<10){ $tracknumberstring="0".$tracknumber; } else { $tracknumberstring=$tracknumber; } $filename=$tracknumberstring."--".$trackname.".m4a"; $path=$directory."/".$filename; $command="faac -w --artist \"".$cd{artist}."\" --title \"".$tracklist[$tracknumber-1]."\" --album \"".$cd{title}."\" --track \"".$tracknumber."/".$count."\" -o \"".$path."\" ".$wavbasename.$tracknumber.".wav"; !system $command or die "something went wrong running the following command:\n$command"; return $filename; } sub trackrange2list { my($string,$max,$min)=@_; if ($string =~ /^(\d+)$/ && $1<=$max && $1>=$min) {return ($1);} if ($string =~ /^(\d+)-(\d+)$/ && $2<=$max && $1>=$min) { return ($1 .. $2);} if ($string =~ /^(\d+)-(\d+)$/ && $2>$max && $1>=$min) { return ($1 .. $max);} if ($string =~ /^(\d+)-(\d+)$/ && $2<=$max && $1<$min) { return ($min .. $2);} if ($string =~ /^(\d+)-(\d+)$/ && $2>$max && $1<$min) { return ($min .. $max);} if ($string =~ /^(\d+)-$/ && $1<=$max && $1>=$min) {return ($1 .. $max);} if ($string =~ /^(\d+)-$/ && $1<$min) {return ($min .. $max);} if ($string =~ /^-(\d+)/ && $1<=$max && $1>=$min) {return ($min .. $1);} if ($string =~ /^-(\d+)/ && $1>$max) {return ($min .. $max);} return (); } sub usage { print "usage : $0 [options]\n\n"; print " -m, --mp3 encode mp3 files\n"; print " --nomp3 don't encode mp3 files [default]\n"; print " --aac encode aac files [default]\n"; print " --noaac don't encode aac files\n"; print " -i, --id3tag add id3v2 tag [default]\n"; print " --noid3tag don't tag the mp3s\n"; print " -n, --net try to lookup cd info from a cddb-server [default]\n"; print " --nonet don't try to lookup cd info from a cddb-server\n"; print " --artist= set the artist name to . (Used if --nonet is set)\n"; print " --album= set the album name to . (Used if --nonet is set)\n"; print " --basename= set the base name for all mp3 to . (Used if\n"; print " --nonet is set)\n"; print " -b, --burn compile a cd from the wav files\n"; print " --noburn don't compile a cd from the wav files [default]\n"; print " -c, --clean remove *.wav nor *.inf when done [default]\n"; print " --noclean don't remove *.wav nor *.inf when done\n"; print " -t, --tracks= is the comma seperated list of tracks to consider:\n"; print " \"1,2,3,7,9\" for the first 3 plus track 7 and 9\n"; print " \"7,1-3,9\" as before\n"; print " \"7,-3,9\" as before\n"; print " \"7,-3,9-\" as before plus all tracks with a number >9\n"; print " -h, --help display this help and exit\n\n"; } sub main { my ($n,$command); my $writtentracks=""; @tracknumbers=(); GetOptions ('mp3!' => \$my_config{mp3}, 'aac!' => \$my_config{aac}, 'burn!' => \$my_config{burn}, 'net!' => \$my_config{net}, 'id3tag!' => \$my_config{tag}, 'clean!' => \$my_config{clean}, 'help' => \$my_config{help}, 'artist=s' => \$my_config{fb_artist}, 'album=s' => \$my_config{fb_album}, 'basename=s' => \$my_config{fb_trackname}, 'tracknumbers=s'=>\@tracknumbers); # maybe only display help if ($my_config{help}) {&usage; exit(0)} # find out the number of tracks on the cd $count=0; $command="./count $cddb_config{CD_DEVICE} "; $count=`$command`; if ($count==0) {die "something went wrong running the following command:\n$command";} # evaluate the tracks to deal with my @tmp=(); # was something set by the user? my $length=@tracknumbers; if ($length==0) # if not take it from @def_tracknumbers {@tracknumbers=@def_tracknumbers;} # build a nice list @tracknumbers = split(/,/,join(',',@tracknumbers)); # and parse this list foreach (@tracknumbers) { @tmp=(@tmp,trackrange2list($_,$count,1)); } # make it a unique list from http://perlmonks.thepen.com/280658.html @tmp = keys %{{ map {$_ => 1} @tmp }}; # sort the list @tracknumbers=sort {$a <=> $b} @tmp; # querry cdddb or manually set some constant values &cddb_or_manuel; # create the dir for the mp3 files print "trying to create directory $directory\n"; mkdir "$directory", 0755 or warn "Can't make directory: $!"; # ripping the wav files print "trying to rip wav files\n"; foreach $n (@tracknumbers){ my $wavfile=&cdda2wav($n); print "riped $wavfile\n"; $writtentracks=$writtentracks." ".$wavfile; } # encoding and tagging the (hopefully) created wav files if ($my_config{mp3}){ print "trying to encode mp3 files\n"; foreach $n (@tracknumbers){ my $trackname=&rename($tracklist[$n-1]); my $mp3file=&gogo_tag($my_config{wav_basename},$trackname,$n,$directory); print "encoded $mp3file\n"; } } # encoding and tagging the (hopefully) created wav files if ($my_config{aac}){ print "trying to encode aac files\n"; foreach $n (@tracknumbers){ my $trackname=&rename($tracklist[$n-1]); my $aacfile=&do_faac($my_config{wav_basename},$trackname,$n,$directory); print "encoded $aacfile\n"; } } # write back to the writer # make sure to remove magicdev it could intercept the burner and screew the media # $> rpm -e magicdev # also if you want to run this script as non-root run # $>chmod u+s /usr/bin/cdrecord if ($my_config{burn}){ print "trying to write burn cd\n"; my $command="cdrecord dev= \"".$cddb_config{CD_WRITER}."\" gracetime=2 fs=4096k driveropts=burnfree,noforcespeed -v -useinfo speed=8 -eject -pad -audio $writtentracks"; system $command; } # cleaning if ($my_config{clean}){ print "removing *.wav *.inf\n"; $command="rm -f *.wav *.inf"; system $command; } } &main; 1;