#!/usr/bin/perl -w
# mythexport v1.0.1
# By: John Baab
# Email: john.baab@gmail.com
# Purpose: Script for exporting mythtv recordings into formats used by portable devices.
# Requirements: ffmpeg (with aac,xvid,h264 support), perl and the DBI & DBD::mysql modules, 
#					MythTV perl bindings, AtomicParsley
#
# Modified from ipodexport.pl By Justin Hornsby 27 July 2007
#
#
# License:
#
# This Package 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 package 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 package; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# On Debian & Ubuntu systems, a complete copy of the GPL can be found under
# /usr/share/common-licenses/GPL-2, or (at your option) any later version

use DBI;
use DBD::mysql;
use MythTV;
use strict;

# Set default values
my $exportdir = '/home/mythtv/';
my $audio_bitrate = '96kb';
my $video_bitrate = '300kb';
my $aspect = '4:3';
my $size = "320x240";
my $delete_period = 30;

my $connect = undef;
my $debug = 0;

my ($starttime, $chanid, $title, $subtitle, $description, $syndicatedepisodenumber, $showtype, $programid, $basename, $exportcodec, $exportdevice, $airdate, $podcast_name) = "";

my $usage = "\nHow to use mythexport : \n"
    ."\nchanid = Channel ID associated with the recording to export.\n"
    ."starttime = Recording start time in either 'yyyy-mm-dd hh:mm:ss' or 'yyyymmddhhmmss' format.\n"
    ."exportdir = Directory to export completed MP4 files to (note the user the script runs as must have write permission on that directory).\n"
    ."size = Frame size of output file.  320x240 is the default value.\n"
    ."aspect = Aspect ratio of output file.  Valid values are 4:3 (default) and 16:9.\n"
    ."audio_bitrate = Audio bitrate in output file in kbps.  Default value is 96kb.\n"
    ."video_bitrate = Video bitrate in output file in kbps.  Default value is 300kb.\n"
	."export_codec = Acceptable export codecs: mpeg4, xvid, h264, mp3, none.\n"
	."export_device = Acceptable export devices: ipod, psp, zune, archos, none.\n"
    ."delete_period = How long (in days) to keep the exported files before removing them.  Default value is 30 days.\n"
	."podcast_name = Used to group recordings into different podcasts.  Default value is null.\n"
    ."debug = Enable debugging information - outputs which commands would be run.\n"
    ."\nExample: mythexport exportdir=/mythtv/ipod starttime=20060803205900 chanid=1006 size=320x240 aspect=4:3 audio_bitrate=192kb video_bitrate=300kb export_device=ipod export_codec=mpeg4 debug\n";

# get this script's ARGS
#

my $num = $#ARGV + 1;

# if user hasn't passed enough arguments, die and print the usage info
if ($num <= 2) {
    die "$usage";
}

#
# Get all the arguments
#

foreach (@ARGV){

    if ($_ =~ m/debug/) {
        $debug = 1;
    }
    elsif ($_ =~ m/size/) {
        $size = (split(/\=/,$_))[1];
    }
    elsif ($_ =~ m/aspect/) {
        $aspect = (split(/\=/,$_))[1];
    }
    elsif ($_ =~ m/audio_bitrate/) {
        $audio_bitrate = (split(/\=/,$_))[1];
    }
	elsif ($_ =~ m/video_bitrate/) {
        $video_bitrate = (split(/\=/,$_))[1];
    }
    elsif ($_ =~ m/starttime/) {
        $starttime = (split(/\=/,$_))[1];
    }
    elsif ($_ =~ m/chanid/) {
        $chanid = (split(/\=/,$_))[1];
    }
    elsif ($_ =~ m/exportdir/) {
        $exportdir = (split(/\=/,$_))[1];
    }
	elsif ($_ =~ m/export_codec/) {
        $exportcodec = (split(/\=/,$_))[1];
    }
	elsif ($_ =~ m/export_device/){
		$exportdevice = (split(/\=/,$_))[1];
	}
    elsif ($_ =~ m/delete_period/){
		$delete_period = (split(/\=/,$_))[1];
	}
    elsif ($_ =~ m/podcast_name/){
		$podcast_name = (split(/\=/,$_))[1];
	}
}

#
#
my $myth = new MythTV();
# connect to database
$connect = $myth->{'dbh'};

#test that the directory has the correct permissions
-w $exportdir || die "ERROR: Directory is not writeable.\n";

