IPod Export Solution: Myth2iPod

From MythTV Official Wiki
Revision as of 14:37, 26 April 2011 by Dbadia (talk | contribs) (encodeForXML was not being display properly)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The iPod Export Solution: myth2ipod article is an attempt to re-document the popular "myth2ipod" script for exporting a recording from MythTV to a video iPod.

Introduction, about this wiki article

This article is intended to re-create and bring somewhat more up to date the information that used to be found on http://myth2ipod.com.

A large reason for the writing of this article is the fact that recently Myth2iPod (one of the most widely used methods of exporting a MythTV Recording to iPod format) and its website recently disappeared from the Internet (the site is now inexplicably an online pharmacy). In trying to setup my system and get this working again, I found I was missing most of the documentation. With many thanks to the MythTV mailing list (and to James Armstrong specifically for the legwork on finding and downloading the archived site) I felt this article a proper place to re-document Myth2iPod, as well as the other solutions and tools I came across in the process.

Most of the information here is contributed by the community at large, where possible I will try to attribute the various major parts to their original authors.

Comments are welcome.

(Most of the information here was originally posted at myth2ipod.com, but it has been modified to be more "up to date" for today's distributions)

Author and System Information

Author Information: (from the myth2ipod script)

 Author: Chris aka Wififun - email: wififun@myth2ipod.com 
 Contributions and testing by Paul Egli
 modified to use nuvexport by starv at juniks dot org

System Information: Generic (Should work on all systems that will support nuvexport and ffmpeg with xvid and faac support.)

Setup Instructions (Edited for Correctness)

Current version: 1.0b2 updated 3/5/06
Script: myth2ipod.txt
Nuvexport plugin: iPod.pm

Author's Note: Note that when these instructions were originally written in '06 it was likely that most systems were not shipping with ffmpeg supporting the proper codecs. These days it is almost impossible to find an ffmpeg that does NOT ship with the codecs needed. Too, there is a HIGH likelihood that the needed packages are available from one of the many external package sites available for the linux flavor of choice. The instructions below are included for completeness but it is unlikely they will need to be followed. It is recommended that users check for the needed packages already installed prior to executing any of the instructions below. If the packages are not currently installed it is then recommended to do a quick search for a package from an external package source that will fulfill the need before attempting to download/compile these packages from scratch. The author has had success with compiling these packages in '07 but has not done so since.

The needed packages are:

  • ffmpeg with xvid and libfaac support.
  • MP4Box (from the gpac package)
  • Nuvexport (be careful of version here, see the note on nuvexport later in this section.)

It is recommended to check for these packages before following the instructions below. If these are all installed and ready, skip to step 4 below.

Author's Note: I have removed the original "Knoppmyth Setup" instructions and the generic shell script that supposedly automatically set all of this up. It was tested to work on R5Bxx and MythTV .19, it is unlikely, 2 years and going later, that this would work for anyone. If anyone knows differently please let me know.


1) First things first. Check the current ffmpeg version to see if it already has xvid, and faac support.

ffmpeg --version

If --enable-xvid and --enable-libfaac (you can also look for --enable-faac but this seems to be very old information) is on the configuration line, then all should be well. Skip to step 3 and check for MP4Box on the system.

2) If the ffmpeg version does not contain xvid or libfacc support and a package that does contain these formats is not available for the system (note for both RedHat based (Fedora, RHEL, CentOS, etc.) and Ubuntu based (Ubuntu, MythBuntu, etc.) there are packages available for installation), then obtain the source for ffmpeg and build this support. Ffmpeg can be obtained from The FFmpeg Downloads Website. Once downloaded, unpack and build the software.

./configure --build i686-linux-gnu --enable-gpl --enable-pp --enable-zlib \
       --enable-vorbis --enable-libogg --enable-theora --enable-a52 --enable-dts --enable-libgsm --disable-debug --enable-xvid  --enable-faac --prefix=/usr
make install

(check the settings for correctness for the system. See the documentation that comes with ffmpeg for appropriate flags to pass to configure and make sure to include faac and xvid support when compiling).

When done, check the ffmpeg version:

ffmpeg --version

Xvid and FAAC should be in the configuration line.

3) Now get MP4Box and compile it. This is used for post processing to allow the feed files to play with the updated iPod firmware. Again, note that generally this is available to all systems through a download source. Check for a gpac package for the distribution before compiling.

cd /tmp
wget http://internap.dl.sourceforge.net/sourceforge/gpac/gpac-0.4.0-rc2.tar.gz
wget http://internap.dl.sourceforge.net/sourceforge/gpac/gpac_extra_libs-0.4.0.tar.gz
tar -zxf gpac-0.4.0-rc2.tar.gz
tar -zxf gpac_extra_libs-0.4.0.tar.gz
cd gpac_extra_libs
cp -r * ../gpac/extra_lib
cd /tmp/gpac
chmod +x configure
make lib
make apps
make install

(Make sure to get the most recent version of gpac, don't necessarily use the 0.4.0-rc2 version linked above.)

Check to make sure MP4Box is working:


4. With all the prerequisites and dependencies out of the way, create the myth2ipod.sh script and obtain the iPod.pm library for nuvexport. See Appendix A in this document for the various scripts needed.

vi /usr/local/bin/myth2ipod (Add text from Appendix A and save)
chmod +x /usr/local/bin/myth2ipod
vi /usr/local/share/nuvexport/export/ffmpeg/iPod.pm (Add text from Appendix A and save)

5) At this point, edit the myth2ipod script in /usr/local/bin to change the user variables at the top of the myth2ipod script to change the feed path options, and the nuvexport settings. At the minimum you will need to modify the $feedurl hostname with your backend’s IP or hostname.

