#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use Getopt::Long; use File::Basename; use Pod::Usage; use Sihnon::Common qw/verbose debug/; use Switch; use Sys::Pushd; use File::Copy; use FindBin; use lib $FindBin::Bin; # Globals our %options = ( verbose => 0, quiet => 0, silent => 0, help => 0, pretend => 0, media_dir => '/export/videos/', torrent_dir => '/export/torrents/completed/', tvrenamer => '/usr/bin/tvrenamer', ); Getopt::Long::Configure( qw(bundling no_getopt_compat) ); GetOptions( 'verbose|v+' => \$Sihnon::Common::VERBOSE, 'debug|d+' => \$Sihnon::Common::DEBUG, 'quiet|q' => \$options{quiet}, 'silent|s' => \$options{silent}, 'help|h' => \$options{help}, 'pretend|n' => \$options{pretend}, 'media-dir|m=s' => \$options{media_dir}, 'torrent-dir|t=s' => \$options{torrent_dir}, 'tvrenamer=s' => \$options{tvrenamer}, ) or pod2usage(-verbose => 0); pod2usage(-verbose => 1) if ($options{help}); my @args = @ARGV; # Get a list of tv series directories, and their normalised names my %series_dir = read_series_directories($options{media_dir}); my $dh; verbose("Reading contents of $options{torrent_dir}"); opendir $dh, $options{torrent_dir}; while (my $completed_torrent = readdir $dh) { next if $completed_torrent =~ m/^\..*/; my $completed_filename = $options{torrent_dir} . '/' . $completed_torrent; next if ! -f $completed_filename; verbose("Dealing with $completed_torrent ($completed_filename)"); my $output_dir = get_destination_dir_from_filename($completed_torrent); if ( ! $output_dir) { warn "Failed to find directory for $completed_torrent"; next; } my $output_filename = $output_dir . '/' . $completed_torrent; # Copy the file over to the media share copy_to_media_share($completed_filename, $output_filename) or next; # Attempt to rename the file my $final_filename = $output_filename; $final_filename = rename_media_share_file($output_filename); # Symlink the file back into the torrent directory for seeding replace_with_symlink($final_filename, $completed_filename); } sub read_series_directories { my $media_share_dir = shift or die; my %series_dir = (); my $dh; opendir $dh, $media_share_dir; while (my $file = readdir $dh) { next if $file =~ m/^\..*/; my $canonical_file = canonicalise($file); $series_dir{$canonical_file} = $file; } return %series_dir; } sub canonicalise { my $input = shift or die; $input =~ tr/A-Z/a-z/; $input =~ s/[^a-z0-9]//g; return $input; } sub copy_to_media_share { my $torrent_dir_filename = shift or die; my $output_filename = shift or die; verbose("Copying $torrent_dir_filename to $output_filename"); if ( ! $options{pretend}) { return copy $torrent_dir_filename, $output_filename; } else { return 1; # Return true if only pretending } } sub get_destination_dir_from_filename { my $source_filename = shift or die; my ($series, $season, $episode) = decode_filename($source_filename); return unless $series; verbose("Series: $series, Season: $season, Episode: $episode"); return $options{media_dir} . '/' . $series . '/Season ' . $season; } sub decode_filename { my $input_filename = shift or die; # grab the series, and season number from the torrented filename $input_filename =~ m/(.*)[-_\.]*(?:(\d+)x(\d+)|[sS](\d+)[eE](\d+))/; my $series = $1; my $season = $4; my $episode = $5; # Get the real series name if it exists my $canonical_series = canonicalise($series) or return; # Sanitise the season and episode numbers $season =~ s/0*(\d+)/$1/; return ($series_dir{$canonical_series}, $season, $episode); } sub replace_with_symlink { my $media_share_filename = shift or die; my $torrent_dir_filename = shift or die; my $temp_filename = $torrent_dir_filename . '.tmp'; verbose("Replacing $torrent_dir_filename with symlink to $media_share_filename"); if ( ! $options{pretend}) { symlink $media_share_filename, $temp_filename; move $temp_filename, $torrent_dir_filename; } } sub rename_media_share_file { my $input_filename = shift or die; my $output_filename = $input_filename; return $output_filename if $options{pretend}; my $input_dir = dirname($input_filename); # Find out the season and episode number for the file we are renaming my ($series, $season, $episode) = decode_filename(basename($input_filename)); return $output_filename unless $series; # Change into the output directory temporarily, to run the renamer my $changed_dir = new Sys::Pushd $input_dir; my $tvrenamer_cmd = "$options{tvrenamer} --unattended --noANSI --rangemin=$episode --rangemax=$episode"; open my $tvrenamer_fh, "$tvrenamer_cmd|"; my @tvrenamer_output = <$tvrenamer_fh>; close $tvrenamer_fh; # Scan through the output and look for the new filename for the episode with this season and episode number foreach my $line (@tvrenamer_output) { my $identifier = $season . 'x' . $episode; if ($line =~ m/$identifier/) { $output_filename = $line; chomp $output_filename; verbose("File has been renamed from $input_filename to $output_filename"); return $input_dir . '/' . $output_filename; } } return $output_filename; } __END__; =head1 NAME =head1 SYNOPSIS =head1 DESCRIPTION =head1 OPTIONS =cut