· 7 years ago · Nov 29, 2018, 01:46 PM
1#!/usr/bin/perl -w
2### User-Job-Command:
3### /path/to/copy_and_transcode.pl -f %FILE% -p autodetect
4# ============================================================================
5# = NAME
6# copy_and_transcode.pl
7#
8# = PURPOSE
9# Copy a recording before transcoding, so the user can re-cut the original
10#
11# = USAGE
12my $usage = 'Usage:
13copy_and_transcode.pl -f %FILE% -p autodetect
14copy_and_transcode.pl -j %JOBID% -p autodetect
15copy_and_transcode.pl --file file [--debug] [--noaction] [transcode-arguments]
16';
17# The first two forms would invoke this as a MythTV User Job
18#
19# = DESCRIPTION
20# The exsisting mythtranscode system is designed for replacing a recording
21# with a transcoded copy. There is a setting which causes the backend to
22# keep the original file, but both the seektable and the cutlist are deleted.
23# If you are using transcode to extract multiple clips from a recording,
24# this is very inconvenient.
25#
26# This script transcodes a recording into a new recording,
27# with a trivially different starttime.
28# If the original recording had a cutlist, that should be honoured in the copy.
29#
30# = INSTALLATION
31# Copy this script to a known location, and then add the command
32# in mythtv-setup as a User Job (pg. 7 & 9 of General button) e.g.
33#
34# cp copy_and_transcode.pl /usr/local/bin
35# UserJob1 /usr/local/bin/copy_and_transcode.pl -f %FILE% -p autodetect
36# UserJobDesc1 Copy and Transcode
37#
38# = KNOWN BUGS
39#
40# = REVISION
41# $Id: copy_and_transcode.pl 20349 2009-04-11 00:04:30Z xris $
42#
43# = AUTHORS
44# Nigel Pearson
45# ============================================================================
46
47use strict;
48use MythTV;
49
50# What file are we copying/transcoding?
51my $file = '';
52my $jobid = -1;
53
54# do nothing?
55my $noexec = 0;
56
57# extra console output?
58my $DEBUG = 1;
59
60# some globals
61my ($chanid, $command, $query, $ref, $starttime);
62my ($newfilename, $newstarttime);
63
64my $mt = new MythTV();
65my $db = $mt->{'dbh'};
66
67
68# ============================================================================
69sub Die($)
70{
71 print STDERR "@_\n";
72 exit -1;
73}
74# ============================================================================
75# Parse command-line arguments, check there is something to do:
76#
77if ( ! @ARGV )
78{ Die "$usage" }
79
80while ( @ARGV && $ARGV[0] =~ m/^-/ )
81{
82 my $arg = shift;
83
84 if ( $arg eq '-d' || $arg eq '--debug' )
85 { $DEBUG = 1 }
86 elsif ( $arg eq '-n' || $arg eq '--noaction' )
87 { $noexec = 1 }
88 elsif ( $arg eq '-j' || $arg eq '--jobid' )
89 { $jobid = shift }
90 elsif ( $arg eq '-f' || $arg eq '--file' )
91 { $file = shift }
92 else
93 {
94 unshift @ARGV, $arg;
95 last;
96 }
97}
98
99if ( ! $file && $jobid == -1 )
100{
101 print "No file or job specified. $usage";
102 exit;
103}
104
105if ( $noexec )
106{ print "NoExecute mode. No transcoding or SQL database changes.\n" }
107
108# ============================================================================
109# If we were supplied a jobid, lookup chanid
110# and starttime so that we can find the filename
111#
112if ( $jobid != -1 )
113{
114 $query = $db->prepare("SELECT chanid, starttime " .
115 "FROM jobqueue WHERE id=$jobid;");
116 $query->execute || Die "Unable to query jobqueue table";
117 $ref = $query->fetchrow_hashref;
118 $chanid = $ref->{'chanid'};
119 $starttime = $ref->{'starttime'};
120 $query->finish;
121
122 if ( ! $chanid || ! $starttime )
123 { Die "Cannot find details for job $jobid" }
124
125 $query = $db->prepare("SELECT basename FROM recorded " .
126 "WHERE chanid=$chanid AND starttime='$starttime';");
127 $query->execute || Die "Unable to query recorded table";
128 ($file) = $query->fetchrow_array;
129 $query->finish;
130
131 if ( ! $file )
132 { Die "Cannot find recording for chan $chanid, starttime $starttime" }
133
134 if ( $DEBUG )
135 {
136 print "Job $jobid refers to recording chanid=$chanid,",
137 " starttime=$starttime\n"
138 }
139}
140else
141
142{
143 if ( $file =~ m/(\d+)_(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ )
144 { $chanid = $1, $starttime = "$2-$3-$4 $5:$6:$7" }
145 else
146 {
147 print "File $file has a strange name. Searching in recorded table\n";
148 $query = $db->prepare("SELECT chanid, starttime " .
149 "FROM recorded WHERE basename='$file';");
150 $query->execute || Die "Unable to query recorded table";
151 ($chanid,$starttime) = $query->fetchrow_array;
152 $query->finish;
153
154 if ( ! $chanid || ! $starttime )
155 { Die "Cannot find details for filename $file" }
156 }
157}
158
159
160# A commonly used SQL row selector:
161my $whereChanAndStarttime = "WHERE chanid=$chanid AND starttime='$starttime'";
162
163
164# ============================================================================
165# Find the directory that contains the recordings, check the file exists
166#
167
168my $dir = undef;
169my $dirs = $mt->{'video_dirs'};
170
171foreach my $d ( @$dirs )
172{
173 if ( ! -e $d )
174 { Die "Cannot find directory $dir that contains recordings" }
175
176 if ( -e "$d/$file" )
177 {
178 $dir = $d;
179 #print "$d/$file exists\n";
180 last
181 }
182 else
183 { print "$d/$file does not exist\n" }
184}
185
186if ( ! $dir )
187{ Die "Cannot find recording" }
188
189# ============================================================================
190
191# First, generate a new filename,
192# by adding one second to the starttime until we get a unique one
193#
194my ($year,$month,$day,$hour,$mins,$secs) = split m/[- :]/, $starttime;
195my $oldShortTime = sprintf "%04d%02d%02d%02d%02d%02d",
196 $year, $month, $day, $hour, $mins, $secs;
197do
198{
199 $secs ++;
200 if ( $secs > 59 )
201 { $secs -= 60, $mins ++ }
202 if ( $mins > 59 )
203 { $mins -= 60, $hour ++ }
204 if ( $hour > 23 )
205 { Die "Cannot generate a new file" }
206
207 $newstarttime = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
208 $year, $month, $day, $hour, $mins, $secs;
209 my $newShrtTm = sprintf "%04d%02d%02d%02d%02d%02d",
210 $year, $month, $day, $hour, $mins, $secs;
211 $newfilename = $file;
212 $newfilename =~ s/_$oldShortTime/_$newShrtTm/;
213} while ( -e "$dir/$newfilename" );
214
215$DEBUG && print "$dir/$newfilename seems unique\n";
216
217# ============================================================================
218# Second, do the transcode, obeying the cutlist if there is one
219#
220$query = $db->prepare("SELECT cutlist FROM recorded $whereChanAndStarttime;");
221$query->execute || Die "Unable to query recorded table";
222my ($cutlist) = $query->fetchrow_array;
223$ref = $query->fetchrow_hashref;
224$query->finish;
225
226$command = "mythtranscode -c $chanid -s '$starttime' -m --outfile $newfilename";
227if ($cutlist)
228{ $command .= ' --honorcutlist' }
229
230if ( @ARGV )
231{ $command .= ' ' . join(' ', @ARGV) }
232
233if ( $DEBUG || $noexec )
234{ print "# $command\n" }
235
236if ( ! $noexec )
237
238{
239 chdir $dir;
240 system $command;
241
242 if ( ! -e "$dir/$newfilename" )
243 { Die "Transcode failed\n" }
244
245
246 # Insert the generated position map into the recorded markup table.
247 # File has a structure like this:
248 #
249 #Type: 9
250 #0 14
251 #12 380942
252 #
253 open(MAP, "$newfilename.map") || Die "Cannot find position map file";
254
255 my $type;
256 if ( <MAP> =~ m/^Type: (\d+)$/ )
257 { $type = $1 }
258 else
259 { Die "Position map file is incomplete" }
260
261 while ( <MAP> )
262 {
263 if ( m/^(\d+) (\d+)$/ )
264 {
265 $command = "INSERT INTO recordedseek" .
266 " (chanid,starttime,mark,offset,type)" .
267 " VALUES ($chanid,'$newstarttime',$1,$2,$type);";
268
269 # This outputs too many lines for normal debugging
270 #if ( $DEBUG || $noexec )
271 #{ print "# $command\n" }
272
273 if ( ! $noexec )
274 {
275 $db->do($command) ||
276 Die "Couldn't insert data into recordedmarkup"
277 }
278 }
279 else
280 { Die "Position map file has bad data line" }
281 }
282 close MAP;
283
284 unlink "$newfilename.map" || Die "Cannot delete position map file";
285}
286
287# ============================================================================
288
289# Last, copy the existing recorded details with the new file name.
290#
291$query = $db->prepare("SELECT * FROM recorded $whereChanAndStarttime;");
292$query->execute || Die "Unable to query recorded table";
293$ref = $query->fetchrow_hashref;
294$query->finish;
295
296$ref->{'starttime'} = $newstarttime;
297$ref->{'basename'} = $newfilename;
298if ( $DEBUG && ! $noexec )
299{
300 print 'Old file size = ' . (-s "$dir/$file") . "\n";
301 print 'New file size = ' . (-s "$dir/$newfilename") . "\n";
302}
303$ref->{'filesize'} = -s "$dir/$newfilename";
304
305my $extra = 'Copy';
306if ( $cutlist )
307{ $extra = 'Cut' }
308
309if ( $ref->{'subtitle'} !~ m/$extra$/ )
310{
311 if ( $ref->{'subtitle'} )
312 { $ref->{'subtitle'} .= " - $extra" }
313 else
314 { $ref->{'subtitle'} = $extra }
315}
316
317#
318# The new recording file has no cutlist, so we don't insert that field
319#
320my @recKeys = grep(!/^cutlist$/, keys %$ref);
321
322#
323# Build up the SQL insert command:
324#
325$command = 'INSERT INTO recorded (' . join(',', @recKeys) . ') VALUES ("';
326foreach my $key ( @recKeys )
327{
328 if (defined $ref->{$key})
329 { $command .= quotemeta($ref->{$key}) . '","' }
330 else
331 { chop $command; $command .= 'NULL,"' }
332}
333
334chop $command; chop $command; # remove trailing comma quote
335
336$command .= ');';
337
338if ( $DEBUG || $noexec )
339{ print "# $command\n" }
340
341if ( ! $noexec )
342{ $db->do($command) || Die "Couldn't create new recording's record" }
343
344# ============================================================================
345
346$db->disconnect;
347
3481;