|
|
Line 148: |
Line 148: |
| === myth2ipod === | | === myth2ipod === |
| | | |
− | myth2ipod script. modified from [http://www.myth2ipod.com/myth2ipod.html myth2ipod]. I use ffmpeg directly, instead of nuvexport to transcode files. i haven't enabled removing commercials. transcoding takes at least as long as the recording is. i've added functionality for ffmpeg to update progress in mythconverge table. (ffmpeg takes the longest by far)
| + | I was getting a floating point exception from ffmpeg. the [[Nuvexport#Bug_Fix_-_Please_Read]] details the fix i used. |
− | <pre>
| |
− | #!/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;
| |
− | use Expect;
| |
− |
| |
− | # User variables
| |
− | my $portable = "ipod";
| |
− | my $feedfile = "/var/lib/mythipod/feed.php";
| |
− | my $feedpath = "/var/lib/mythipod/";
| |
− | my $wwwloc = "/var/www/";
| |
− | my $feedurl = "http://192.168.1.10/mythipod/";
| |
− | 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){
| |
− | system("clear");
| |
− | 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") {
| |
− | DoSetup();
| |
− | exit;
| |
− | }
| |
− | print "Setup exited. Nothing done.\n";
| |
− | exit;
| |
− | }
| |
− | elsif ($rebuild == 1){
| |
− | GenerateRSSFeed();
| |
− | print "Rebuilding of RSS feed is complete.\n";
| |
− | exit;
| |
− | }
| |
− | else {
| |
− | Encode4Portable();
| |
− | 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";
| |
− | GenerateRSSFeed();
| |
− | print "All done.\n";
| |
− | }
| |
− | }
| |
− | | |
− | sub Encode4Portable{
| |
− | if ($#ARGV != 1) {
| |
− | print "Encoding requires options.\nusage: myth2ipod <options> DIRECTORY FILE\n";
| |
− | exit;
| |
− | }
| |
− |
| |
− | # 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";
| |
− | exit;
| |
− | }
| |
− |
| |
− | # Connect to the database
| |
− | PrepSQLRead();
| |
− | $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";
| |
− |
| |
− | $statement->execute()
| |
− | 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");
| |
− | EncodeIt();
| |
− | printf("Nuvexport completed, starting xml generation...\n");
| |
− | CreateItemXML();
| |
− | 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/bin/nuvexport --chanid=$chanid --start=$start $nuvoptions --filename=$chanid\_$start.temp --path=$feedpath";
| |
− | $cmd = "ffmpeg -vcodec xvid -b 300 -qmin 3 -qmax 5 -bufsize 4096 -g 300 -acodec aac -ab 96 -i /$directory/$file -s 320x240 -aspect 4:3 $feedpath$chanid\_$start.temp.mp4";
| |
− | print $cmd."\n";
| |
− | ffmpegExpect();
| |
− |
| |
− | $command->soft_close();
| |
− |
| |
− | # Now clean up the output so iPods with firmware 1.1 and above can use it
| |
− | $cmd = "/usr/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;
| |
− | }
| |
− | | |
− | #
| |
− | # Use Expect to update database on transcoding progress
| |
− | #
| |
− | sub ffmpegExpect {
| |
− | $command = Expect->spawn($cmd);
| |
− | $update = 1;
| |
− | ($hrs, $min, $sec, $msec, $seconds,$frames) = (0,0,0,0,0,1);
| |
− |
| |
− | if ($command->expect(2, '-re', "Duration: [0-9]{2,2}:[0-9]{2,2}:[0-9]{2,2}.[0-9],")) {
| |
− | $command->exp_match() =~ /([0-9]{2,2}):([0-9]{2,2}):([0-9]{2,2}).([0-9])/;
| |
− | ($hrs, $min, $sec, $msec) = ($1, $2, $3, $4);
| |
− | $seconds = $msec/10 + $sec + 60 * ( $min + 60 * ($hrs) );
| |
− | } else { $update = 0; }
| |
− |
| |
− | if ($command->expect(2, '-re', ", [0-9]{2,2}.[0-9]{2,2} fps")) {
| |
− | $command->exp_match() =~ /, ([0-9\.]{5,5}) /;
| |
− | $frames = $seconds * $1;
| |
− | } else { $update = 0; }
| |
− |
| |
− | # `jobqueue`.`chanid`='%s' AND `jobqueue`.`starttime`='%s'
| |
− | if ($update) {
| |
− | while ($command->expect(undef, '-re', "frame= *[0-9]+ q")) {
| |
− | $command->exp_match() =~ /frame= *([0-9]+) q/;
| |
− | $completion = ($1) / ($frames) * 100;
| |
− | $sql = sprintf("UPDATE `jobqueue` SET `comment`='%.3f%% Completed (ffMPEG Transcoding)', `statustime`=NOW() WHERE `jobqueue`.`chanid`='%s' AND `jobqueue`.`starttime`='%s' LIMIT 1 ;",$completion,$chanid,$start);
| |
− | $statement = $db_handle->prepare($sql) or die "Couldn't prepare query '$sql': $DBI::errstr\n";
| |
− | $statement->execute() or die "Couldn't execute query '$sql': $DBI::errstr\n";
| |
− | }
| |
− | }
| |
− | }
| |
− | | |
− | #
| |
− | # 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";
| |
− | close(ITEM);
| |
− | 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 "<? foreach (glob(\$title\.\"*\.$portable\.xml\") as \$file) {include \$file;} ?>\n";
| |
− | print RSS "</channel>\n";
| |
− | print RSS "</rss>\n";
| |
− | close(RSS);
| |
− | 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/&/&/g;
| |
− | $result =~ s/</</g;
| |
− | $result =~ s/>/>/g;
| |
− | $result;
| |
− | }
| |
− | | |
− | | |
− | #
| |
− | # This code taken from one of the mythlink.sh scripts to get MySQL information
| |
− | #
| |
− | sub PrepSQLRead{
| |
− | # Get the hostname of this machine
| |
− | $hostname = `hostname`;
| |
− | chomp($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',
| |
− | '/usr/share/mythtv/mysql.txt',
| |
− | '/etc/mythtv/mysql.txt',
| |
− | '/usr/local/etc/mythtv/mysql.txt',
| |
− | "$ENV{HOME}/.mythtv/mysql.txt",
| |
− | '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 //;
| |
− | chomp($line);
| |
− | # 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)
| |
− | chomp;
| |
− | 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;
| |
− | }
| |
− | </pre>
| |
MythTV is awesome for my roommates and I. We love the commercial skip! While I always hoped to experiment with MythTV, my roommates endorsed the idea in November 2006. My experiment has become quite a practical establishment in our house.
Combined Frontend/Backend.
I tried quite a few distros before figuring out how to upgrade Debian to Etch. Here's what I can remember that conflicted with my setup, mainly the EPIA motherboard. See the Operating system page for more distros and information.
I think I tried some more, but ran into some other probelms.
The On-Screen Electronic Program Guide stopped working for me at some point. The focus seemed to be screwed up. To get Myth working again, I had two options: restart or telnet. the telnet option required a
I haven't spent much time diagnosing the problem though. when watching LiveTV I use comcast's digital cable EPG. I use MythWeb to schedule my recordings. Any suggestions are certainly welcome, though!
I used many resources to help me with the installation. I pieced the story together myself, without following one set of directions and having it work. here's what I used, in no particular order.
The PVR-350 is awesome for this puny system. Before I had tested the hardware mpeg-2 decoder, I tried playing a recorded mpeg-2. the video was very, very, very choppy and slow. I was a little worried that I may have spent money on a useless machine. The reviews on the hardware decoder lived up to everything the should have, though. I am able to record a show, commercial flag two shows, and watch a recording without noticing anything.
Switching to IceWM was a big help on cutting down login time.
MythWeb is pretty darn slow. Searching and TV listings especially.
I don't have any updates/modifications planned.
startmythfrontend script to restart MythTV via irexec.
the .lircrc file (buttons as per the DVICO remote, which I think are now standard in the lirc distribution)