Specifically change the following section:

# User variables
my $portable = "ipod";
my $feedfile = "/myth/ipodfeed/feed.php";
my $feedpath = "/myth/ipodfeed/";
my $wwwloc = "/var/www/";
my $feedurl = "http://hostname/ipodfeed/";
my $nuvoptions ="--mode=iPod --nice=19 --cutlist --nodenoise --nodeinterlace --nomultipass";
  • feedfile is the path and script file name to where the feed should be located. This is the filename of the controlling script that manages the output feed for iTunes.
  • feedpath is the path where the final output files (exported videos) will reside and from where iTunes will pick up it's output feed. Make sure this path has enough space to store the converted files to be loaded to the iPod.
  • wwwloc is the directory to the root of your webserver (/var/www/, /var/www/html/, or /var/www/htdocs/ is a good choice depending upon your distribution).
  • feedurl is the HTTP address that maps to the directory identified in feedpath. If you have feed path set to /var/www/htdocs/ipodfeed and wwwloc is /var/www/htdocs then feedurl would be http://hostname/ipodfeed/
  • nuvoptions are the options passed to nuvexport. Note that by default, with the above options, this creates an h.234 (Quicktime) stream that I found to actually not work with my video iPod. I found that by adding --mp4_codec=mpeg4 to the output it created an mpeg4 stream that was perfectly compatible with my device (both video iPod and first gen iPhone).

5. Setup the directories for file storage and feed location. (These are based on the myth2ipod script user variables section: specifically $feedpath. Change the below examples if the default values above were modified.)

mkdir /myth/ipodfeed
chown mythtv.mythtv /myth/ipodfeed
ln -s /myth/ipodfeed /var/www/ipodfeed
/usr/local/bin/myth2ipod -rebuild
  • Create the $feedpath directory on the system.
  • Change the $feedpath directory owner and group to the mythtv user's owner and group. (Note this directory MUST be owned by the user who is running MythTV or conversely give write permissions with the chmod command.)
  • Create a symbolic link in the $wwwloc directory to the $feedpath directory. This allows the web server to see the feed directory. There is an alternative to this if using apache that will be listed in the next section.
  • Create the actual feed. Note that this needs to be done any time the script is upgraded.

If the user only wishes to use the conversion utility from the command line, the setup is now complete. It would be a good idea to look at the "usage" section to see how the script is used and run a test conversion. This script DOES NOT automatically delete the prior recording or modify myth's database in any way, so it is "safe" to use. If there are any problems with conversion it is likely at least a useless .xml file will be in the $feedpath directory if not a bad .mp4 file as well. It is safe to rm -rf the ENTIRE contents of the $feedpath directory (DO NOT delete the $feedpath directory itself, ONLY delete the CONTENTS of the $feedpath directory), including the feed.php file and then just run "myth2ipod -rebuild" to recreate the feed.

If however the user wishes to use this as a job within MythTV please continue with step 6.

6. Lastly, create a user job to allow MythTV to actually use the script. Access mythtv-setup and add the script as a User Job in MythTV. Under General, setup one of your User Jobs to run:

/usr/local/bin/myth2ipod "%DIR%" "%FILE%"

or to also use commercial flagging cutlist

/usr/local/bin/myth2ipod -cut "%DIR%" "%FILE%"

Give it a name like “Encode for iPod”, and be sure to also enable which ever User Job number you input this into (a screen or two BEFORE the screen where you can edit jobs). Now, when setting up a recording you can add this user job to make a copy of the recording available for your iPod. You can also convert an individual file to the feed by hitting the info button on a recording (go to the recordings screen, highlight a recording and hit the "i" Key (default) to bring up the information window about that recording) in the Watch Recordings screen, then selecting Job Options.

7. To subscribe to the feed from iTunes, select "Subscribe to Podcast..." from the Advanced menu. Enter a url like this one to subscribe to all shows you have added to the feed.


Where this is $feedurl + the filename portion at the end of $feedfile (do not include the path).


The following re-creates the feed after an upgrade or a delete of the directory:

myth2ipod -rebuild

The following converts a MythTV show into a format viewable on a video iPod, iPod Touch or iPhone.

myth2ipod <video directory> <video file>

e.g. myth2ipod /video 1111_20080101110000.mpg - Would convert the show at channel ID 1111 on date 01/01/2008 at 11:00 am where the file resides in the /video directory. The file will be converted and placed in $feedpath and the RSS feed updated so that it shows up on a user's iTunes installation.

Setup Tip: Add Apache Alias instead of Symbolic Link.

As submitted to the mythtv-users list by James Howison back in 2005:

chmod +x feed.php 

Then to make the file accessible to the web server add something like this to /etc/apache/httpd.conf (could be /etc/apache2 or /etc/httpd depending upon your system).

Alias /ipodfeed/ /myth/ipod/
   <Directory /myth/ipod>
        AllowOverride None
        Order allow,deny
        Allow from all

Where /ipodfeed/ is the directory portion of the URL in $feedurl (not the hostname), and /myth/ipod/ is the path specified in $feedpath.

Author's Note: There is no real reason to make the php script executable. PHP Files are not actually "executed" by the web server, they are interpreted very much like the HTML code itself is.

Managing The iPod Feed Directory: m2iweb.php