# PREPARE THE QUERY
my $query = "SELECT rec.title, rec.subtitle, rec.description, pg.syndicatedepisodenumber, pg.showtype, rec.programid, rec.basename, rec.starttime
FROM recorded rec left join program pg on pg.programid=rec.programid
WHERE rec.chanid=$chanid AND rec.starttime='$starttime'";
#print $query;
my $query_handle = $connect->prepare($query);


# EXECUTE THE QUERY
$query_handle->execute() || die "ERROR: Cannot connect to database \n";

# BIND TABLE COLUMNS TO VARIABLES
$query_handle->bind_columns(undef, \$title, \$subtitle, \$description, \$syndicatedepisodenumber, \$showtype, \$programid, \$basename, \$airdate);

# LOOP THROUGH RESULTS
$query_handle->fetch();

my $schemaVer = $myth->backend_setting('DBSchemaVer');
# Storage Groups were added in DBSchemaVer 1171
# FIND WHERE THE RECORDINGS LIVE
my $dir = 'UNKNOWN';
if ($schemaVer < 1171)
{
    if ($debug) {
        print ("Using compatibility mode\n");
    }
    $dir = $myth->backend_setting('RecordFilePrefix');
}
else
{
    if ($debug) {
        print ("Going into new mode\n");
    }
    my $storagegroup = new MythTV::StorageGroup();
    $dir = $storagegroup->FindRecordingDir($basename);
}

# FIND OUT THE CHANNEL NAME
$query = "SELECT callsign FROM channel WHERE chanid=?";
$query_handle = $connect->prepare($query);
$query_handle->execute($chanid)  || die "ERROR: Unable to query settings table";

my $channame = $query_handle->fetchrow_array;

# replace non-word characters in channame with dashes
$channame =~ s/\W+/-/g;

# replace non-word characters in title with underscores
my $title_old = $title;
$title =~ s/\W+/_/g;
# replace non-word characters in subtitle with underscores
my $subtitle_old = $subtitle;
$subtitle =~ s/\W+/_/g;

# Remove non alphanumeric chars from $starttime & $endtime
my $newstarttime = $starttime;
$newstarttime =~ s/[|^\W|\s|-|]//g;
#my $filename = $dir."/".$chanid."_".$newstarttime.".mpg";
#my $newfilename = $exportdir."/".$channame."_".$title."_".$subtitle."_".$newstarttime;
my $filename = $dir."/".$basename;
my $newfilename = $exportdir."/".$channame."-".$title."-".$subtitle."-".$newstarttime;
$newfilename =~ s/\..*?$//;

$syndicatedepisodenumber =~ m/^.*?(\d*)(\d{2})$/;
my $seasonnumber = $1;
my $episodenumber = $2;

# Trim any characters over 63 (59+4 for the extentsion), 
# it seems iTunes does not like files with lenghts this long.
$newfilename =~ s/^(.*\/(.{1,59})).*$/$1/g;
my $webfilename = "$2";

if ($debug) {
    print "\n\n Source filename:$filename \nDestination filename:$newfilename\n \n";
}

# move to the export directory incase any logs get written
chdir $exportdir;

# Now run ffmpeg to get iPod compatible video with a simple ffmpeg line
my $command = "";
my $command2 = "";
my $command3 = "";


