package export_DivX; # Define any command line parameters used by this module *Arg_str = *main::Arg_str; push @Arg_str, 'h_res:i', 'v_res:i', 'motion', 'deinterlace', 'hq'; # Load the nuv utilities use nuv_utils; # Make sure we have pointers to the main:: namespace for certain variables *Args = *main::Args; *Prog = *main::Prog; *gui = *main::gui; sub new { my $class = shift; my $self = { # The name of this export function - displayed to users in interactive mode 'name' => 'Export DivX (CLI support)', # The --mode match (regex) for non-interactive command line interface 'cli_mode' => 'divx', # Set to 0 to disable this export function (eg. if there are errors) 'enabled' => 1, # An array of errors - generally displayed to users when they try to use a disabled function 'errors' => undef, # Misc variables all export functions should keep track of 'started' => 0, 'children' => [], 'episode' => undef, 'is_cli' => 0, # Variables specific to export_DivX 'savepath' => '.', 'outfile' => 'out.avi', 'fifodir' => "fifodir.$$", 'use_cutlist' => 0, 'a_bitrate' => 64, 'v_bitrate' => 256, 'h_res' => 320, 'v_res' => 240, 'sql_file' => undef, 'deinterlace' => 0, 'motion' => 0, 'hq' => 0, @_ #allows user-specified attributes to override the defaults }; bless($self, $class); # Return return $self; } sub gather_cli_data { my $self = shift; return undef unless ($Args{mode} && $Args{mode} =~ /$self->{cli_mode}/i); # Get the save path ($self->{savepath}, $self->{outfile}) = $gui->query_filename($self->{default_outfile}, 'avi', $self->{savepath}); # Audio bitrate $self->{a_bitrate} = $Args{a_bitrate} if ($Args{a_bitrate}); die "Audio bitrate is too low; please choose a bitrate >= 64.\n\n" if ($self->{a_bitrate} < 64); die "Audio bitrate is too high; please choose a bitrate <= 448\n\n" if ($self->{a_bitrate} > 448); # Video Bitrate $self->{v_bitrate} = $Args{v_bitrate} if ($Args{v_bitrate}); die "Video bitrate is too low; please choose a bitrate >= 256.\n\n" if ($self->{v_bitrate} < 256); die "Video bitrate is too high; please choose a bitrate <= 4000\n\n" if ($self->{v_bitrate} > 4000); # Deinterlace $self->{deinterlace} = $Args{deinterlace} if ($Args{deinterlace}); # Four Motion $self->{motion} = $Args{motion} if ($Args{motion}); # HQ $self->{hq} = $Args{hq} if ($Args{hq}); # Noise reduction $self->{noise_reduction} = $Args{noise_reduction}; # Cutlist and noise reduction don't require checking $self->{use_cutlist} = ($self->{episode}->{cutlist} && $self->{episode}->{cutlist} =~ /\d/ && $Args{use_cutlist}) ? 1 : 0; # Resolution X / Y $self->{h_res} = $Args{h_res} if ($Args{h_res}); $self->{v_res} = $Args{v_res} if ($Args{v_res}); # Return true, so we skip the interactive data gathering return 1; } sub gather_data { my $self = shift; my $default_filename; # Ask the user for the filename if($self->{episode}->{show_name} ne 'Untitled' and $self->{episode}->{title} ne 'Untitled') { $default_filename = $self->{episode}->{show_name}.' - '.$self->{episode}->{title}; } elsif($self->{episode}->{show_name} ne 'Untitled') { $default_filename = $self->{episode}->{show_name}; } elsif($self->{episode}->{title} ne 'Untitled') { $default_filename = $self->{episode}->{title}; } # grabbing data from the command line? return if ($self->gather_cli_data()); # Get the save path $self->{savepath} = $gui->query_savepath(); $self->{outfile} = $gui->query_filename($default_filename, 'avi', $self->{savepath}); # Ask the user if he/she wants to use the cutlist if ($self->{episode}->{cutlist} && $self->{episode}->{cutlist} =~ /\d/) { $self->{use_cutlist} = $gui->query_text('Enable Myth cutlist?', 'yesno', $self->{use_cutlist} ? 'Yes' : 'No'); } else { $gui->notify('No cutlist found. Hopefully this means that you already removed the commercials.'); } # Ask the user what audio bitrate he/she wants my $a_bitrate = $gui->query_text('Audio bitrate?', 'int', $self->{a_bitrate}); $self->{a_bitrate} = $a_bitrate; # Ask the user what video bitrate he/she wants my $v_bitrate = $gui->query_text('Video bitrate?', 'int', $self->{v_bitrate}); $self->{v_bitrate} = $v_bitrate; # Ask the user what horiz res he/she wants my $h_res = $gui->query_text('Horizontal resolution?', 'int', $self->{h_res}); $self->{h_res} = $h_res; # Ask the user what vert res he/she wants my $v_res = $gui->query_text('Vertical resolution?', 'int', $self->{v_res}); $self->{v_res} = $v_res; } sub execute { my $self = shift; # make sure that the fifo dir is clean if (-e "$self->{fifodir}/vidout" || -e "$self->{fifodir}/audout") { die "Possibly stale mythtranscode fifo's in fifodir.\nPlease remove them before running nuvexport.\n\n"; } # Gather any necessary data $self->{episode} = shift; $self->gather_data; # Load nuv info my %nuv_info = nuv_info($self->{episode}->{filename}); # Set this to true so that the cleanup routine actually runs $self->{started} = 1; # Create a directory for mythtranscode's fifo's unless (-d $self->{fifodir}) { mkdir($self->{fifodir}, 0755) or die "Can't create $self->{fifodir}: $!\n\n"; } # Here, we have to fork off a copy of mythtranscode my $command = "nice -n 19 mythtranscode -p autodetect -c $self->{episode}->{channel} -s $self->{episode}->{start_time_sep} -f $self->{fifodir}"; $command .= ' --honorcutlist' if ($self->{use_cutlist}); push @{$self->{children}}, fork_command($command); fifos_wait($self->{fifodir}); # Now we fork off a process to encode everything $safe_outfile = shell_escape($self->{savepath}.'/'.$self->{outfile}); $extracommand .= ' -hq' if ($self->{hq}); $extracommand .= ' -4mv' if ($self->{motion}); $extracommand .= ' -deinterlace' if ($self->{deinterlace}); $command = "nice -n 19 ffmpeg -y -f s16le -ar $nuv_info{audio_sample_rate} -ac 2 -i $self->{fifodir}/audout -f rawvideo -s $nuv_info{width}x$nuv_info{height} -r $nuv_info{fps} -i $self->{fifodir}/vidout -b $self->{v_bitrate} -ab $self->{a_bitrate} -s $self->{h_res}x$self->{v_res} $extracommand $safe_outfile"; push @{$self->{children}}, fork_command($command); # Wait for child processes to finish 1 while (wait > 0); $self->{children} = undef; } sub cleanup { my $self = shift; return unless ($self->{started}); # Make sure any child processes also go away if ($self->{children} && @{$self->{children}}) { foreach my $child (@{$self->{children}}) { kill('INT', $child); } 1 while (wait > 0); } # Remove any temporary files foreach my $file ("$self->{fifodir}/audout", "$self->{fifodir}/vidout") { unlink $file if (-e $file); } rmdir $self->{fifodir} if (-e $self->{fifodir}); } 1; #return true