#!/usr/bin/perl ## ## Author: David McKeon <@bonzoli.com> ## URL: http://bonzoli.com ## Program: sort_mp3s ## Creation Date: Date: 2007/01/30 18:25:19 PST ## Last Revision: $Date$ ## Version: v.5 ## ############################################################################# # David McKeon <@bonzoli.com> http://bonzoli.com # # # # Copyright (C) 2007-2007 David McKeon. All rights reserved. # # # # This program is free software; you can redistribute it and/or modify it # # under the terms of the GNU General Public License as published by the # # Free Software Foundation; either version 2 of the License, or (at your # # option) any later version. # # # # This program is distributed in the hope that it will be useful, but # # WITHOUT ANY WARRANTY; without even the implied warranty of # # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General # # Public License for more details. # # # # You should have received a copy of the GNU General Public License along # # with this program; if not, write to: # # # # Free Software Foundation, Inc. # # 59 Temple Place - Suite 330 # # Boston, MA 02111-1307, USA. # # # # Or you can find the full GNU GPL online at: http://www.gnu.org # # # ############################################################################# ## Category: Perl ## ## ## ## ## # $| = 1; use Getopt::Long; use File::Basename; use File::Find; # Find files and run a process against them use File::Path; # create paths/dirs use MP3::Tag; # work with mp3 id3 tags. my $USAGE="Usage: sort_mp3 [-l target_mp3_directory] NOTE: *.mp3 works \n"; my $file,$mp3dir; if (! $ARGV[0]) { print "$USAGE\n"; exit; } # Sets Directory if its there # -l has to be first in command line. if ($ARGV[0] eq '-l') { shift(@ARGV); $mp3dir=$ARGV[0]; shift(@ARGV); } else{ $mp3dir='.'; } #--------------------------------------------------------------------------- Getopt::Long::Configure("no_ignore_case"); GetOptions( "h|help" => \$help, "V|version" => \$vers, #----------------------- ); #--------------------------------------------------------------------------- if($help){ exec("perldoc $0"); } if($vers){ version(); } #--------------------------------------------------------------------------- #--------------------------------------------------------------------------- sub version { my($date) = "\$Date$_"; my($rvsn) = "First"; my($rcsd) = "v.5"; $date =~ s/(.*: +)(.*?)(\s*$)/$2/g; $rvsn =~ s/(.*: +)(.*?)(\s*$)/$2/g; $rcsd =~ s/(.*: +)(.*?)(\s*$)/$2/g; print < URL: http://bonzoli.com Creation Date: 2007/01/30 18:25:19 PST Last Revision: $date PST Version: v Copyright (C) 2007-2007 David McKeon. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA. Or you can find the full GNU GPL online at: http://www.gnu.org EOF exit; } sub move_to_new_dir { ########################################################################## # FUNCTION DEFINITIONS #------------------------------------------------------------------------- # FUNCTION move_to_new_dir # RECEIVES ($NEWDIR, $NEWFILE, $OLDFILE); # RETURNS new file with path # EXPECTS ($NEWDIR, $NEWFILE, $OLDFILE); # SETS creates new directory when needed, then renames file to that dir # DOES We will make directory and move file to it, if' directory doesn't exist. my $NEWDIR=shift; my $NEWFILE=shift; my $OLDFILE=shift; eval { mkpath("$NEWDIR", 1, 0755) }; if ($@) { print "Couldn\'t create $NEWDIR: $@"; } else { # Create directory worked, moving file now. #print "Moving: mv $OLDFILE $NEWFILE\n"; if (!rename "$OLDFILE", "$NEWFILE"){ print "ERROR: Failed to move $OLDFILE to $NEWFILE!!!!\n"; } else { # if' it worked then return the new file return $NEWFILE; } } # We return old file if we didn't make a good directory return $OLDFILE; } sub shellify_string { ########################################################################## # FUNCTION DEFINITIONS #------------------------------------------------------------------------- # FUNCTION shellify_string # RECEIVES $string # RETURNS $string # DOES Going to make this line readable by shell, so mv's and copies work. # add a backslash before unusual characters my ( $file ); # shouldn't just shift work here? $file = shift @_; #print "my file is $file \n"; #If its not one of these characters put a \ in front of it. $file =~ s|[^-a-zA-Z0-9_.,/]|\\$&|g; # backslash newline gets ignored by sh, so we have to use quotes. $file =~ s|\\\n|'\n'|g; # make sure name doesn't have a leading - $file =~ s|^-|./-|; # null name is unprintable, make it '.' if ($file eq '') { $file = "."; } return $file; } sub sanitize_string { ########################################################################## # FUNCTION DEFINITIONS #------------------------------------------------------------------------- # FUNCTION sanitize_string # RECEIVES string # RETURNS sanitized string # DOES sanitize artist name (or filename fragment) for use as a dir name my $string = shift; return 'unsorted' unless $string; $string = lc($string); $string =~ s/\bthe\b//; $string =~ s/^\.+//; $string =~ s/\.$//; $string =~ s/_/ /g; $string =~ s/^ +//; $string =~ s/ +$//; $string =~ s/ +/ /g; $string =~ s/\// /g; $string =~ s/[,(){}\[\]]//g; return $string; } ##MAIN####################################################################### foreach $_ (@ARGV) { #$_=shift; my $mp3file=$_; my ($artist, $album, $tag); my ($name,$path,$suffix) = fileparse($_,''); print "GETTING TAGS FOR $_\n" if ($debug == 1); # attempt to find artist name through mp3 tag my $tag = MP3::Tag->new( $_ ); $tag->get_tags; my ($title, $track, $artist, $album, $comment, $year, $genre) = $tag->autoinfo(); if ($album ne '' && $artist eq '') { $artist = "misc"; } if (! $artist) { print "NO ID information thats usable, skipping\n"; next; } # Create folders for music files. print "PATH IS: $artist/$album\n" if ($debug == 1); # fall back to scanning filename. we're assuming artist name # is everything up to the first hyphen # unless ( $tag && $artist !~ /^\s*$/ && $artist ne 'artist' ) { ($artist) = /^([^-]+?)-.+$/; $artist ||= 'unsorted'; } else { $artist = $artist; } $album = $album || ""; if ( $album =~ /^\s*$/ || $album eq 'title' ) { $album = 'misc' } # Get rid of those unwanted characters so we can make directories. $artist = sanitize_string( $artist ) unless $artist eq 'unsorted'; $album = sanitize_string( $album ) unless $album eq 'misc'; #print "==>ALBUM=>$mp3dir/$artist/$album\n" if ($debug == 1); # Make directory and if it works move file. my $NEWDIR="$mp3dir/$artist/$album"; my $NEWFILE="$NEWDIR/$name"; my $OLDFILE=$mp3file; if ( $NEWFILE ne $OLDFILE) { #print "move_to_new_dir($NEWDIR, $NEWFILE, $OLDFILE)\n"; $mp3file=move_to_new_dir($NEWDIR, $NEWFILE, $OLDFILE); } else{ print "This file *$OLDFILE* already exists, I can't move it.\n" if ($debug == 1); } } ##END MAIN####################################################################### #--------------------------------------------------------------------------- ## ## Use "perldoc sort_mp3s" to read the man page below. # __END__ =head1 NAME B - Sorts directory of MP3's into an organized set of directories. =head1 SYNOPSIS B S<[ B<-hv> ]> S<[ I ]> =head1 DESCRIPTION B sorts your cluster of mp3's that are all in 1 directory into an organized set of directories. artist->album this information is pulled from the id3 tags. You can tell the program what the destination top directory is by using -l /dir/path. If you do not supply a directory to move to, it assumes the current directory, and creates the artists there. If you specify a destination directory that does not exist, this directory will be made along with any paths. If your tags are missing or to corrupted, it will create a "misc/unsorted" directory, or just leave them alone. Depending on how much information it can find. In some cases it might try to guess the name from the files name. This program will remove The XXXX, "The" will be removed, so The Beatles, would be ---> beatles You could tinker with this in the santatize string sub routine. You should run this from the directory containing the mp3's. ################################### You might see: Unknown ID3v2-Tag version: v2.4.0 | 4 > 4 || 1 == 0 | 0,0,1,1,1n0: 0 1: 0 2: 1 3: 1 4: 1 5: ################################### If ID3 Tagging could not be read correctly by the program or its more advanced then id3 2.4, just watch and see if its moving into a proper directory or a misc directory. This program will not alter the MP3's in any way, just moves them with a verification. It should not delete anything by accident. But if it eats your collection, please understand there is no warranty on this, use at your own risk. With that said it appears to work fine for me. =head1 QUICK START The most common usage is as follows: B [-l target_mp3_directory] NOTE: *.mp3 works B [-l target_mp3_directory] B -l /mp3s <*.mp3> which is my favorite. =head1 STANDARD OPTIONS B<-h --help> Prints this information. B<-v --version> Prints version information. =head1 OPTIONS =head1 EXAMPLE The example below B B<-l /mp3s> I<*.mp3> =head1 BUGS None. =head1 SEE ALSO sort_flacs, sort_mp3s, sort_oggs, sort_wmas =head1 AUTHOR AND COPYRIGHT David McKeon <@bonzoli.com> http://bonzoli.com Copyright (C) 2007-2007 David McKeon. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA. Or you can find the full GNU GPL online at: http://www.gnu.org =head1 VERSION Current Revision: $Revision$ Last Modification: $Date$ =pod SCRIPT CATEGORIES UNIX/System_administration =pod OSNAMES Any