[mythtv-users] Updates to Nuvexport - mp3 export

Malc malc at porsche.demon.co.uk
Mon Jan 19 17:53:20 EST 2004


Attached are updates to nuvexport.

I've created a new function to export the audio stream to mp3.

What would you want to export just mp3?  A couple of reasons I can think of
 1. You have recorded music videos and only want the audio portion
 2.  You have recorded radio from DVB-T (at present you need to capture 
a video stream to keep myth happy)
 
 I use to extract the audio from UK Radio 4/7 broadcasts and then add it 
to my mp3 library.

2 files
 1. Minor fixes to nuvexport to add the export_mp3 function
  2. A new file export_mp3.pm (This goes in /usr/local/share/nuvexport )

Comments / additions welcome.

Chris if you are happy with the changes, could you add to the nuvexport 
distribution.

-malc-
-------------- next part --------------
package export_mp3;

# Load the nuv utilities
	use nuv_utils;

# Make sure we have pointers to the main:: namespace for certain variables
	*Prog = *main::Prog;
	*gui  = *main::gui;

	sub new {
		my $class = shift;
		my $self  = {
					 'name'            => 'Export to mp3',
					 'enabled'         => 1,
					 'started'         => 0,
					 'fifodir'         => "fifodir.$$",
					 'children'        => [],
					 'errors'          => undef,
					 'episode'         => undef,
					 'savepath'        => '.',
					 'outfile'         => 'out.mpg',
					 'tmp_a'           => 'out.mp2',
					 'tmp_v'           => 'out.m2v',
					 'bitrate'	   => '224',
					 'use_cutlist'     => 0,
					 'noise_reduction' => 1,
					 @_		#allows user-specified attributes to override the defaults
					};
		bless($self, $class);
	# Make sure that we have an mp2 encoder
		$Prog{mp2_encoder} = find_program('toolame', 'mp2enc');
		push @{$self->{errors}}, 'You need toolame or mp2enc to export an svcd.' unless ($Prog{mp2_encoder});
	# Any errors?  disable this function
		$self->{enabled} = 0 if ($self->{errors} && @{$self->{errors}} > 0);
	# Return
		return $self;
	}

	sub gather_data {
		my $self    = shift;
		my $default_filename;
	# Get the save path
		$self->{savepath} = $gui->query_savepath();
	# 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}.' - '.$self->{episode}->{start_time_sep};
		}
		elsif($self->{episode}->{title} ne 'Untitled')
		{
			$default_filename = $self->{episode}->{title}.' - '.$self->{episode}->{start_time_sep};
		}

# Ask the user what audio bitrate he/she wants
                my $bitrate = $gui->query_text('Audio bitrate?',
                                                                                 'int',

 $self->{bitrate});
                $self->{bitrate} = $bitrate;


		$self->{outfile} = $gui->query_filename($default_filename, 'mp3', $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.');
		}
	}

	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 $self->{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";
		}
	# Generate some names for the temporary audio and video files
		($self->{tmp_a} = $self->{episode}->{filename}) =~ s/\.nuv$/.mp2/;
		($self->{tmp_v} = $self->{episode}->{filename}) =~ s/\.nuv$/.m1v/;
	# 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} --fifosync";
		my $command = "nice -n 19 mythtranscode -p autodetect -c $self->{episode}->{channel} -s $self->{episode}->{start_time_sep} -f $self->{fifodir} --fifosync";
	#	$command .= ' --honorcutlist' if ($self->{use_cutlist});
		print "$command \n" ; 
		push @{$self->{children}}, fork_command($command);
	# Sleep a bit to let mythtranscode start up
		my $overload = 0;
		while (++$overload < 30 && !(-e "$self->{fifodir}/audout" && -e "$self->{fifodir}/vidout")) {
			sleep 1;
			print "Waiting for mythtranscode to set up the fifos.\n";
		}
		unless (-e "$self->{fifodir}/audout" && -e "$self->{fifodir}/vidout") {
			die "Waited too long for mythtranscode to create its fifos.  Please try again.\n\n";
		}
	# Now we fork off a process to extract  the audio
	$command = "nice -19 cat < $self->{fifodir}/audout > $self->{tmp_a}" ; 
		print "$command \n";
		push @{$self->{children}}, fork_command($command);

	# Null command for video
		$command = "nice -19 cat < $self->{fifodir}/vidout >/dev/null";

		push @{$self->{children}}, fork_command($command);
	# Wait for child processes to finish
		1 while (wait > 0);
		$self->{children} = undef;

        # Now encode the raw file to mp3
			$sample = $nuv_info{audio_sample_rate} / 1000;
		         my $safe_outfile = shell_escape($self->{outfile});
			$command = "nice -n 19 toolame -t1 -s $sample -m j -b $self->{bitrate} $self->{tmp_a} $safe_outfile";
			#$command = "nice -n 19 lame -r -v -q 2  -s $sample -m j -V 2  -B $self->{bitrate}  $self->{tmp_a} $safe_outfile";