One of the largest problems I found with myth2ipod is the fact that there is no method to manage the feed. Unless you do manual management (i.e. Use OS specific tools) there is really no method to handle the feed. This can cause space issues (a 3g recording will typically compress in mp4 format to about 100m, but that's still 100m of DUPLICATE data laying around) on both the Myth box (or NAS) AS WELL AS the users LOCAL PC (when users use iTunes to push these files to their iPod the first thing iTunes does is DOWNLOAD the feed to the local directory). Again, without going into the filesystem and deleting files and rebuilding the feed there was simply no way to manage these feeds.

I recently ran across a neat little utility called m2iweb that integrates (albeit not so nicely) into MythWeb and provides a web based administration method for managing the files in the Podcast.

The original m2iweb was created by the user "alusby" on the Knoppmyth boards (mysettopbox.tv) and posted originally at m2iweb.sumimasen.org which again has since been taken offline. The original m2iweb thread can be found at: http://mysettopbox.tv/phpBB2/viewtopic.php?t=9676&highlight=m2iweb for full information.

There were several changes made and "Antebios" from the same boards has made the final set of changes and the information is available in the knoppmythwiki at: http://knoppmythwiki.org/index.php?page=VideoIpod. Note that in the knoppmythwiki article it is still referring to the myth2ipod website, all of that information can be found above. The files for m2iweb themselves are downloadable from http://www.macrossplusinc.com/files/mythtv/m2iweb.zip

Installing m2iweb

This is actually fairly basic:

  • Download the zip from above or copy the script out of Appendix A below into a file named "m2iweb.php" in the $feedpath directory.
  • Ensure that the $feedpath permissions allow the m2iweb.php script (generally run as the WEBSERVER USER) to delete files in the directory. The recommendation at this point is to just change the permissions to 777 (open to all) but this might not be the best idea on an open system. Instead add the webserver user to the mythtv group (assuming your mythtv installation runs with the mythtv group) and set permissions to 775 (read/write/execute to all but other) which should serve the same purpose without allowing "bad stuff" to happen.

(now for the nasty part)

Unfortunately, to be able to use m2iweb.php from within mythweb the mythweb header will actually need to be edited to include a link to the m2iweb.php script. This, of course, means that any time MythWeb is upgraded it has the potential to overwrite the changes and loose the link to m2iweb.php till the code here is re-added to the new version. I have no better solution to this at the moment, if anyone does, please feel free to write it up here or post it in the discussion area.

Edit the mythtv default header by:

  • edit /usr/share/mythtv/mythweb/themes/default/header.php (old) or /usr/share/mythtv/mythweb/modules/_shared/tmpl/default/header.php (current) and find Recorded Programs and add in after the </a>:
<a href="/ipodfeed/m2iweb.php">myth2ipod</a>

Where the href points to the $feedpath/m2iweb.php script. The next time MythWeb is loaded it will show a link entitled "myth2ipod" and clicking that link should lead to a page showing all of the converted recordings in the feed with a delete link at the end to remove them from the feed.

Notes on NuvExport

The most general note on nuvexport I can put here is that the version of nuvexport MUST match the version of MythTV for any of this to work. As of somewhere in the .21 era nuvexport went from 0.4.0 to 0.5.0. If nuvexport is being manually built on the system (instead of downloading a package for the distribution from an external packaging source) make sure to verify that this script is actually working.

If errors about cannot connect to database or cannot find show are displayed, it is possible that the correct version of nuvexport is not being used. Either update the system through the system's package update method (yum, apt-get, etc.) if nuvexport is installed as a package or download a new source distribution and re-compile. See the [[[Nuvexport]]] article for help on obtaining, compiling and installing nuvexport.


See Myth2ipod Troubleshooting for troubleshooting information.

New ffmpeg compile and configure (version for FFmpeg version SVN-r19393)
== 10 July 2009 (by hwkit) ==
updated information for using compiling new ffmpeg
1. to get a new ffmpeg, you should type
   svn checkout svn://svn.ffmpeg.org/ffmpeg/trunk ffmpeg
2. since some parameters setting is difference between the old version, new  configure is 
   ./configure --enable-x11grab --enable-gpl --enable-nonfree \
   --enable-postproc --enable-zlib --enable-pthreads --enable-libvorbis\
   --enable-libx264 --enable-libxvid --enable-muxer=ogg \
   --enable-libtheora --enable-encoder=libxvid --enable-libgsm \
   --disable-debug --enable-libfaac --prefix=/usr
3. modify /usr/share/nuvexport/export/ffmpeg/MP4.pm
  line 223:  ." -title $safe_title";                            ==> ;  ### ." -title $safe_title"
  line 227:  ." -loop 1'                                        ==>    ### ." -loop 1'
  line 230:  ." -rc_eq \'blurCplx^(1-qComp)\'"                  ==>    ###   ." -rc_eq \'blurCplx^(1-qComp)\'"
  line 237:  .' -me umh'                                        ==>    ### .' -me umh'   
  line 241:  $ffmpeg_xtra .= ' -flags +mv4+trell+loop'          ==> $ffmpeg_xtra .= ' -flags +mv4'      ## +trell+loop'
  line 242:  .' -aic 1'                                         ==>    ### .' -aic 1' 
  line 294:  .' -flags2 +bpyramid+wpred+mixed_refs+8x8dct+brdo' ==> .' -flags2 +bpyramid+wpred+mixed_refs+8x8dct' ##+brdo'
  line 297:  .' -chroma 1'                                      ==> .' -chromaoffset 1'
  line 298:  .' -slice 2'                                       ==> ### .' -slice2'
  line 299:  .' -cmp 1'                                         ==> ### .' -cmp1'
== end of updated information ==

== 25 Jan 2010 (by hwkit) ==
updated information for debugging Unknown aspect ratio: 1.8182 (Due to Hong Kong J2 Channel problem)
please add some script on /usr/share/nuvexport/export/generic.pm to prevent error occurs
line 275:
    if ("$episodes[0]->{'finfo'}{'aspect_f'}" eq "1.8182:1") {
            $episodes[0]->{'finfo'}{'aspect_f'} = 2;         ### you may set to 1.8 or 1.9
== end of updated information ==
== 01 Feb 2010 (by hwkit) ==
Solve problem error "TERM was not set at /usr/share/nuvexport/nuv_export/shared_utils.pm" in mythbackup.log
modify /usr/share/nuvexport/nuv_export/shared_utils.pm
add line  
     my $TERM="vt100"
== end of updated information ==

Appendix A: Available Scripts


#!/usr/bin/perl -w
# VERSION: 1.0b2 - myth2ipod
# Get the latest version, and change log at myth2ipod.com
# Author: Chris aka Wififun - email: wififun@myth2ipod.com 
# Contributions and testing by Paul Egli
# modified to use nuvexport by starv at juniks dot org

# Includes
	use DBI;
	use Getopt::Long;
	use File::Path;
# User variables
	my $portable = "ipod";
	my $feedfile = "/myth/ipodfeed/feed.php";
	my $feedpath = "/myth/ipodfeed/";
	my $wwwloc = "/var/www/";
	my $feedurl = "http://hostname/ipodfeed/";
	my $nuvoptions ="--mode=iPod --nice=19 --cutlist --nodenoise --nodeinterlace --nomultipass";
# Some variables
	our ($dest, $format, $usage);
	our ($db_host, $db_user, $db_name, $db_pass, $video_dir);
	our ($hostname, $db_handle, $sql, $statement, $row_ref);
    our ($chanid, $start, $nuvfile, @nuvarray);	
	my $rebuild = '0';
	my $encode = '0';
	my $debug = '0';
	my $setup = '0';
	my $cut = '0';
	my( $rightnow ) = `date`; 

GetOptions ("rebuild" => \$rebuild,
			"encode"   => \$encode,
			"debug"	=>	\$debug,
			"setup" => \$setup,
			"cut" => \$cut);

if ($setup == 1){
		print "Setup will do everything needed to run this script.\n";
		print "This has only been tested on KnoppMyth R5A22.\n";
		print "make sure you have edited the variables for your conguration.\n";
		my $cksetup = &promptUser("\nAre you sure you want to procceed?","n");
			if ($cksetup =~ "y") {
			print "Setup exited. Nothing done.\n";
elsif ($rebuild == 1){
		print "Rebuilding of RSS feed is complete.\n";
else {
		print "$title is ready for your $portable\n";
		# Check to see if the feed file exists; if not, create it.
		if (! -e $feedfile) { 
		print "No feed file found. I will make one for you.\n";
		print "All done.\n";

sub Encode4Portable{
	if ($#ARGV != 1) { 
		print "Encoding requires options.\nusage: myth2ipod <options> DIRECTORY FILE\n"; 
	# Get the show information
	$directory = $ARGV[0]; 
	$file = $ARGV[1]; 
	@file = split(/_/, $file);
		$chanid = $file[0];
		$start = substr $file[1],0,14;
		if($debug == 1){ print "$chanid\n$start\n"};
	if (! -e $directory."/".$file){
		print "Opps, the file ".$directory.$file." does not exist.\n";
	# Connect to the database
	$db_handle = DBI->connect("dbi:mysql:database=$db_name:host=$db_host", $db_user, $db_pass)
	or die "Cannot connect to database: $!\n\n";
		$sql = "SELECT title, subtitle, description, category, starttime FROM recorded WHERE chanid = $chanid AND DATE_FORMAT(starttime,'%Y%m%d%H%i%s') = $start";

		$statement = $db_handle->prepare($sql)
			or die "Couldn't prepare query '$sql': $DBI::errstr\n";
		or die "Couldn't execute query '$sql': $DBI::errstr\n";
		$row_ref = $statement->fetchrow_hashref();
		if($debug == 1){ print "$row_ref->{starttime}\n"};
	$title = $row_ref->{title}; 
	$subtitle = $row_ref->{subtitle};
	$recorddate = $row_ref->{starttime}; 
	$description = $row_ref->{description};
	$category = $row_ref->{category};
	$filename = $title."-".$subtitle."-".substr $start, 0, 8;
	$filename =~ s/ /_/g;
	$filename =~ s/&/+/g;
	$filename =~ s/\047//g;
    $filename =~ s/[^+0-9a-zA-Z_-]+/_/g;
    $filename =~ s/_$//g;
	printf("Starting nuvexport...\n"); 
	printf("Nuvexport completed, starting xml generation...\n"); 
	printf("XML file created for \"$filename\" : Yipeee\n"); 
	printf("Cleaning up temporary files\n"); 
		$cmd = "rm -f $feedpath$chanid\_$start.temp.mp4";
		print $cmd."\n";
		if(system($cmd)) { print "Removing nuvexport temp file failed\n"; }
		# remove the cutlist incase we added it.
		if ($cut == 1){
		printf("Generating cutlist\n"); 
		$cmd = "/usr/bin/mythcommflag --chanid $chanid --starttime $start --clearcutlist";
		print $cmd."\n"; 
		if(system($cmd)) { print "It looks like I was not able to generate a cutlist.\n"; } 
	return 0;

# Encode for Portable
sub EncodeIt { 
	# Create cutlist from commercial flagging if -cut was passed to the script
	if ($cut == 1){
	printf("Generating cutlist\n"); 
	$cmd = "/usr/bin/mythcommflag --chanid $chanid --starttime $start --gencutlist";
	print $cmd."\n"; 
	if(system($cmd)) { print "It looks like I was not able to generate a cutlist.\n"; } 
	# Use nuvexport to do the work
	$cmd = "/usr/local/bin/nuvexport --chanid=$chanid --start=$start $nuvoptions --filename=$chanid\_$start.temp --path=$feedpath";
	print $cmd."\n"; 
	if(system($cmd)) { print "Nuvexport encoding seems to have failed\n"; } 
	# Now clean up the output so iPods with firmware 1.1 and above can use it
	$cmd = "/usr/local/bin/MP4Box -add $feedpath$chanid\_$start.temp.mp4 $feedpath$chanid\_$start.$portable.mp4";
	print $cmd."\n"; 
	if(system($cmd)) { print "MP4Box cleanup seems to have failed\n"; } 
	return 0;

# Create XML with <ITEM> tag for this video file
sub CreateItemXML {
		open(ITEM, ">$feedpath$chanid\_$start.$portable.xml");
			print ITEM "<item>\n";  
			print ITEM "<title>".&encodeForXML($title." - ".$subtitle)."</title>\n";
			print ITEM "<itunes:author>MythTV</itunes:author>\n";  
			print ITEM "<author>MythTV</author>\n";  
			print ITEM "<itunes:category text=\"TV Shows\"></itunes:category>\n";
			print ITEM "<comments>".&encodeForXML($file)."</comments>\n";  
			print ITEM "<description>".&encodeForXML($description)."</description>\n";  
			print ITEM "<pubDate>".$recorddate."</pubDate>\n";  
			print ITEM "<enclosure url=\"".&encodeForXML("$feedurl$chanid\_$start.$portable.mp4")."\" type=\"video/quicktime\" />\n";  
			print ITEM "<itunes:duration></itunes:duration>\n";  
			print ITEM "<itunes:keywords>".&encodeForXML($title." - ".$subtitle." - ".$category)."</itunes:keywords>\n";
			print ITEM "<category>".&encodeForXML($category)."</category>\n";			
			print ITEM "</item>\n";
			print "\"$filename\" has been added to the feed.\n";
	return 0;

# Generate the RSS feed by combining the ITEM XML Files
sub GenerateRSSFeed {

	open(RSS, ">$feedfile");
		print RSS "<?php\n"; 
		print RSS "header(\"Content-Type: text/xml\");\n"; 
		print RSS "echo \"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\"; ?>\n"; 
		print RSS "<rss xmlns:itunes=\"http://www.itunes.com/DTDs/Podcast-1.0.dtd\" version=\"2.0\">\n"; 
		print RSS "<channel>\n"; 
		print RSS "<title>MythTV - <? if (\$_GET['title'] == \"\") { \$title = \"*\"; echo \"Recorded Programs\"; }\n";
		print RSS "else { \$title = \$_GET['title']; echo str_replace(\"_\",\" \",\$_GET['title']); } ?> </title>\n";
		print RSS "<itunes:author>MythTV - myth2ipod</itunes:author>\n"; 
		print RSS "<link>".&encodeForXML($feedurl)."</link>\n";
		print RSS "<itunes:subtitle>Transcoded recording for your iPod Video.</itunes:subtitle>\n"; 
		print RSS "<itunes:summary>Myth TV Recorded Programs for the iPod v.1</itunes:summary>\n"; 
		print RSS "<description>Myth TV Recorded Programs for the iPod v.1</description>\n"; 
		print RSS "<itunes:owner>\n"; 
		print RSS "<itunes:name>MythTV</itunes:name>\n"; 
		print RSS "<itunes:email>mythtv\@localhost</itunes:email>\n"; 
		print RSS "</itunes:owner>\n"; 
		print RSS "<itunes:explicit>No</itunes:explicit>\n"; 
		print RSS "<language>en-us</language>\n"; 
		print RSS "<copyright>Copyright 2005.</copyright>\n"; 
		print RSS "<webMaster>mythtv\@localhost</webMaster>\n"; 
		print RSS "<itunes:image href=\"http://myth2ipod.com/mythipod_200.jpg\" />\n"; 
		print RSS "<itunes:category text=\"TV Shows\"></itunes:category>\n"; 
		print RSS "<category>TV Shows</category>\n";
		print RSS "<itunes:image href=\"http://myth2ipod.com/mythipod_200.jpg\"/>";
		print RSS "<image>"; 
		print RSS "<url>http://myth2ipod.com/mythipod_200.jpg</url>\n";
		print RSS "<title>MythTV 2 iPod</title>\n";
		print RSS "<link>".&encodeForXML($feedurl)."</link>\n";	
		print RSS "<width>200</width>\n";		
		print RSS "<height>200</height>\n";		
		print RSS "</image>\n";
		print RSS "<? \$feed_array = glob(\"*\.ipod\.xml\"); ?>\n";
		print RSS "<? usort(\$feed_array, create_function('\$a,\$b', 'return filemtime(\$b) - filemtime(\$a);')); ?>\n";
		print RSS "<? foreach (\$feed_array as \$file) {include \$file;} ?>\n";
		print RSS "</channel>\n"; 
		print RSS "</rss>\n";
	if($debug == 1){ print "I created a feed file, was I supposed to?\n"};

	return 0;

# substitute for XML entities
sub encodeForXML {
	local $result;
	$result = $_[0];
	$result =~ s/&/&amp;/g;
	$result =~ s/</&lt;/g;
	$result =~ s/>/&gt;/g;

# This code taken from one of the mythlink.sh scripts to get MySQL information
sub PrepSQLRead{
# Get the hostname of this machine
    $hostname = `hostname`;
# Read the mysql.txt file in use by MythTV.
# could be in a couple places, so try the usual suspects
    my $found = 0;
    my @mysql = ('/usr/local/share/mythtv/mysql.txt',
    foreach my $file (@mysql) {
        next unless (-e $file);
        $found = 1;
        open(CONF, $file) or die "Unable to open $file:  $!\n\n";
        while (my $line = <CONF>) {
        # Cleanup
            next if ($line =~ /^\s*#/);
            $line =~ s/^str //;
        # Split off the var=val pairs
            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;
        # Hostname override
            elsif ($var eq 'LocalHostName') {
                $hostname = $val;
        close CONF;
    die "Unable to locate mysql.txt:  $!\n\n" unless ($found && $db_host);
    return 0;

sub promptUser {
   local($promptString,$defaultValue) = @_;
   if ($defaultValue) {
      print $promptString, "[", $defaultValue, "]: ";
   } else {
      print $promptString, ": ";

   $| = 1;               # force a flush after our print
   $_ = <STDIN>;         # get the input from STDIN (presumably the keyboard)
   if ("$defaultValue") {
      return $_ ? $_ : $defaultValue;    # return $_ if it has a value
   } else {
      return $_;

sub DoSetup {
	print "\nNot ready yet. How do you send the cd command from perl?\n";
	return 0;


# $Date: 2006-01-18 00:15:31 -0800 (Wed, 18 Jan 2006) $
# $Revision: 226 $
# $Author: xris $
#  export::ffmpeg::iPod

package export::ffmpeg::iPod;
    use base 'export::ffmpeg';

# Load the myth and nuv utilities, and make sure we're connected to the database
    use nuv_export::shared_utils;
    use nuv_export::cli;
    use nuv_export::ui;
    use mythtv::db;
    use mythtv::recordings;

# Load the following extra parameters from the commandline
    add_arg('quantisation|q=i', 'Quantisation');
    add_arg('a_bitrate|a=i',    'Audio bitrate');
    add_arg('v_bitrate|v=i',    'Video bitrate');
    add_arg('multipass!',       'Enably two-pass encoding.');

    sub new {
        my $class = shift;
        my $self  = {
                     'cli'      => qr/\bipod\b/i,
                     'name'     => 'Export to iPod',
                     'enabled'  => 1,
                     'errors'   => [],
                     'defaults' => {},
        bless($self, $class);

    # Initialize the default parameters

    # Verify any commandline or config file options
        die "Audio bitrate must be > 0\n" unless (!defined $self->val('a_bitrate') || $self->{'a_bitrate'} > 0);
        die "Video bitrate must be > 0\n" unless (!defined $self->val('v_bitrate') || $self->{'v_bitrate'} > 0);

    # VBR, multipass, etc.
        if ($self->val('multipass')) {
            $self->{'vbr'} = 0;
        elsif ($self->val('quantisation')) {
            die "Quantisation must be a number between 1 and 31 (lower means better quality).\n" if ($self->{'quantisation'} < 1 || $self->{'quantisation'} > 31);
            $self->{'vbr'} = 1;

    # Initialize and check for ffmpeg

    # Can we even encode ipod?
    #    if (!$self->can_encode('mov')) {
    #        push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to mov file formats.";
    #    }
        if (!$self->can_encode('xvid')) {
            push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to xvid video.";
        if (!$self->can_encode('aac')) {
            push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to aac audio.";
    # Any errors?  disable this function
        $self->{'enabled'} = 0 if ($self->{'errors'} && @{$self->{'errors'}} > 0);
    # Return
        return $self;

# Load default settings
    sub load_defaults {
        my $self = shift;
    # Load the parent module's settings
    # Default bitrates
        $self->{'defaults'}{'v_bitrate'} = 384;
        $self->{'defaults'}{'a_bitrate'} = 64;

# Gather settings from the user
    sub gather_settings {
        my $self = shift;
    # Load the parent module's settings
    # Audio Bitrate
        $self->{'a_bitrate'} = query_text('Audio bitrate?',
    # VBR options
        if (!$is_cli) {
            $self->{'vbr'} = query_text('Variable bitrate video?',
            if ($self->{'vbr'}) {
                $self->{'multipass'} = query_text('Multi-pass (slower, but better quality)?',
                if (!$self->{'multipass'}) {
                    while (1) {
                        my $quantisation = query_text('VBR quality/quantisation (1-31)?',
                        if ($quantisation < 1) {
                            print "Too low; please choose a number between 1 and 31.\n";
                        elsif ($quantisation > 31) {
                            print "Too high; please choose a number between 1 and 31\n";
                        else {
                            $self->{'quantisation'} = $quantisation;
            } else {
                $self->{'multipass'} = 0;
        # Ask the user what video bitrate he/she wants
            $self->{'v_bitrate'} = query_text('Video bitrate?',

    sub export {
        my $self    = shift;
        my $episode = shift;
    # Force to 4:3 aspect ratio
        $self->{'out_aspect'}       = 1.3333;
        $self->{'aspect_stretched'} = 1;
    # PAL or NTSC?
        my $standard = ($episode->{'finfo'}{'fps'} =~ /^2(?:5|4\.9)/) ? 'PAL' : 'NTSC';
        $self->{'width'}   = 320;
        $self->{'height'}  = ($standard eq 'PAL') ? '288' : '240';
        $self->{'out_fps'} = ($standard eq 'PAL') ? 25    : 29.97;
    # Embed the title
        my $safe_title = shell_escape($episode->{'show_name'}.' - '.$episode->{'title'});
    # Dual pass?
        if ($self->{'multipass'}) {
        # Build the common ffmpeg string
            my $ffmpeg_xtra  = ' -b ' . $self->{'v_bitrate'}
                              .' -bufsize 65535'
                              .' -vcodec xvid -acodec aac '
                              .' -ab ' . $self->{'a_bitrate'}
                              ." -f mp4 -title $safe_title";
        # Add the temporary file to the list
            push @tmpfiles, "/tmp/xvid.$$.log";
        # Back up the path and use /dev/null for the first pass
            my $path_bak = $self->{'path'};
            $self->{'path'} = '/dev/null';
        # Build the ffmpeg string
            print "First pass...\n";
            $self->{'ffmpeg_xtra'}  = " -pass 1 -passlogfile '/tmp/divx.$$.log'"
            $self->SUPER::export($episode, '');
        # Restore the path
            $self->{'path'} = $path_bak;
        # Second Pass
            print "Final pass...\n";
            $self->{'ffmpeg_xtra'}  = " -pass 2 -passlogfile '/tmp/divx.$$.log'"
    # Single Pass
        else {
            $self->{'ffmpeg_xtra'}  = ' -b ' . $self->{'v_bitrate'}
                                       ? ' -qmin '.$self->{'quantisation'}
                                        .' -qmax 31 -minrate 32'
                                        .' -maxrate '.(2*$self->{'v_bitrate'})
                                        .' -bt 32'
                                       : '')
                                     .' -vcodec xvid -acodec aac '
                                     .' -ab ' . $self->{'a_bitrate'}
                                     ." -f mp4 -title $safe_title";
    # Execute the (final pass) encode
        $self->SUPER::export($episode, '.mp4');

1;  #return true

# vim:ts=4:sw=4:ai:et:si:sts=4


//release 0.1
// Modified by Antebios
//release 0.2

    <title>MythWeb - myth2ipod Recordings</title>

    <meta http-equiv="content-type" content="text/html; charset=utf-8" />

    <script type="text/javascript" src="/mythweb/js/init.js"></script>
    <script type="text/javascript" src="/mythweb/js/browser.js"></script>
    <script type="text/javascript" src="/mythweb/js/utils.js"></script>

    <script type="text/javascript" src="/mythweb/js/mouseovers.js"></script>
    <script type="text/javascript" src="/mythweb/js/visibility.js"></script>
    <script type="text/javascript" src="/mythweb/js/ajax.js"></script>

    <link rel="stylesheet" type="text/css" href="/mythweb/skins/default/style.css" />
    <link rel="stylesheet" type="text/css" href="/mythweb/skins/default/header.css" />
    <link rel="stylesheet" type="text/css" href="/mythweb/skins/default/menus.css" />
    <link rel="stylesheet" type="text/css" href="/mythweb/skins/default/programming.css" />

    <link rel="stylesheet" type="text/css" href="/mythweb/skins/default/status.css" />



<div id="page_header" class="clearfix">
    <div id="logo_box">
        <a id="mythtv_logo" href="/mythweb/">
        <img src="/mythweb/skins/default/img/mythtv-logo.png" 
width="174" height="48" border="0" alt="MythTV" class="alpha_png">

    <div id="sections">
        <a id="tv_link" href="/mythweb/tv" onmouseover="return help_text('TV functions, including recorded programs.')" onmouseout="return help_text()">
            <img src="/mythweb/skins/default/img/tv.png" width="48" height="48" class="alpha_png" alt="MythTV"/>
        <a id="video_link" href="/mythweb/video" onmouseover="return help_text('MythVideo on the web.')" onmouseout="return help_text()">
            <img src="/mythweb/skins/default/img/video.png" width="48" height="48" class="alpha_png" alt="MythVideo" />
        <a id="weather_link" href="/mythweb/weather" onmouseover="return help_text('MythWeb Weather.')" onmouseout="return help_text()">
            <img src="/mythweb/skins/default/img/weather.png" width="48" height="48" class="alpha_png" alt="MythWeather" />
        <a id="settings_link" href="/mythweb/settings" onmouseover="return help_text('Edit MythWeb and some MythTV settings.')" onmouseout="return help_text()">
            <img src="/mythweb/skins/default/img/settings.png" width="48" height="48" class="alpha_png" alt="Settings" />
    <div id="extra_header">
        <div id="help_wrapper">
            <div id="help_box">

                <div id="help_text_default">
                MythWeb: Mon May 16, 2006, 09:09 PM                </div>
                <div id="help_text">
        <div id="search" >
            <form action="/mythweb/tv/search" method="post">

                <div id="simple_search">
                    <input id="search_text" type="text" name="searchstr" size="15" value="">
                    <input id="search_submit" type="submit" class="submit" value="Search"> (<a href="/mythweb/tv/search">Advanced</a>)
                <div id="search_options">


<table width="100%" border="0" cellspacing="2" cellpadding="0">

    <td colspan="2" class="menu menu_border_t menu_border_b"><table class="body" width="100%" border="0" 

cellspacing="2" cellpadding="2">
            <td><div id="command_choices">
                    <a href="/mythweb/" id="category_legend" onmouseover="popup('category_legend'); return true;">MythTV:</a>    

                    <a href="/mythweb/tv/list">Listings</a>
                    <a href="/mythweb/tv/searches">Searches</a>
                    <a href="/mythweb/tv/schedules">Recording Schedules</a>
                    (<a href="/mythweb/tv/schedules/manual">Manual</a>,
                    <a href="/mythweb/tv/schedules/custom">Custom</a>)
                    <a href="/mythweb/tv/upcoming">Upcoming Recordings</a>
                    <a href="/mythweb/tv/recorded">Recorded Programs</a>
                                        <a href="/ipodfeed/m2iweb.php">Myth2iPod</a>
                                        <a href="/archive/archive.php">XviD</a>
                    <a href="/mythweb/status">Backend Status</a>


<table width="100%" border="0" cellpadding="4" cellspacing="2" class="list small">
<tr><td colspan="10" class="list_separator">View information and delete myth2ipod recordings.</td></tr>
$delete = $_REQUEST['delete'];
if ($delete AND substr($delete, -9) == ".ipod.xml") {
  echo '<tr><td colspan="10" class="list_separator">
        <font color="red">';
  echo $delete;
  unlink($delete) or print(" failed to be ");
  echo ' has been deleted, ';
  $deletetoo = substr($delete, 0, -3)."mp4";
  echo $deletetoo;
  unlink($deletetoo) or print(" failed to be ");
  echo ' has been deleted.';
  // echo '     <a href="?">Go back to Myth2iPod Home Page</a>';
//else {
  foreach (glob($title."*.ipod.xml") as $file) {
    $lines = file($file);
    echo '<tr class="recorded"><td class="list"> </td><td>';
    echo '<font color="';
    if (!file_exists(substr($file, 0, -3)."mp4")) {
      echo "red";
    echo '">'.$i.'</font>';
        // <td><img src=".'"/mythweb/data/cache/'.strip_tags($lines[5]).'.png"'."></td>
    echo "</td><td><img src=ipod.jpg></td><td><a href=".'"'.substr($file, 0, -3)."mp4".'"'."$showcolor>".strip_tags($lines[1])."</a></td><td>".strip_tags($lines[7])."</td><td>".strip_tags($lines[6])."</td><td nowrap>".filesizeparse(filesize(substr($file, 0, -3)."mp4"))."</td><td>".'<a href="?delete='.$file.'" onclick="return confirm('."'Are you sure you want to delete?'".')">Delete</a>'."</td>".'<td class="list"> </td>'."</tr>";

function filesizeparse($size){
  $iec = array(" B", " KB", " MB", " GB", " TB");
  while (($size/1024)>1) {
  return substr($size,0,strpos($size,'.')+3).$iec[$i];

Appendix B: A user's "Ubuntu Specific" instructions

Author's Note this is a much more up to date version of the instructions above but they are SPECIFICALLY tuned to Ubuntu (and it's derivatives like mythbuntu). Given that the instructions below highlight the fact that there is no need to compile these applications any more, as well as there are many users of Ubuntu systems I felt it was worth it to include these here.

Note that this is still out of date (MythTV v0.20 and Ubuntu 7.04 - Feisty), but generally the information still applies.

From http://lianza.org/blog/2007/06/27/myth2ipod-on-ubuntu/ (Captain's Log)

If you want to get myth2ipod working on Ubuntu (assuming you have both MythTV and Ubuntu installed) there aren’t any instructions to my knowledge. I found the currently posted set of instructions to be outdated (last update 3/5/06). The following, updated instructions are actually much simpler (everything is in apt).

I did as follows (Myth .20, Ubuntu 7.04):

1) Get the appropriate version of ffmpeg (with xvid support). To do that, add this line to your /etc/apt/sources.list file:

 deb http://packages.medibuntu.org/ feisty free non-free

and then run

 wget -q http://packages.medibuntu.org/medibuntu-key.gpg -O- | sudo apt-key add - && sudo apt-get update

Then install your brand new version of ffmpeg:

 sudo apt-get install ffmpeg

(found that tip linked from [1])

2) To test what you did in step 1, run ffmpeg -version and make sure you see ‘enable-xvid’ and ‘enable-faac’ in the output. (Author's Note: Again, it is not enable-faac it's enable-libfaac)

3) Install the codec library, which is required to make sure you can encode aac audio.

sudo apt-get install libavcodec0d

To test that you can, run ffmpeg -formats and make sure you see DEA aac listed under “Codecs”.

4) Install mp4box, which is available in apt under ‘gpac’

sudo apt-get install gpac

5) Grab nuvexport: (Author's Note: Nuvexport is now included in mythbuntu (and probably others) by default in the 8.x version, no need to build it it works right out of the box.)

     cd /tmp
     wget http://forevermore.net/files/nuvexport-latest.tar.bz2
     tar -xjvf nuvexport-latest.tar.bz2
     sudo mv nuvexport-0.4 /usr/local/share/nuvexport

6) Copy down the scripts and put them in the right places:

     cd /tmp
     wget http://myth2ipod.com/myth2ipod.txt (Author's Note: Get the file from Appendix A above.)
     wget http://myth2ipod.com/iPod.pm (Author's Note: Get the file from Appendix A above.)
     sudo mv myth2ipod.txt /usr/local/bin/myth2ipod
     sudo chmod +x /usr/local/bin/myth2ipod
     sudo mv iPod.pm /usr/local/share/nuvexport/export/ffmpeg/iPod.pm
     sudo ln -s /usr/local/share/nuvexport/nuvexport /usr/local/bin/nuvexport

7) At this point you can follow the rest of the instructions at the site (Above) for editing /usr/local/bin/myth2ipod , etc. The rest of it is not distribution-specific. The slight change I had to make was the path to MP4Box which is hard-coded in the script to point to /usr/local/bin but on Ubuntu it gets put into /usr/bin.