#!/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