~mythbuntu/mythexport/trunk

1 by john
1.0.1 Release
1
#!/usr/bin/perl -w
2
# mythexport v1.0.1
3
# By: John Baab
4
# Email: john.baab@gmail.com
5
# Purpose: Script for exporting mythtv recordings into formats used by portable devices.
6
# Requirements: ffmpeg (with aac,xvid,h264 support), perl and the DBI & DBD::mysql modules, 
7
#					MythTV perl bindings, AtomicParsley
8
#
9
# Modified from ipodexport.pl By Justin Hornsby 27 July 2007
10
#
11
#
12
# License:
13
#
14
# This Package is free software; you can redistribute it and/or
15
# modify it under the terms of the GNU General Public
16
# License as published by the Free Software Foundation; either
17
# version 2 of the License, or (at your option) any later version.
18
#
19
# This package is distributed in the hope that it will be useful,
20
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22
# General Public License for more details.
23
#
24
# You should have received a copy of the GNU General Public
25
# License along with this package; if not, write to the Free Software
26
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
27
#
28
# On Debian & Ubuntu systems, a complete copy of the GPL can be found under
29
# /usr/share/common-licenses/GPL-2, or (at your option) any later version
30
31
use DBI;
32
use DBD::mysql;
33
use MythTV;
34
use strict;
35
36
# Set default values
37
my $exportdir = '/home/mythtv/';
38
my $audio_bitrate = '96kb';
39
my $video_bitrate = '300kb';
40
my $aspect = '4:3';
41
my $size = "320x240";
42
43
my $connect = undef;
44
my $debug = 0;
45
46
my ($starttime, $chanid, $title, $subtitle, $description, $syndicatedepisodenumber, $showtype, $programid, $basename, $exportcodec, $exportdevice) = "";
47
48
##################################
49
#                                #
50
#    Main code starts here !!    #
51
#                                #
52
##################################
53
54
my $usage = "\nHow to use mythexport : \n"
55
    ."\nchanid = Channel ID associated with the recording to export.\n"
56
    ."starttime = Recording start time in either 'yyyy-mm-dd hh:mm:ss' or 'yyyymmddhhmmss' format.\n"
57
    ."exportdir = Directory to export completed MP4 files to (note the user the script runs as must have write permission on that directory).\n"
58
    ."size = Frame size of output file.  320x240 is the default value.\n"
59
    ."aspect = Aspect ratio of output file.  Valid values are 4:3 (default) and 16:9.\n"
60
    ."audio_bitrate = Audio bitrate in output file in kbps.  Default value is 96kb.\n"
61
    ."video_bitrate = Video bitrate in output file in kbps.  Default value is 300kb.\n"
62
	."export_codec = Acceptable export codecs: mpeg4, xvid, h264.\n"
63
	."export_device = Acceptable export devices: ipod, psp, archos.\n"
64
    ."debug = Enable debugging information - outputs which commands would be run.\n"
65
    ."\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";
