The script below will search through a given directory for all mp3 files, determine the artist and title from the filename, and then group them into directories by artist. It makes sorting MP3's a lot less tedious. Be sure to check all the directory paths before you run it on your collection.

Update: Now bases information on ID tags of MP3 file instead of title.

#!/usr/bin/perl use warnings; use strict; use File::Copy; use MP3::Info; chdir "d:\\music\\new"; my ($artist, $song, $dirname, $wait, $info); while(glob("*.mp3")) { $info = get_mp3tag($_); ($artist, $song) = ($info->{ARTIST}, $info->{TITLE}); next unless $artist and $song; $dirname = "..\\" . $artist; $dirname =~ s/the//g; $dirname =~ s/ /_/g; $dirname =~ s/^\s*//g; $dirname =~ s/\s*$//g; $dirname = lc $dirname; if(!(-e $dirname and -d $dirname)) { print "Create $dirname folder?"; mkdir $dirname if <STDIN> =~ /^y/; } if(-e $dirname and -d $dirname) { print "moving $artist - $song.mp3 to $dirname folder.\n"; move($_, "$dirname\\$artist - $song.mp3"); } }

Replies are listed 'Best First'.
Re: MP3 Sorter
by Aristotle (Chancellor) on Dec 29, 2002 at 00:18 UTC
    ++ :) Here's what I did to it:
    #!/usr/bin/perl -w use strict; use Cwd; use File::Spec::Functions qw(catdir catfile); use File::Copy; use MP3::Info; die "usage: $0 dirname [dirname ...]\n" unless @ARGV; my $cwd = cwd(); my %skip; for(@ARGV) { chdir($_) or (warn("Couldn't chdir to $_\n"), next); opendir my($dh), "."; for my $file (grep /\.mp3\z/i and -f, readdir $dh) { my ($artist, $title) = @{ get_mp3tag($file) }{qw(ARTIST TITLE) +}; next unless $artist and $song; my $dirname = lc $artist; s/\bthe\s*//g, s/^\s+//g, s/\s+$//g, tr/ /_/ for $dirname; my $targ_dir = catdir("..", $dirname); unless($skip{$directory} or -e $targ_dir) { print "Create $dirname folder (y/n)? "; <STDIN> =~ /^y/i ? mkdir $targ_dir : undef $skip{$director +y}; } if(-d $targ_dir) { my $target = catfile($targ_dir, "$artist - $title.mp3"); print "$file -> $target\n"; move($file, $target); } else { warn( "Skipping $file, $directory ", (-e $targ_dir ? "is not a directory" : "was not created" ), ".\n" ); } } chdir($cwd); }

    Untested. :) (I'd modify it further for my own perusal.)

    For critique look at the changes. In short: use File::Spec to compose filenames, take arguments from commandline, try to allow for processing more than one thing per call (not an issue in your own situation right now of course, but will likely become and should be taken care of when releasing code), prefer readdir, inform the user well. The %skip bit is thrown in for good measure, so that you don't have to no the same prompt for the same artist ten times in a row if that happens to happen. :)

    I'd add a commandline option (Getopt::Std is very easy to incorporate) like -f (= force) that tells the script to just go ahead and create directories without asking. Something like -d dir (= destination directory) would probably be a good idea also. A switch to adjust behaviour between taking artist/song info from filename and/or ID3 could be useful as well.

    Makeshifts last the longest.

      good work as usual, aristotle :)
Re: MP3 Sorter
by em (Scribe) on Dec 29, 2002 at 23:18 UTC
    Just a couple points:

    Perl will usually 'do the right thing' if you use '/' as the directory separator. This way I can use your script on my linux box and Mac OS X laptop as well as my W2K system without edits:

    chdir "d:/music/new";
    is the same as
    chdir "d:\\music\\new";

    Instead of hard coding the directory (or using Getopt::*), you could do:

    my $music_dir = shift || "d:/music/new"; chdir $music_dir;

    Which allows 'mp3_sorter.pl' (to process d:\music\new) as well as 'mp3_sorter.pl /Volumes/myafp/mp3' (to process the mp3s on an AppleTalk share).

    For little hacks, I'll embed string constants. But I try to make it easy to override them if at all possible.