Updated for Ubuntu 22.04. Also fixed merge videos cmin check
[videoscripts/.git] / make_home_videos.pl
1 #!/usr/bin/perl
2
3 # Prerequisites:
4 # This script requires the following linux packages to be installed:
5 # 1) dvgrab    : apt-get install dvgrab
6 # 2) mencoder  : http://www.mplayerhq.hu/homepage/design7/dload.html
7 # 3) transcode : vi sources.list; deb ftp://ftp.nerim.net/debian-marillat sarge main
8 #                apt-get install transcode
9 # 4) avimerge  : apt-get install transcode-utils
10
11 use Getopt::Std;
12
13 ############################################################
14 # GLOBALS
15 ############################################################
16 $uid = "ajp";
17 $gid = "ajp";
18 #bitrate=3000 is a good choice to allow 2 90 minutes tapes to fit on one DVD
19 #bitrate=4500 is a good choice to allow 2 60 minute tapes to fit on one DVD
20 $bitrate = 4500; 
21 $encoded_dir = "xvid_clips";
22 $merge_dir = "daily_clips";
23 $default_tape_name = "home_video";
24 $playlist_extension = "pls";
25 $dvgrab = "/usr/bin/dvgrab";
26 $mencoder = "/usr/bin/mencoder";
27 $mplayer = "/usr/bin/mplayer";
28 $avimerge = "/usr/bin/avimerge";
29 $avisplit = "/usr/bin/avisplit";
30 $xvid_suffix = "_xvid$bitrate";
31 $dvgrab_log = "../dvgrab.log";
32 $mencoder_log = "../encoding_errors.log";
33 $merge_log = "../avimerge.log";
34 $max_dv_clip_size_in_mb = "1023"; #I had to keep it at this size to avoid errors until avisplit fixed them.
35 #$max_dv_clip_size_in_mb = "22000"; #avisplit still doesn't seem to do things right for large clips
36 # max_tape_grab_time=7200 Allows Max time of 2 hours to allow a tape to be dumped (good for 90 min tape)
37 # max_tape_grab_time=5400 Allows Max time of 1.5 hours to allow a tape to be dumped (good for 60 min tape)
38 $max_tape_grab_time = 7200;
39 #$dvgrab_options = "--autosplit --timestamp --format dv2 --opendml --buffers 2048 --rewind";
40 #$dvgrab_options = "-autosplit -timestamp -format dv2 -opendml -buffers 2048 -rewind";
41 $dvgrab_options = "-autosplit -timestamp -format dv2 -opendml -rewind";
42 $encode_options_pass1 = "-mc 0 -oac mp3lame -lameopts cbr:cbr=128 -ffourcc XVID -fps 29.97 -ovc lavc ";
43 $encode_options_pass1.= "-lavcopts vcodec=mpeg4:vhq:vbitrate=$bitrate:keyint=15:vpass=1 -vf pp=ci";
44 $encode_options_pass2 = "-mc 0 -oac mp3lame -lameopts cbr:cbr=128 -ffourcc XVID -fps 29.97 -ovc lavc ";
45 $encode_options_pass2.= "-lavcopts vcodec=mpeg4:vhq:vbitrate=$bitrate:keyint=15:vpass=2 -vf pp=ci";
46 $mplayer_identify_options = "-vo null -ao null -identify -frames 0";
47
48 $SIG{ALRM} = \&stop_dvgrab;
49 $SIG{INT}  = \&exit_now;
50 $SIG{SUSP} = \&exit_now;
51
52 ############################################################
53 # COMMAND LINE OPTIONS & PROCESSING
54 ############################################################
55 getopts('hecrasvpmb:t:n:d:');
56 if($opt_h) { &usage(); }
57
58 sub usage() {
59     print "\n";
60     print "Usage: $0 <options>\n";
61     print "   -e         Encode captured raw video from camera.\n";
62     print "   -c         Capture raw video from camera.\n";
63     print "   -m         Merge videos clips found in the directory specified by -d <dir>. \n";
64     print "   -p         Create video playlists for each year & month of video clips in the directory specified by -d <dir>.\n";
65     print "   -n <tape>  Name of the tape to prepend to the front of each videoclip filename.\n";
66     print "   -d <dir>   Directory we want to encode. If not specified, <dir>=<tape>\n";
67     print "   -a         Use abbreviated names for merged video clips: yyyy-mm-dd.avi vs. <tape>.yyyy.mm.dd.avi\n";
68     print "   -s         Simulate Mode. Don't encode, just tell me what you would do.\n";
69     print "   -h         Show this usage\n";
70     print "   -v         Show verbosity\n";
71     print "   -t <min>   Time in minutes to grab from the camera (default "; print $max_tape_grab_time/60; print ")\n";
72     print "   -b <mb>    Max size each clip grabbed should be in MB (default $max_dv_clip_size_in_mb)\n";
73     print "   -r         Reboot the system once the script completes.\n";
74     print "\n";
75     print "File Structure:\n";
76     print "   <dir>/<tape>*.avi               MiniDV Raw AVI files\n";
77     print "   <dir>/$encoded_dir/<tape>*.avi    XVID compressed versions of the Raw AVI files\n";
78     print "   <dir>/$merge_dir/<tape>*.avi   All XVID clips taken on each day combined together\n";
79     print "\n";
80     exit 1;
81 }
82
83 if(!$opt_n && !$opt_d) { &usage(); }
84
85 if(!$opt_c && !$opt_e && !$opt_p && $opt_m) {
86     if(!$opt_d) { &usage(); }
87     $opt_m = $opt_d;
88     $merge_only = 1;
89 }
90 else { $merge_only = 0; }
91
92 if(!$opt_c && !$opt_e && !$opt_m && $opt_p) {
93     if(!$opt_d) { &usage(); }
94     $opt_p = $opt_d;
95     $playlist_only = 1;
96 }
97 else { $playlist_only = 0; }
98
99 if(!$opt_d) { 
100     system("mkdir -p $opt_n");
101     chdir "$opt_n";
102     $opt_d = $opt_n;
103 } else {
104     if(! -d $opt_d) { die "-E- Could not cd to $opt_d\n"; }
105     chdir "$opt_d";
106     if(!$opt_n) { $opt_n = $default_tape_name; }
107 }
108 if($opt_t) { $max_tape_grab_time = $opt_t * 60; }
109 if($opt_b) { $max_dv_clip_size_in_mb = $opt_b; }
110 $video_directory = qx[pwd]; chomp($video_directory);
111
112 print "\n-> Changed working directory to: $video_directory\n";
113
114 ############################################################
115 # MAIN
116 ############################################################
117 print "\n";
118
119 if($opt_c) { &capture_videos(); }
120 if($opt_e) { &encode_videos(); }
121 if($opt_m) { &merge_videos(); }
122 if($opt_p) { &create_playlists(); }
123
124 # Fix the permissions
125 print "-> Fixing permissions: chown -R $uid.$gid\n";
126 system("chown -R $uid.$gid ../$opt_d");
127
128 print "\n";
129 if($opt_r) { &reboot_system(); }
130
131 ############################################################
132 # SUBROUTINES
133 ############################################################
134
135 sub exit_now {
136     print "-E- Interupt signal recieved. Exiting..\n";
137     &stop_dvgrab();
138     exit 1;
139 }
140
141 # A little routine to stop dvgrab. It hangs when it hits the end of a tape.
142 sub stop_dvgrab {
143     my $prog = "dvgrab";
144     my @pids;
145     my $numkilled = 0;
146     @pids = `ps -efw | grep $prog | grep -v grep | grep -v $0`;
147     foreach $pid ( @pids ) {
148         $oldpid = $pid;
149         if( $pid =~ s/^\S*\s*(\d*).*$prog.*\n$/$1/ ) {
150             if($pid eq "") { next; }
151             print "$date : -I- Stopped dvgrab Process id: \"$pid\"\n";
152             system("kill -9 $pid");
153             $numkilled++;
154         }
155     }
156     sleep 5;
157 }
158
159 sub capture_videos {
160     if(! -f $dvgrab ){ die "-E- Missing required component: $dvgrab\n"; }
161     alarm($max_tape_grab_time);
162     $minutes = $max_tape_grab_time / 60;
163     print "-> Max tape grab time set to $minutes minutes\n";
164     print "-> Grabbing raw video from the camera\n";
165     print "   Sending the status of grabbing the raw video to $dvgrab_log\n";
166     print "   Press CTRL-C once to stop the video grab and move onto the next step\n";
167     print "   Press CTRL-C three times to stop the video grab and exit\n";
168     print "\n";
169     if(!$opt_s) {
170         system("rm -f $dvgrab_log; touch $dvgrab_log; tail -f $dvgrab_log &");
171         print "$dvgrab $dvgrab_options --size $max_dv_clip_size_in_mb ${opt_n}_\n";
172         system("$dvgrab $dvgrab_options --size $max_dv_clip_size_in_mb ${opt_n}_ 2> $dvgrab_log");
173     }
174     else { print "   $dvgrab $dvgrab_options --size $max_dv_clip_size_in_mb ${opt_n}_ 2> $dvgrab_log\n"; }
175     alarm(0);
176     sleep 5; # Give some time to catch an interrupt if needed
177     print "-> Done grabbing video from camera\n\n";
178
179     # Now see if we need to add a ".avi" extension to the video files dvgrab created
180     opendir(VIDEODIR, $video_directory) or die "-E- Could not open: $video_directory";
181     my @all_files = readdir VIDEODIR;
182     closedir(VIDEODIR);
183     foreach $file (@all_files) {
184       if(($file =~ /$opt_n/) && ($file !~ /$opt_n.*?\.avi/)) { rename("$file","$file.avi"); }
185     }
186 }
187
188 sub encode_videos {
189     if(! -f $mencoder) { die "-E- Missing required component: $mencoder\n"; }
190     print "-> Creating high quality xvid copies of each video clip\n";
191   
192     system("rm -f $mencoder_log");
193     system("touch $mencoder_log");
194     
195     my $current_file = "";
196     my $current_output_file = "";
197     my $prev_output_file_dv = "";
198     my $prev_output_file_xvid = "";
199
200     print "   Processing All Videos in Directory: $video_directory\n";
201     
202     opendir(VIDEODIR, $video_directory) or die "-E- Could not open: $video_directory";
203     my @all_files = readdir VIDEODIR;
204     closedir(VIDEODIR);
205     @all_files = sort @all_files;
206     system "mkdir -p $encoded_dir";
207     
208     foreach $current_file (@all_files) {
209         next if -d $current_file;
210         next if (!($current_file =~ /\.avi$/));
211         $current_output_file = $current_file;
212         $current_output_file =~ s/\.avi$/$xvid_suffix\.avi/;
213         if (-f "./$encoded_dir/$current_output_file") {
214             print "   skipping $current_file - ./$encoded_dir/$current_output_file already exists.\n";
215             next;
216         }
217         print "   $current_file -> ./$encoded_dir/$current_output_file\n";
218         if($opt_v) {
219             print "$mencoder $encode_options_pass1 -o ./$encoded_dir/$current_output_file -idx $current_file\n";
220             print "$mencoder $encode_options_pass2 -o ./$encoded_dir/$current_output_file -idx $current_file\n";
221         }
222         if(!$opt_s) {
223             $tempfile = `mktemp`; chomp($tempfile);
224             system("$mencoder $encode_options_pass1 -o ./$encoded_dir/$current_output_file -idx $current_file 2>$tempfile");
225             system("$mencoder $encode_options_pass2 -o ./$encoded_dir/$current_output_file -idx $current_file 2>>$tempfile");
226             if(! -e "./$encoded_dir/$current_output_file") {
227                 print "-WARNING- File could not be encoded. Attempting repair through avisplit. Encoding reattempt will follow.\n";
228                 if(! -f $avisplit) { die "-E- Missing required component: $avisplit\n"; }
229                 $split_size = $max_dv_clip_size_in_mb * 10;
230                 system("$avisplit -s $split_size -i $current_file");
231                 #print "$avisplit -s $split_size -i $current_file\n";
232                 system("mv $current_file-0000 $current_file");
233                 system("$mencoder $encode_options_pass1 -o ./$encoded_dir/$current_output_file -idx $current_file 2>>$tempfile");
234                 system("$mencoder $encode_options_pass2 -o ./$encoded_dir/$current_output_file -idx $current_file 2>>$tempfile");
235                 if(! -e "./$encoded_dir/$current_output_file") {
236                     print "-E- Errors encountered encoding $current_file\n";
237                     system("echo \"-> Errors encountered encoding $current_file\" >> $mencoder_log");
238                     system("cat $tempfile >> $mencoder_log");
239                 }
240             }
241             unlink "$tempfile";
242             sleep 2; # Give some time to catch an interrupt if needed
243         }
244     }
245     
246     $current_output_file = "";
247     print "-> Done creating xvid copies\n\n";
248 }
249
250 sub merge_videos {
251     if(! -f $avimerge) { die "-E- Missing required component: $avimerge\n"; }
252     print "-> Now creating merged videos for each day\n";
253     
254     system("rm -f $merge_log");
255     system("touch $merge_log");
256     system "mkdir -p $merge_dir";
257     
258     if($merge_only) {
259         print "-> Merging all videos by day from this directory: $video_directory\n";
260         opendir(VIDEODIR, "$video_directory") or die "-E- Could not open: $video_directory";
261     } else {
262         print "-> Merging all videos by day from this directory: $video_directory/$encoded_dir\n";
263         opendir(VIDEODIR, "$video_directory/$encoded_dir") or die "-E- Could not open: $video_directory/$encoded_dir";
264     }
265     print "\n";
266     
267     my @all_files = readdir VIDEODIR;
268     closedir(VIDEODIR);
269     @all_files = sort @all_files;
270     foreach $current_file (@all_files) {
271         next if -d $current_file;
272         next if (!($current_file =~ /\.avi$/));
273         next if (system("$mplayer $mplayer_identify_options $current_file 2>&1 | grep \"Missing video stream\" > /dev/null") == 0);
274         $current_output_file = $current_file;
275         $current_output_file =~ s/_[\-0-9]{8}\.avi$/\.avi/;
276         $current_output_file =~ s/_[\-0-9]{8}$xvid_suffix\.avi$/\.avi/;
277         if($opt_a && $current_output_file =~ /.*?(\d\d\d\d)\.(\d\d)\.(\d\d)/) {
278             $current_output_file = "$1-$2-$3.avi";
279             if( -f "./$merge_dir/$current_output_file" ) {
280                 my $num = 1;
281                 while(-f "./$merge_dir/$1-$2-$3.$num.avi") { $num++; }
282                 $current_output_file = "$1-$2-$3.$num.avi";
283             }
284         }
285         if (!($prev_output_file eq $current_output_file) && !($prev_output_file eq "")) {
286             print "   Creating merged video: ./$merge_dir/$prev_output_file_xvid\n";
287             if($opt_v) {
288                 print "   $avimerge -c -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge\n";
289             }
290             if(!$opt_s) {
291                 system("$avimerge -c -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge >> $merge_log");
292                 sleep 2; # Give some time to catch an interrupt if needed
293             }
294             $files_to_merge = "";
295             print "\n";
296         }
297         #store all the file names into one variable
298         if($merge_only) {           
299             $files_to_merge .= " ./$current_file";
300             print "   adding ./$current_file to merge list\n";
301         } else {
302             $files_to_merge .= " ./$encoded_dir/$current_file";
303             print "   adding ./$encoded_dir/$current_file to merge list\n";
304         }
305         $prev_output_file = $current_output_file;
306         $prev_output_file_xvid = $current_output_file;
307     }
308     
309     #Now merge the last one
310     if (!($current_output_file eq "")) {
311         print "   Creating merged video: ./$merge_dir/$prev_output_file_xvid\n";
312         if($opt_v) {
313             print "   $avimerge -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge\n";
314         }
315         if(!$opt_s) {
316             system("$avimerge -o ./$merge_dir/$prev_output_file_xvid -i $files_to_merge");
317         }
318         $files_to_merge = "";
319         print "\n";
320     }
321     
322     print "-> Done creating merged videos for each day\n\n";
323 }
324
325 sub create_playlists {
326     print "-> Now creating playlists of merged videos for each month & year\n";
327
328     if($playlist_only) {
329         print "-> Creating playlists for each month & year of clips from this directory: $video_directory\n";
330         opendir(VIDEODIR, "$video_directory") or die "-E- could not open: $video_directory";
331         chdir "$video_directory";
332     } else {
333         print "-> Creating playlists for each month & year of clips from this directory: $video_directory/$merge_dir\n";
334         opendir(VIDEODIR, "$video_directory/$merge_dir") or die "-E- could not open: $video_directory/$merge_dir";      
335         chdir "$video_directory/$merge_dir";
336     }
337
338     my @all_files = readdir VIDEODIR;
339     closedir(VIDEODIR);
340     @all_files = sort @all_files;
341
342     print "   Removing all existing playlists from the directory\n";
343     system("rm *.$playlist_extension > /dev/null 2>&1");
344     
345     foreach $file (@all_files) {
346         next if -d $file;
347         next if ($file !~ /\.avi$/ && $file !~ /\.mpg$/);
348         if($opt_a) { $file =~ /(\d\d\d\d)-(\d\d)-(\d\d)/; $year = $1; $month = $2; }
349         else { $file =~ /(\d\d\d\d).(\d\d).(\d\d)/; $year = $1; $month = $2; }
350         print "   Adding $file to $year.$playlist_extension & $year-$month.$playlist_extension\n";
351         system("echo \"$file\" >> $year.$playlist_extension");
352         system("echo \"$file\" >> $year-$month.$playlist_extension");
353     }
354     chdir "$video_directory";
355 }
356
357 # Reboot the system if requested
358 sub reboot_system {
359     print "-> Rebooting the system in 10 seconds... Press CTRL-C to abort...\n";
360     sleep 10;
361     system("sudo /sbin/reboot") if(!$opt_s);
362 }
363