Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

mp3 organizer

by sacked (Hermit)
on Jan 31, 2001 at 00:27 UTC ( #55341=sourcecode: print w/ replies, xml ) Need Help??

Category: Audio Related Programs
Author/Contact Info sacked
Description: This script reads in a list of mp3s (assumed to be in one directory) and organizes them into directories by artist/album. It first attempts to use the id3 tag, but if one is not found, it falls back to parsing the filename for the artist name. I use this script every so often because I download all my mp3s to one directory, and it gets cluttered quickly. The script doesn't have the desired results, however, if mp3s without id3 tags have a hyphen in the filename, but don't have the artist first (e.g., "ice ice baby - vanilla ice.mp3").

This is my first post, please feel free to offer comments/criticism. Thanks!

Update: I removed the system calls to /bin/mv and replaced them with calls to rename, after a tip from salvadors.
#!/usr/bin/perl -w
#
# attempts to organize mp3s into dirs by:
#   1. checking ID3 tag for Artist name
#   2. scanning the filename
#
# it will further organize the mp3s into subdirectories under the
# artist name by album
#

use strict;
use MP3::Info;

use vars qw( $mp3dir @files %dirs );

# change as necessary
#
$mp3dir = '/opt/mp3/';

chdir $mp3dir or die "can't chdir to $mp3dir: $!\n\n";

# get file list
#
opendir(DIR, $mp3dir) or die "can't open $mp3dir for read: $!\n\n";
@files = grep { /\.[Mm][Pp]3$/ } readdir(DIR);
closedir(DIR) or warn "error closing $mp3dir: $!\n\n";

&make_dir_list;
&move_files;
exit;


# create dir listing
# %dirs = ( 'dirname' => { 'subdir1' => [ file1, file2, ... ], ... }, 
+...  );
#
sub make_dir_list {
  foreach ( @files ) {
    my ($artist, $album, $tag);

    # attempt to find artist name through mp3 tag
    #
    $tag = get_mp3tag( $_ );

    # fall back to scanning filename. we're assuming artist name
    # is everything up to the first hyphen
    #
    unless ( $tag && $tag->{ARTIST} !~ /^\s*$/ && $tag->{ARTIST} ne 'a
+rtist' ) {
      ($artist) = /^([^-]+?)-.+$/;
      $artist ||= 'unsorted';
    }
    else {
      $artist = $tag->{ARTIST};
    }

    $album  = $tag->{ALBUM} || "";
    if ( $album =~ /^\s*$/ || $album eq 'title' ) { $album = 'misc' }

    $artist = clean_name( $artist ) unless $artist eq 'unsorted';
    $album  = clean_name( $album ) unless $album eq 'misc';
    push @{ $dirs{$artist}{$album} }, $_;
  }
}


# sanitize artist name (or filename fragment) for use as a dir name
#
sub clean_name {
  my $dir = shift;
  return 'unsorted' unless $dir;

  $dir =  lc($dir);
  $dir =~ s/\bthe\b//;
  $dir =~ s/_/ /g;
  $dir =~ s/^ +//;
  $dir =~ s/ +$//;
  $dir =~ s/ +/ /g;
  $dir =~ s/[,(){}\[\]]//g;

  return $dir;
}


# create dirs, put sorted files into them
#
sub move_files {
  foreach my $artist ( keys %dirs ) {
    # XXX debug
    #
    #print "  $artist\n";

    unless ( -d qq{$artist} ) {
      mkdir( qq{$artist}, 0755 ) or die "error mkdir($artist): $!\n\n"
+;
    }

    foreach my $album ( keys %{ $dirs{$artist} } ) {
      # XXX debug
      #
      #print "    $album\n", map { "\t$_\n" } @{ $dirs{$artist}{$album
+} }, "\n";

      unless ( -d qq{$artist/$album} ) {
        mkdir( qq{$artist/$album}, 0755 ) or die "error mkdir($artist/
+$album): $
!\n\n";
      }

      #system('/bin/mv', @{ $dirs{$artist}{$album} }, qq{$artist/$albu