66
67
# get this script's ARGS
68
#
69
70
my $num = $#ARGV + 1;
71
72
# if user hasn't passed enough arguments, die and print the usage info
73
if ($num <= 2) {
74
    die "$usage";
75
}
76
77
#
78
# Get all the arguments
79
#
80
81
foreach (@ARGV){
82
83
    if ($_ =~ m/debug/) {
84
        $debug = 1;
85
    }
86
    elsif ($_ =~ m/size/) {
87
        $size = (split(/\=/,$_))[1];
88
    }
89
    elsif ($_ =~ m/aspect/) {
90
        $aspect = (split(/\=/,$_))[1];
91
    }
92
    elsif ($_ =~ m/audio_bitrate/) {
93
        $audio_bitrate = (split(/\=/,$_))[1];
94
    }
95
	elsif ($_ =~ m/video_bitrate/) {
96
        $video_bitrate = (split(/\=/,$_))[1];
97
    }
98
    elsif ($_ =~ m/starttime/) {
99
        $starttime = (split(/\=/,$_))[1];
100
    }
101
    elsif ($_ =~ m/chanid/) {
102
        $chanid = (split(/\=/,$_))[1];
103
    }
104
    elsif ($_ =~ m/exportdir/) {
105
        $exportdir = (split(/\=/,$_))[1];
106
    }
107
	elsif ($_ =~ m/export_codec/) {
108
        $exportcodec = (split(/\=/,$_))[1];
109
    }
110
	elsif ($_ =~ m/export_device/){
111
		$exportdevice = (split(/\=/,$_))[1];
112
	}
113
}
114
115
#
116
#
117
my $myth = new MythTV();
118
# connect to database
119
$connect = $myth->{'dbh'};
120
121
# PREPARE THE QUERY
122
my $query = "SELECT rec.title, rec.subtitle, rec.description, pg.syndicatedepisodenumber, pg.showtype, rec.programid, rec.basename
123
FROM recorded rec left join program pg on pg.programid=rec.programid
124
WHERE rec.chanid=$chanid AND rec.starttime='$starttime'";
125
#print $query;
126
my $query_handle = $connect->prepare($query);
127
128
129
# EXECUTE THE QUERY
130
$query_handle->execute() || die "Cannot connect to database \n";
131
132
# BIND TABLE COLUMNS TO VARIABLES
133
$query_handle->bind_columns(undef, \$title, \$subtitle, \$description, \$syndicatedepisodenumber, \$showtype, \$programid, \$basename);
134
135
# LOOP THROUGH RESULTS
136
$query_handle->fetch();
137
138
my $schemaVer = $myth->backend_setting('DBSchemaVer');
139
# Storage Groups were added in DBSchemaVer 1171
140
# FIND WHERE THE RECORDINGS LIVE
141
my $dir = 'UNKNOWN';
142
if ($schemaVer < 1171)
143
{
144
    if ($debug) {
145
        print ("Using compatibility mode\n");
146
    }
147
    $dir = $myth->backend_setting('RecordFilePrefix');
148
}
149
else
150
{
151
    if ($debug) {
152
        print ("Going into new mode\n");
153
    }
154
    my $storagegroup = new MythTV::StorageGroup();
155
    $dir = $storagegroup->FindRecordingDir($basename);
156
}
157
158
# FIND OUT THE CHANNEL NAME
159
$query = "SELECT callsign FROM channel WHERE chanid=?";
160
$query_handle = $connect->prepare($query);
161
$query_handle->execute($chanid)  || die "Unable to query settings table";
162
163
my $channame = $query_handle->fetchrow_array;
164
165
# replace whitespace in channame with dashes
166
$channame =~ s/\s+/-/g;
167
168
# replace non-word characters in title with underscores
169
my $title_old = $title;
170
$title =~ s/\W+/_/g;
171
# replace non-word characters in subtitle with underscores
172
my $subtitle_old = $subtitle;
173
$subtitle =~ s/\W+/_/g;
174
175
# Remove non alphanumeric chars from $starttime & $endtime
176
my $newstarttime = $starttime;
177
$newstarttime =~ s/[|^\W|\s|-|]//g;
178
#my $filename = $dir."/".$chanid."_".$newstarttime.".mpg";
179
#my $newfilename = $exportdir."/".$channame."_".$title."_".$subtitle."_".$newstarttime;
180
my $filename = $dir."/".$basename;
181
my $newfilename = $exportdir."/".$channame."-".$title."-".$subtitle."-".$newstarttime;
182
$newfilename =~ s/\..*?$//;
183
184
$syndicatedepisodenumber =~ m/^.*?(\d*)(\d{2})$/;
185
my $seasonnumber = $1;
186
my $episodenumber = $2;
187
188
# Trim any characters over 63 (59+4 for the extentsion), 
189
# it seems iTunes does not like files with lenghts this long.
190
$newfilename =~ s/^(.*\/(.{1,59})).*$/$1/g;
191
192
if ($debug) {
193
    print "\n\n Source filename:$filename \nDestination filename:$newfilename\n \n";
194
}
195
196
# move to the export directory incase any logs get written
197
chdir $exportdir;
198
199
# Now run ffmpeg to get iPod compatible video with a simple ffmpeg line
200
my $command = "";
201
my $command2 = "";
202
my $command3 = "";
203
204
if ($exportdevice eq "ipod"){
205
    $newfilename .= ".mp4";
206
	if ($exportcodec eq "mpeg4" or $exportcodec eq "xvid"){
207
		# This VBR line works well, but only in xvid.  Causes buffere underflows in Gutsy.	
208
		#$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";
209
		# This VBR line works well, but only in xvid.  Causes buffere underflows in Gutsy.
210
		#$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"
211
	
212
		# Works cleanly with both vcodecs, but produces larger files.
213
		$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 -aspect $aspect '$newfilename' 2>&1";
214
	}
215
	elsif ($exportcodec eq "h264"){
216
		# First pass
217
		$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";
218
		# Second pass
219
		$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";
220
	}
221
	else{
222
		print "\n\nERROR: Unexpected Export Codec.";
223
		exit;
224
	}
225
}
226
elsif ($exportdevice eq "psp"){
227
    $newfilename .= ".mp4";
228
	if ($exportcodec eq "mpeg4" or $exportcodec eq "xvid"){
229
		$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";
230
	}
231
	elsif ($exportcodec eq "h264"){
232
		# PSP h.254 encoding doesn't seem to be working.
233
		print "\n\nError: PSP h.264 encoding is not currently supported.  Use xvid or mpeg4 instead.";
234
		exit;		
235
		# According to the ffmpeg website, this should have worked.
236
		#$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";
237
	}
238
	else{
239
		print "\n\nERROR: Unexpected Export Codec.";
240
		exit;
241
	}
242
}
243
elsif ($exportdevice eq "archos"){
244
    $newfilename .= ".avi";
245
    if ($exportcodec eq "mpeg4" or $exportcodec eq "xvid"){
246
        # ffmpeg line from courtesy of freymann
247
        # ffmpeg -i infile.mpg -vcodec mpeg4 -s 320x240 -b 906k -acodec mp3 -ab 192kb outfile.avi
248
        # modified to use all the flags used in other ffmpeg lines.
249
        $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";
250
    }
251
	elsif ($exportcodec eq "h264"){
252
        print "\n\nError: Archos h.264 encoding is not currently supported.  Use xvid or mpeg4 instead.";
253
		exit;
254
	}
255
	else{
256
		print "\n\nERROR: Unexpected Export Codec.";
257
		exit;
258
	}
259
}
260
else{
261
	print "\n\nERROR: Unexpected Export Device.";
262
	exit;
263
}
264
265
# Update mp4 info using AtomicParsley
266
# need to fix this so movies actually show up as movies.
267
if ($exportdevice ne "archos"){
268
    $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";
269
}
270
271
# Print the commands instead of running them if in debug mode
272
if ($debug) {
273
    print "\n\nUSING $command \n\n";
274
	if ($command2){
275
		print "\n\nUSING $command2 \n\n";
276
	}
277
	print "\n\nUSING $command3 \n\n";
278
}
279
280
# Run the commands
281
if (!$debug) {
282
    system "$command";
283
	if ($command2){
284
		system "$command2";
285
	}
286
	if ($command3){
287
	   system "$command3";
288
	   system "rm $newfilename";
289
	   system "mv $exportdir/*temp* $newfilename";
290
	}
291
}