|
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 |
}
|