if ($exportdevice eq "none" || $exportcodec eq "none"){
    $command = "ln -s $filename $newfilename";
}
elsif ($exportcodec eq "mp3"){
	$newfilename .= ".mp3";
	$webfilename .= ".mp3";
	$command = "nice -n19 ffmpeg -i $filename -acodec mp3 -ac 2 -ab $audio_bitrate -vn '$newfilename' 2>&1";
}
elsif ($exportdevice eq "ipod" || $exportdevice eq "zune"){
	$newfilename .= ".mp4";
	$webfilename .= ".mp4";
	if ($exportcodec eq "mpeg4" or $exportcodec eq "xvid"){
		# This VBR line works well, but only in xvid.  Causes buffere underflows in Gutsy.	
		#$command = "nice -n19 ffmpeg -i $filename -acodec aac -ab $audio_bitrate -qmin 3 -qmax 5 -g 300 -bufsize 4096 -vcodec $exportcodec -b $video_bitrate -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s $size -aspect $aspect '$newfilename' 2>&1";
		# This VBR line works well, but only in xvid.  Causes buffere underflows in Gutsy.
		#$command = "nice -n19 ffmpeg -i $filename -f mp4 -vcodec $exportcodec -maxrate 1000 -b $video_bitrate -qmin 3 -qmax 5 -bufsize 4096 -g 300 -acodec aac -ab $audio_bitrate -s $size -aspect $aspect '$newfilename' 2>&1"
	
		# Works cleanly with both vcodecs, but produces larger files.
		$command = "nice -n19 ffmpeg -i $filename -acodec aac -ab $audio_bitrate -vcodec $exportcodec -b $video_bitrate -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s $size '$newfilename' 2>&1";
	}
	elsif ($exportcodec eq "h264"){
		# First pass
		$command = "nice -n19 ffmpeg -y -i $filename -an -v 1 -threads auto -vcodec h264 -b $video_bitrate -bt 175k -refs 1 -loop 1 -deblockalpha 0 -deblockbeta 0 -parti4x4 1 -partp8x8 1 -me full -subq 1 -me_range 21 -chroma 1 -slice 2 -bf 0 -level 30 -g 300 -keyint_min 30 -sc_threshold 40 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.7 -qmax 51 -qdiff 4 -i_qfactor 0.71428572 -maxrate 768k -bufsize 2M -cmp 1 -s $size -f mp4 -pass 1 /dev/null";
		# Second pass
		$command2 = "nice -n19 ffmpeg -y -i $filename -v 1 -threads auto -vcodec h264 -b $video_bitrate -bt 175k -refs 1 -loop 1 -deblockalpha 0 -deblockbeta 0 -parti4x4 1 -partp8x8 1 -me full -subq 6 -me_range 21 -chroma 1 -slice 2 -bf 0 -level 30 -g 300 -keyint_min 30 -sc_threshold 40 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.7 -qmax 51 -qdiff 4 -i_qfactor 0.71428572 -maxrate 768k -bufsize 2M -cmp 1 -s $size -acodec aac -ab $audio_bitrate -ar 48000 -ac 2 -f mp4 -pass 2 '$newfilename' 2>&1";
	}
	else{
		print "\n\nERROR: Unexpected Export Codec.";
		exit;
	}
}
elsif ($exportdevice eq "psp"){
	$newfilename .= ".mp4";
	$webfilename .= ".mp4";
	if ($exportcodec eq "mpeg4" or $exportcodec eq "xvid"){
		$command = "nice -n19 ffmpeg -i $filename -acodec aac -ab $audio_bitrate -vcodec $exportcodec -b $video_bitrate -ar 24000 -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s $size -aspect $aspect -r 30000/1001 -f psp '$newfilename' 2>&1";
	}
	elsif ($exportcodec eq "h264"){
		# PSP h.254 encoding doesn't seem to be working.
		print "\n\nError: PSP h.264 encoding is not currently supported.  Use xvid or mpeg4 instead.";
		exit;		
		# According to the ffmpeg website, this should have worked.
		#$command = "nice -n19 ffmpeg -i $filename -acodec aac -ab $audio_bitrate -vcodec h264 -b $video_bitrate -ar 48000 -mbd 2 -coder 1 -cmp 2 -subcmp 2 -s $size -aspect $aspect -r 30000/1001 -title X -f psp -flags loop -trellis 2 -partitions parti4x4+parti8x8+partp4x4+partp8x8+partb8x8 '$newfilename' 2>&1";
	}
	else{
		print "\n\nERROR: Unexpected Export Codec.";
		exit;
	}
}
elsif ($exportdevice eq "archos"){
    $newfilename .= ".avi";
    $webfilename .= ".avi";
    if ($exportcodec eq "mpeg4" or $exportcodec eq "xvid"){
        # ffmpeg line from courtesy of freymann
        # ffmpeg -i infile.mpg -vcodec mpeg4 -s 320x240 -b 906k -acodec mp3 -ab 192kb outfile.avi
        # modified to use all the flags used in other ffmpeg lines.
        $command = "nice -n19 ffmpeg -i $filename -acodec mp3 -ab $audio_bitrate -vcodec $exportcodec -b $video_bitrate -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s $size '$newfilename' 2>&1";
    }
	elsif ($exportcodec eq "h264"){
        print "\n\nError: Archos h.264 encoding is not currently supported.  Use xvid or mpeg4 instead.";
		exit;
	}
	else{
		print "\n\nERROR: Unexpected Export Codec.";
		exit;
	}
}
elsif ($exportdevice eq "xbox360"){
	$newfilename .= ".mp4";
	$webfilename .= ".mp4";
	if ($exportcodec eq "mpeg4" or $exportcodec eq "xvid"){
		# This VBR line works well, but only in xvid.  Causes buffere underflows in Gutsy.
		#$command = "nice -n19 ffmpeg -i $filename -acodec aac -ab $audio_bitrate -qmin 3 -qmax 5 -g 300 -bufsize 4096 -vcodec $exportcodec -b $video_bitrate -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s $size -aspect $aspect '$newfilename' 2>&1";
		# This VBR line works well, but only in xvid.  Causes buffere underflows in Gutsy.
		#$command = "nice -n19 ffmpeg -i $filename -f mp4 -vcodec $exportcodec -maxrate 1000 -b $video_bitrate -qmin 3 -qmax 5 -bufsize 4096 -g 300 -acodec aac -ab $audio_bitrate -s $size -aspect $aspect '$newfilename' 2>&1"

		# Works cleanly with both vcodecs, but produces larger files.
		$command = "nice -n19 ffmpeg -i $filename -acodec aac -ab $audio_bitrate -vcodec $exportcodec -b $video_bitrate -mbd 2 -flags +4mv+trell -aic 2 -cmp 2 -subcmp 2 -s $size '$newfilename' 2>&1";
	}
	elsif ($exportcodec eq "h264"){
		# First pass
		$command = "nice -n19 ffmpeg -y -i $filename -an -v 1 -threads auto -vcodec h264 -b $video_bitrate -bt 175k -refs 1 -loop 1 -deblockalpha 0 -deblockbeta 0 -parti4x4 1 -partp8x8 1 -me full -subq 1 -me_range 21 -chroma 1 -slice 2 -bf 0 -level 30 -g 300 -keyint_min 30 -sc_threshold 40 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.7 -qmax 51 -qdiff 4 -i_qfactor 0.71428572 -maxrate 768k -bufsize 2M -cmp 1 -s $size -f mp4 -pass 1 /dev/null";
		# Second pass
		$command2 = "nice -n19 ffmpeg -y -i $filename -v 1 -threads auto -vcodec h264 -b $video_bitrate -bt 175k -refs 1 -loop 1 -deblockalpha 0 -deblockbeta 0 -parti4x4 1 -partp8x8 1 -me full -subq 6 -me_range 21 -chroma 1 -slice 2 -bf 0 -level 30 -g 300 -keyint_min 30 -sc_threshold 40 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.7 -qmax 51 -qdiff 4 -i_qfactor 0.71428572 -maxrate 768k -bufsize 2M -cmp 1 -s $size -acodec aac -ab $audio_bitrate -ar 48000 -ac 2 -f mp4 -pass 2 '$newfilename' 2>&1";
	}
	else{
		print "\n\nERROR: Unexpected Export Codec.";
		exit;
	}
}
else{
	print "\n\nERROR: Unexpected Export Device.";
	exit;
}