print "$command \n";
		system($command);

	# Now tag it
	$command = "id3tag -A\"$self->{episode}->{title}\" -a\"$self->{episode}->{channel}\" -c\"$self->{episode}->{description}\" -s\"$self->{episode}->{show_name}\" $safe_outfile";
		print "$command \n";
		system($command);
	}

	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", $self->{tmp_a}, $self->{tmp_v}) {
			unlink $file if (-e $file);
		}
		rmdir $self->{fifodir} if (-e $self->{fifodir});
	}

1;	#return true
-------------- next part --------------
#!/usr/bin/perl -w
# Last Updated: 2003.09.22 (xris)

=head1 NAME

nuvexport

This is a perl script designed to export various formats from mythtv nuv
video files.  Just run the script to see the various available options.
Descriptions of the various export formats and their requirements are below.

        Main code by:  Chris Petersen
DivX and WMV code by:  Dennis Lou

=head1 MPEG-BASED NUV

nuvexport supports mpeg-based nuv files like those saved from the Hauppauge PVR
cards.  In order to get at the mpeg info, however, you will need either mpgtx
or tcprobe (part of transcode).  Either program should work fine.

=head1 FORMATS/REQUIREMENTS

Except for the basic raw nuv/sql export, all formats require ffmpeg.

=head2 NUV/SQL

I<WARNING: THIS REMOVES THE RECORDED SHOW FROM THE ORIGINATING SERVER.>

This is the most basic export format, and is intended for moving files between
different backend setups.  It merely copies the nuv file to the specified output
directory, and extracts all related database info into a matching .sql file.

To import this into another myth setup, just copy the nuv to your video directory
and run:

	mysql mythconverg < sql_file_name.sql

=head2 SVCD

Super VideoCD's are an mpeg2 enhancement of the original VideoCD format.
In order to encode an svcd, you need ffmpeg, yuvscaler, and mpeg2enc.  Most of
these come with the mjpeg package.

You will also need an mp2 audio encoder.  I strongly suggest that you grab
toolame, it's an excellent encoder based on the LAME engine.  If you don't,
nuvexport will use ffmpeg.

To multiplex the audio and video streams, you can use mjpeg's mplex program, but
I've had bad luck with this, and suggest that you install transcode in order to
get tcmplex - it works much better with SVCD mpeg2 files than mplex.

Though this script will not create the bin/cue images needed for burning an SVCD
under linux, if you want to do so, you will also need the vcdimager program.

=head2 DivX

DivX...  not much to say here, I didn't write the exporter

All you need is ffmpeg.


=head2 WMV

Windows Media Video...  not much to say here, I didn't write the exporter

All you need is ffmpeg.

NOTE: Microsoft's Windows Media Player will only play audio from WMV
files encoded using their Windows Media Audio (WMA) codec.  FFMPEG
contains a WMA decoder but not an encoder.  Therefore, Windows Media Player
will have problems with WMV files generated from this script.  If you
want to use WMV files, you should use mplayer or export to another
format and use Microsoft's Windows Media Encoder to encode a WMV file.

=cut

# Load some modules that we'll use
	use DBI;
	use Getopt::Long;
	use File::Basename;

# Add a couple of include paths so we can load the various export and gui modules
	use lib dirname($ENV{'_'}), '/usr/share/nuvexport', '/usr/local/share/nuvexport';

# A global list of the various programs (and full paths) we'll be needing/using
	our %Prog;
	our %Shows;
	our @Functions;
	our %Args;
	our $DEBUG;
	our $gui;
	our $video_dir;
	our $num_shows;
	our $dbh;
	our $hostname;

# Load the nuv utilities
	use nuv_utils;

# Load the gui modules
	use gui_text;

# Load the export modules
	use export_DivX;
	use export_NUV_SQL;
	use export_SVCD;
	use export_VCD;
	use export_WMV;
	use export_Trans_XviD;
	use export_Trans_VCD;
	use export_Trans_SVCD;
	use export_mp3;

# Make sure that we have mythtranscode installed
	$Prog{mythtranscode} = find_program('mythtranscode');
	die "You need mythtranscode to use this program.\n\n" unless ($Prog{mythtranscode});

# Make sure that we have ffmpeg installed
	$Prog{ffmpeg} = find_program('ffmpeg');
	die "You need ffmpeg to use this program.\n\n" unless ($Prog{ffmpeg});

# Make sure that we have nice installed
	$Prog{nice} = find_program('nice');
	die "You need ffmpeg to use this program.\n\n" unless ($Prog{nice});

# Load the commandline options
	%Args = ('debug' => \$DEBUG);
	GetOptions(\%Args, 'help', 'debug',
					   'gui|ui:s',
					   'function|export:s');

# Print the help - for now, this is just perldoc
	if ($Args{help}) {
		exec("perldoc $0");
	}

	if ($Args{function}) {
		print "function: $Args{function}\n\n";
		exit;
	}

	if ($Args{ui}) {
		print "ui: $Args{ui}\n\n";
		exit;
	}

