Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

From MySQL MP3 Database to CD-R

by shockme (Chaplain)
on Jan 18, 2002 at 06:17 UTC ( [id://139711]=CUFP: print w/replies, xml ) Need Help??

I use Linux almost exclusively for everything I do. And while I appreciate X-Windows, I pretty much work from the command line.

I have a server which holds all of my MP3 files. Additionally, I have a MySQL database which catalogs all of my music. This database is composed of three tables. One is artist, which holds all of the information on a particular performer. Another is album, which contains all of the applicable information for a given album. The last is song, which contains the song name and where the song resides on my hard drive. This table also has foreign keys, which links the song to the album and artist.

From the command line, burning these MP3s back to CD can be a time-consuming process. First, I copy the particular album from its home directory to a working directory (/music/wavs/). Then I rename the files. For example, I'd rename "01 This Song.mp3" to 01.mp3. Afterwards, I run mpg123 to convert the MP3 files to .wav files, then sox on the WAV files to convert them to .cdr files. Finally, I run cdrecord to burn the CDR files to a CD-R.

It (finally) occurred to me that I could script this entire process. What follows is a perl script which does all of the foregoing. All I have to do is enter burncd -album="Album Name", and the script does the rest. It also allows an optional -artist parameter, for those multiple Greatest Hits albums.

I realize there may be better command-line utilities to accomplish my methods, but these methods have worked well for me over the years. I just think it's very cool that I was able to use Perl to automate the entire process.

#!/usr/bin/perl -w # # burncd.pl - gets album name from command line # pulls songs from mp3 database # copies songs to /music/wavs/ # renames songs to 01.mp3, 02.mp3, etc. format # calls mpg123 to convert mp3 to wav # calls sox to convert wav to cdr # calls cdrecord to burn CD-R # cleans up the /music/wavs/ directory use strict; use vars qw/$DBI/; use DBI; use Getopt::Long; use File::Basename; my $startTime = `date`; my $DSN = "DBI:mysql:mp3"; my $host = "localhost"; my $user = "userid"; my $passwd = "seekrit"; my $WavDir = "/music/wavs"; my ($ALBUM, $ARTIST); GetOptions("album=s" => \$ALBUM, "artist=s"=> \$ARTIST); # connect to database my $dbh = DBI->connect($DSN, $user, $passwd); die "Cannot connect: $DBI:errstr\n" if (!($dbh)); my $drh = DBI->install_driver("mysql"); # $ALBUM is required input if (!($ALBUM)) { print "correct usage is: burncd <options>\n"; print "where options equal:\n"; print "-album=\"album name\"\n"; print "-artist=\"artist name\"\n"; print "\n"; print "-artist is only necessary if there are two\n"; print "or more albums of the same name (i.e.,Greatest Hits)\n"; exit; } my $SQLText = "SELECT location FROM song, album WHERE fk_album_id = album.id AND album = \"$ALBUM\" ORDER BY location"; if ($ARTIST) { $SQLText = "SELECT location FROM song, album, artist WHERE fk_artist_id = artist.id AND fk_album_id = album.id AND album = \"$ALBUM\" AND performer = \"$ARTIST\" ORDER BY location"; } # this is what we're executing print "SQL: $SQLText\n"; my $sth = $dbh->prepare($SQLText); my $query = $sth->execute; my $FilesFound = "0"; my @list; # copy files from $Loc to $WavDir while (my $ref = $sth->fetchrow_hashref()) { $FilesFound = '1'; last if (!($FilesFound)); my $Loc = $ref->{'location'}; print "copying $Loc to $WavDir\n"; my @args = ("cp", "$Loc", "$WavDir"); system(@args) == 0 or die "system @args failed: $?"; my ($filename,$pathname,$suffixname) = fileparse($Loc,"\.[^.]*"); $filename = $filename . $suffixname; push @list, $filename; } if (!($FilesFound)) { print "No entries found for $ALBUM $ARTIST\n"; exit; } # set working dir to $WavDir chdir($WavDir); # rename files to 01.mp3, 02.mp3, etc. my @mp3list = process_files("mv", undef, "renaming", "\.mp3", @list); # call mpg123 to convert .mp3 to .wav my @wavlist = process_files("mpg123", "-qw", "converting", "\.wav", @mp3list); # call sox to convert each .wav to .cdr my @cdrlist = process_files("sox", undef, "converting", "\.cdr", @wavlist); # call cdrecord on *.cdr my @args = ("cdrecord -v dev=0,4,0 -audio *.cdr"); system(@args) == 0 or die "system @args failed: $?"; # clean up print "cleaning up $WavDir\n"; unlink @list; unlink @mp3list; unlink @wavlist; unlink @cdrlist; # done print "processing for $ALBUM complete\n"; my $endTime = `date`; print "start time: $startTime\n"; print "end time : $endTime\n"; exit; sub process_files { my ($function, $parm, $message, $extension, @list) = @_; my @returnlist; my @args; foreach my $file (@list) { my $Newfile = substr($file, 0, 2); $Newfile = $Newfile . $extension; print "($function) $message $file to $Newfile\n"; if ($function =~ /mpg123/) { @args = ("$function", "$parm", "$Newfile", "$file"); } else { @args = ("$function", "$file", "$Newfile"); } system(@args) == 0 or die "system @args failed: $?"; push @returnlist, $Newfile; } return(@returnlist); }

Update: Correct $FilesFound flag per dws's comment No. 1.

If things get any worse, I'll have to ask you to stop helping me.

Replies are listed 'Best First'.
Re: From MySQL MP3 Database to CD-R
by dws (Chancellor) on Jan 18, 2002 at 06:49 UTC
    First, nice piece of work. A couple of comments:
    1. Setting $FilesFound to 'Y' or 'N' isn't doing what you seem to expect it to. Logical tests of $FilesFound will always be true. Use 0 and 1 instead.

    2. You're going to run into trouble the first time you hit an album name that has quotes in it. You can avoid this by using bind variables in your SQL queries, and pass the actual arguments to prepare(). Let MySQL do the quoting for you.

    3. Setting RaiseError=1 when you connect might save you some SQL debugging grief.

    4. A slight stylistic nit. return isn't a function call. I find it clearer to write   return @returnlist; than to write   return(@returnlist); which, on a quick skim, is easy to miss if one is focusing on control flow. This is a point on which reasonable people disagree.
      1. Doh! It's been fixed. ++.

      2. This is a good point. For my stuff though, I take care of the quotes when the CD is ripped. Still, your point is well taken. Once again, ++.

      3. I'm just beginning to work my way through Programming the Perl DBI, and setting the RaiseError flag hasn't become second nature yet. ++.

      4. You say tomato, I say tomato. Hrm....that doesn't translate very well in print....;)

      If things get any worse, I'll have to ask you to stop helping me.

Re: From MySQL MP3 Database to CD-R
by spaz (Pilgrim) on Jan 18, 2002 at 06:59 UTC
    I have one small comment that might make your code more modular, remove:
    if ($function =~ /mpg123/) {
    If I'm not mistaken you can pass an argument of undef to system() without it freaking out too much.

    -- Dave
      This is a good point. I hadn't considered passing undef. I did try passing "", which tanked immediately on the mv (complaining that when moving multiple files, the destination must be a directory).

      I'll give undef a go the next time I burn. If it works, I'll modify the code accordingly. I absolutely hate the if statement in the subroutine. It completely ruins the modularity, so here's to hoping you're correct.

      If things get any worse, I'll have to ask you to stop helping me.

Re: From MySQL MP3 Database to CD-R
by talexb (Chancellor) on Jan 18, 2002 at 20:35 UTC
    As a style suggestion, I'd change process_files to use an argument hash. Simple to set up, and oh-so self-documenting. This would change
    sub process_files { my ($function, $parm, $message, $extension, @list) = @_; ...
    to
    sub process_files { my ( $SubArgs ) = @_; my $function = $SubArgs->{ function } my $message = $SubArgs->{ message } my $parm = $SubArgs->{ parm } || ""; # If no parameter, def +ault to "". my $extension = $SubArgs->{ extension }; my @list = @{ $SubArgs->{ list } }; ...
    The loop in process_files would change to be
    foreach my $file (@list) { my $Newfile = substr($file, 0, 2); $Newfile = $Newfile . $extension; print "($function) $message $file to $Newfile\n"; @args = ("$function", "$parm", "$Newfile", "$file"); system(@args) == 0 or die "system @args failed: $?"; push @returnlist, $Newfile; }
    Another note: I don't believe the double quotes are needed where you build your argument list.

    So now your calls look like this:

    # rename files to 01.mp3, 02.mp3, etc. my @mp3list = process_files( { function => "mv", message => "renaming", extension + => "\.mp3", list => @list } ); # call mpg123 to convert .mp3 to .wav my @wavlist = process_files( { function => "mpg123", param => "-qw", message => " +converting", extension => "\.wav", list => @mp3list } ); # call sox to convert each .wav to .cdr my @cdrlist = process_files( { function => "sox", message => "converting", extens +ion => "\.cdr", list => @wavlist } );
    It might also be possible just to dump the parameter variable and use "mpg123 -qw" as the function you're running. It means your status message gets a little munged, but that makes the code a little simpler.

    --t. alex

    "Of course, you realize that this means war." -- Bugs Bunny.

      All very good suggestions. I attempted "mpg123 -qw" prior to introducting the parameter variable. For reasons I do not know, system() didn't like it at all. Laziness triumphed over curiosity, and $param was brought into the code.

      If things get any worse, I'll have to ask you to stop helping me.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://139711]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (4)
As of 2024-04-19 13:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found