Film Sorter Script

On my server I have a store of films located at /share/media/movies. Using NFS I share this directory to various other computers on my home network. For a long while Faye has been asking me to categorise them into genres and cast, but with over 300 files I’ve been loath to do it by hand, so I thought I’d automate things using Perl.

Using the IMDB::Film module I have been able to automate all of the data gathering. The module goes off and searches IMDB for a matching title (in this case I try searching for the file name sans the extension. Once found you can query the returned object about data such as cast member, genre, director, etc. Using this data I then create new folders based on the genre name or individual cast names, then symlink the original file to the new folder. When I mount the general movie folder and the sorted movie folder, everything ties in well and works pretty nicely!

It does have it’s flaws, a couple of films have come back and been the wrong one as IMDB confused the title, but overall it does the job.

If you know a bit of Perl, here is the script for you to modify for your own needs:

#!/usr/bin/perl

=head1 License
Copyright 2011 Ben Lavery. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY Ben Lavery ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Ben Lavery OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Ben Lavery.
=cut

use 5.010;
use File::Spec;
use IMDB::Film;
use Cwd;

# Get current working directory
my $cwd = cwd();

# Set up log file
$LOGFILE='/var/tmp/media_script_log';
open(LOGFILE, ">>", "$LOGFILE") or die "can't open $LOGFILE: $!";

# Set up where the categories are to be stored
$SORTED = "/share/media/sorted";
$GENRE = "$SORTED/genre/";
$ACTOR = "$SORTED/actor/";

# The directory that contains the media is the first argument
$DIRECTORY = $ARGV[0];
die "Directory doesn't exist. Stopped" unless -d $DIRECTORY;

# Get list of films
opendir(DIRHANDLE,"$DIRECTORY") or die "Cannot open $DIRECTORY.  Stopped";
my @files = sort readdir(DIRHANDLE);
close(DIRHANDLE);

# Iterate through the list of films and act on each
foreach $media (@files){
	chdir($DIRECTORY);

	# Skip folders and hidden Mac OS X files
	next if ($media eq ".DS_Store");
	next if (-d $media);

	say "Starting: $media";

	# Set up variables
	my $title = undef;
	my @starring = undef;
	my @genre = undef;
	my $IMDB = undef;

	# Strip extention
	$_ = "$media";
	s/(.*)..*/$1/;

	my $error_incurred = 1;
	while($error_incurred){
		$@ = undef;
		# Create IMDB object and search for the title
		eval{$IMDB = new IMDB::Film(crit => $_) || say "UNKNOWN ERROR - RETRYING";};
		#Check for errors.  If there are errors, try again
		$error_incurred = 0 unless $@ && (!$IMDB->status);
	}

	# Find the correct title
	# This is because "The Lord Of The Rings 2" gets translated to "The Lord Of The Rings: The Two Towers"
	$title = $IMDB->title();
	say "tTitle is: $title";

	$error_incurred = 1;
	while($error_incurred){
		$@ = undef;
		# Search for actual title
		eval{$IMDB = new IMDB::Film(crit => $title) || say "UNKNOWN ERROR - RETRYING";};
		#Check for errors.  If there are errors, try again
		$error_incurred = 0 unless $@ && (!$IMDB->status);
	}

	# Get lists of cast and genres
	@starring = @{$IMDB->cast()};
	@genre = @{$IMDB->genres()};

	# Add films to sorted directory
	chdir $GENRE;
	foreach $genre (@genre){
		# If genre directory doesn't exist, create it
		mkdir $genre unless (-d $genre);

		# Calculate relative path between media directory and sorted directory
		my $genre_specific_dir = "$GENRE/$genre";
		my $path = File::Spec->abs2rel($DIRECTORY, $genre_specific_dir);
		$path .= "/$media";

		my $newpath = "$GENRE/$genre/$title";

		# Create symlink
		symlink($path, $newpath);
	}

	chdir $ACTOR;
	foreach $actor (@starring){
		# Get name of the current actor
		my $name = $actor->{name};

		# If genre directory doesn't exist, create it
		mkdir $name unless (-d $name);

		# Calculate relative path between media directory and sorted directory
		my $actor_specific_dir = "$ACTOR/$name";
		my $path = File::Spec->abs2rel($DIRECTORY, $actor_specific_dir);
		$path .= "/$media";

		my $newpath = "$ACTOR/$name/$title";

		# Create symlink
		symlink($path, $newpath);
	}

	# Print message saying that the current media file has been processed
	say "FINSHED.";

}

close(LOGFILE);

chdir $cwd;
print "a";

 |