+m/}) == 0
      #  or die "system('/bin/mv'): $?\n\n";

      foreach my $mp3 ( @{ $dirs{$artist}{$album} } ) {
        rename $mp3, qq{$artist/$album/$mp3} or die "can't rename $mp3
+: $!\n\n";
      }
    }
  }
}

Comment on mp3 organizer
Download Code
Re: mp3 organizer
by salvadors (Pilgrim) on Jan 31, 2001 at 20:04 UTC

    Quite nice. But rather than making system calls to /bin/mv, you should really be using the builtin rename command...

    Tony

      Thanks for the suggestion! I was curious as to whether rename would be faster, though, since it requires one call per mp3, whereas /bin/mv requires only one call per directory. After a quick scan of my collection, I made a conservative estimate of five mp3s per directory (meaning, of course, five times as many calls are required for rename as /bin/mv). The results of a benchmark, however, were enlightening. Here is the benchmarking code I used:
      #!/usr/bin/perl -w # # system('/bin/mv') vs rename use strict; use Benchmark; use vars qw($rename_cnt $system_cnt $mp3dir1 $mp3dir2 @files @f1 @f2 $ +i $flip); # $flip switches once per @files runs of the rename sub, # then switches every call to the /bin/mv sub $flip = $i = 0; # flip-flop between these two dirs for move operations $mp3dir1 = '/opt/mp3/'; $mp3dir2 = '/opt/tmp/'; # get file list opendir(DIR, $mp3dir2) or die "can't open $mp3dir2 for read: $!\n\n"; @files = grep { /\.[Mm][Pp]3$/ } readdir(DIR); closedir(DIR) or warn "error closing $mp3dir2: $!\n\n"; @f1 = map { $mp3dir1 . $_ } @files; @f2 = map { $mp3dir2 . $_ } @files; # run the func a few times $rename_cnt = $#files * 10; $system_cnt = int($rename_cnt / 5); my $t = timeit($rename_cnt, \&rename_move_files); print "$rename_cnt loops of 'rename' took:",timestr($t),"\n"; $t = timeit($system_cnt, \&system_move_files); print "$system_cnt loops of 'system' took:",timestr($t),"\n"; exit; sub system_move_files { my ($src,$dst) = ($flip % 2) ? (\@f1,$mp3dir2) : (\@f2,$mp3dir1); system('/bin/mv', @$src, qq{$dst}) == 0 or die "system('/bin/mv'): $?\n\n"; ++$flip; } sub rename_move_files { my ($src,$dst) = ($flip % 2) ? (\@f1,\@f2) : (\@f2,\@f1); # so we can run this more than @files times my $j = $i % $#files; rename qq{$src->[$j]}, qq{$dst->[$j]} or die "can't rename $src->[$j +]: $!\n\n"; ++$i; ++$flip if ($i % $#files == 0); } __END__ results ranged from: 1300 loops of 'rename' took: 0 wallclock secs ( 0.01 usr + 0.05 sys = + 0.06 CPU) @ 21666.67/s (n=1300) 260 loops of 'system' took: 2 wallclock secs ( 0.08 usr 0.13 sys + 0 +.44 cusr 1.46 csys = 2.11 CPU) @ 1238.10/s (n=260) to: 1300 loops of 'rename' took: 0 wallclock secs ( 0.02 usr + 0.04 sys = + 0.06 CPU) @ 21666.67/s (n=1300) 260 loops of 'system' took: 2 wallclock secs ( 0.03 usr 0.04 sys + 0 +.38 cusr 1.67 csys = 2.12 CPU) @ 3714.29/s (n=260)

      Looks like the rename function wins.
      --
      "All we gotta do is apply the final touches!" -Parappa

      sacked
Re: mp3 organizer
by Anonymous Monk on Aug 23, 2002 at 18:52 UTC
    I take it I need to install the MP3::Info module? Where do I get that from?

      Where else? The magical little toolbox that is CPAN!

      Go here -> MP3::Info


      _______________
      DamnDirtyApe
      Those who know that they are profound strive for clarity. Those who
      would like to seem profound to the crowd strive for obscurity.
                  --Friedrich Nietzsche

Back to Code Catacombs

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://55341]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (13)
As of 2014-10-01 14:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    What is your favourite meta-syntactic variable name?














    Results (29 votes), past polls