#!/usr/bin/perl -w

# mp3rename - Rename MP3 files based on their IDv3 tags.
#
# Usage: mp3rename [opts] <mp3s>
#
# Examples:
# mp3rename --format '{artist} - {title}' $HOME/Music/*.mp3
#
# This script has POD documentation, so `perldoc mp3rename`
#
# --Kirsle
# http://sh.kirsle.net/

=head1 NAME

mp3rename - Rename MP3 files based on their IDv3 tags.

=head1 USAGE

  mp3rename [opts] <mp3s>

=head1 OPTIONS

  --format <format>
  -f

    Specify the format to output the files in. Doesn't need to include
    the .mp3 extension. Example: --format "{artist} - {title}"

    Format tags include:
    {artist} - Artist
    {album}  - Album
    {title}  - Song title
    {track}  - Track number

    Default is: {artist} - {title}

  --output <directory>
  -o

    Specify a directory to putput the files to. Default is the current
    working directory.

  --recursive
  -r

    Recursively descend into any directories included in the arguments.
    By default, directories will just be skipped over.

  --copy
  -c

    Copy, don't rename.

  --test
  -t

    Show what the files would be renamed to, but don't actually rename
    them.

  --help
  -h
  -?

    Show this documentation.

=head1 AUTHOR

Noah Petherbridge, http://www.kirsle.net/

=cut

use strict;
use warnings;
use File::Basename;
use File::Path qw(mkpath);
use File::Copy;
use Getopt::Long;
use MP3::Info;

# Get command-line arguments.
my %o = (
	format    => '{artist} - {title}',
	output    => '.',
	copy      => 0,
	recursive => 0,
	test      => 0,
	help      => 0,
);
GetOptions (
	'format|f=s'  => \$o{format},
	'output|o=s'  => \$o{output},
	'recursive|r' => \$o{recursive},
	'copy|c'      => \$o{copy},
	'test|t'      => \$o{test},
	'help|h|?'    => \$o{help},
);

# Help?
&help() if $o{help};

# Get the file list.
my @files = @ARGV;
if (scalar(@files) == 0) {
	&help();
}

foreach my $file (@files) {
	if (-d $file && $o{recursive}) {
		&crawldir($file);
	}
	elsif (-d $file) {
		next;
	}
	&rename($file);
}

sub crawldir {
	my $dir = shift;

	opendir (DIR, $dir) or die "Can't crawl into directory $dir: $!";
	foreach my $file (sort(grep(!/^\./, readdir(DIR)))) {
		next if -l "$dir/$file"; # Don't follow links.
		if (-d "$dir/$file") {
			&crawldir("$dir/$file");
		}
		elsif (-f "$dir/$file") {
			&rename("$dir/$file");
		}
	}
}

sub rename {
	my $file = shift;

	return unless -f $file;
	my $mp3 = get_mp3tag($file);
	return unless $mp3;

	my $artist = $mp3->{ARTIST} || "Unknown";
	my $album  = $mp3->{ALBUM} || "Unknown";
	my $title  = $mp3->{TITLE} || basename($file, ".mp3");
	my $track  = $mp3->{TRACKNUM} || "";

	# Make the new file name.
	my $rename = $o{format};
	$rename .= ".mp3" unless $rename =~ /\.mp3$/i;
	$rename =~ s/{artist}/$artist/ig;
	$rename =~ s/{album}/$album/ig;
	$rename =~ s/{title}/$title/ig;
	$rename =~ s/{track}/$track/ig;

	my $basename = basename($file);

	# Just testing?
	if ($o{test}) {
		print "$basename -> $rename\n";
		return;
	}

	# Make the output directory?
	if (!-d $o{output}) {
		warn "Creating output directory: $o{output}";
		mkpath($o{output}) or die "Can't create $o{output}: $!";
	}

	# Rename it. Or copy?
	if ($o{copy}) {
		print "COPY: $basename -> $o{output}/$rename\n";
		copy($file,"$o{output}/$rename");
	}
	else {
		print "RENAME: $basename -> $o{output}/$rename\n";
		rename($file, "$o{output}/$rename");
	}
}

sub help {
	exec("perldoc $0");
}