Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

mp3 organizer

by sacked (Hermit)
on Jan 31, 2001 at 00:27 UTC ( [id://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";
      }
    }
  }
}
Replies are listed 'Best First'.
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

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-03-29 10:07 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found