# Find out which encoders are available to use
	push @Functions, export_SVCD->new,
					 export_NUV_SQL->new,
					 export_DivX->new,
					 export_WMV->new,
					 export_VCD->new,
                                         export_Trans_XviD->new,
                                         export_Trans_VCD->new,
                                         export_Trans_SVCD->new,
                                         export_mp3->new;

# Set up the signal handlers
	$SIG{INT}  = \&Quit;
	$SIG{QUIT} = \&Quit;

# Read the mysql.txt file in use by MythTV.
# could be in a couple places, so try the usual suspects
	open(CONF, "/usr/share/mythtv/mysql.txt")
		or open(CONF, "/usr/local/share/mythtv/mysql.txt")
		or die ("Unable to open /usr/share/mythtv/mysql.txt:  $!\n\n");
	while (my $line = <CONF>) {
		chomp($line);
		$line =~ s/^str //;
		my ($var, $val) = split(/\=/, $line, 2);
		next unless ($var && $var =~ /\w/);
		if ($var eq 'DBHostName') {
			$db_host = $val;
		}
		elsif ($var eq 'DBUserName') {
			$db_user = $val;
		}
		elsif ($var eq 'DBName') {
			$db_name = $val;
		}
		elsif ($var eq 'DBPassword') {
			$db_pass = $val;
		}
	}
	close CONF;

# Connect to the database
	$dbh = DBI->connect("dbi:mysql:database=$db_name:host=$db_host", $db_user, $db_pass)
		or die "Cannot connect to database: $!\n\n";

# Get the hostname of this machine
	$hostname = `hostname`;
	chomp($hostname);

# Find the directory where the recordings are located, and grab all of the filenames
	my $q = "SELECT data FROM settings WHERE value='RecordFilePrefix' AND hostname=?";
	my $sh = $dbh->prepare($q);
		$sh->execute($hostname) or die "Could not execute ($q):  $!\n\n";
	($video_dir) = $sh->fetchrow_array;
	die "This host not configured for myth.\n\n" unless ($video_dir);
	die "Recordings directory $video_dir doesn't exist!\n\n" unless (-d $video_dir);
	opendir(DIR, $video_dir) or die "Can't open $video_dir:  $!\n\n";
	my @Files = grep /\.nuv$/, readdir(DIR);
	closedir DIR;
	die "No recordings found!\n\n" unless (@Files);

# Parse out the record data for each file
	$q = "SELECT title, subtitle, description, hostname, cutlist FROM recorded WHERE chanid=? AND starttime=? AND endtime=?";
	$sh = $dbh->prepare($q);
	foreach $file (@Files) {
		next unless ($file =~ /\.nuv$/);
	# Pull out the various parts that make up the filename
		($channel,
			$syear, $smonth, $sday, $shour, $sminute, $ssecond,
			$eyear, $emonth, $eday, $ehour, $eminute, $esecond) = $file =~/^([a-z0-9]+)_(....)(..)(..)(..)(..)(..)_(....)(..)(..)(..)(..)(..)\.nuv$/i;
	# Found a bad filename?
		unless ($channel) {
			print "Unknown filename format:  $file\n";
			next;
		}
	# Execute the query
		$sh->execute($channel, "$syear$smonth$sday$shour$sminute$ssecond", "$eyear$emonth$eday$ehour$eminute$esecond")
			or die "Could not execute ($q):  $!\n\n";
		my ($show, $episode, $description, $show_hostname, $cutlist) = $sh->fetchrow_array;
	# Unknown file - someday we should report this
		next unless ($show);
	#$description =~ s/(?:''|``)/"/sg;
		push @{$Shows{$show}}, {'filename'       => $file,
								'channel'        => $channel,
								'start_time'     => "$syear$smonth$sday$shour$sminute$ssecond",
								'end_time'       => "$eyear$emonth$eday$ehour$eminute$esecond",
								'start_time_sep' => "$syear-$smonth-$sday-$shour-$sminute-$ssecond",
								'show_name'      => $show,
								'title'          => ($episode or 'Untitled'),
								'description'    => ($description or 'No Description'),
								'hostname'       => $show_hostname,
								'cutlist'        => $cutlist,
								'showtime'       => generate_showtime($syear, $smonth, $sday, $shour, $sminute, $ssecond)};
	}
	$sh->finish();


# We now have a hash of show names, containing an array of episodes
# We should probably do some sorting by timestamp (and also count how many shows there are)
	$num_shows = 0;
	foreach $show (sort keys %Shows) {
		@{$Shows{$show}} = sort {$a->{start_time} <=> $b->{start_time} || $a->{channel} <=> $b->{channel}} @{$Shows{$show}};
		$num_shows++;
	}

# No shows found?
	die 'Found '. at Files." files, but no matching database entries.\n\n" unless ($num_shows);

# Load the chosen gui
	$gui = gui_text->new;
	$gui->main_loop;

# Exit gracefully, in case we might accidentally execute some code below
	Quit();





More information about the mythtv-users mailing list