# Update mp4 info using AtomicParsley
# need to fix this so movies actually show up as movies.
if ($exportdevice ne "archos" && $exportcodec ne "mp3" && $exportdevice ne "none" && $exportcodec ne "none"){
    $command3 = "AtomicParsley '$newfilename' --genre \"TV Shows\" --stik \"TV Show\" --TVNetwork $channame --TVShowName \"$title_old\" --TVEpisode \"$programid\" --TVEpisodeNum $episodenumber --TVSeason $seasonnumber --description \"$description\" --title \"$subtitle_old\" 2>&1";
}

# Print the commands instead of running them if in debug mode
if ($debug) {
   print "\n\nUSING $command \n\n";
	if ($command2){
		print "\n\nUSING $command2 \n\n";
	}
	print "\n\nUSING $command3 \n\n";
}

# Run the commands
if (!$debug) {
    if ($command){
        system "$command";
    }	
    if ($command2){
		system "$command2";
	}
	if ($command3){
	   system "$command3";
	   system "rm $newfilename";
	   system "mv $exportdir/*temp* $newfilename";
	}
    	
    # Save Data
	$query = "INSERT into mythexport VALUES(NULL,?,?,?,?,NOW(),DATE_ADD(NOW(),INTERVAL ? DAY),?,?)";
	$query_handle = $connect->prepare($query);
	$query_handle->execute($webfilename,$title_old,$subtitle,$description,$delete_period,$airdate,$podcast_name)  || die "ERROR: Unable to update table.";
}
