This is an old revision of the document!


mplayer-resume

mplayer-resume is a PHP shell script I wrote to resume playback of files through mplayer. It works using slave mode and LIRC with mplayer, which outputs the variables to stdout. The wrapper script then keeps these values, and uses them to jump to that last point.

Note that this code is an archived copy of the source code. It's unmaintained, but probably works.

mplayer-resume 2.0
#!/usr/bin/env php
<?
 
	// By default, save files in ~/.mplayer/playback/
	$home = getenv('HOME');
	$save_files_to = "$home/.mplayer/playback/";
 
	/**
	 *
	 * @author Steve Dibb <steve dot dibb at gmail dot com>
	 * @copyright public-domain
	 * @date 2009-08-11
	 * @version 2.0
	 * @homepage http://spaceparanoids.org/trac/bend/wiki/mplayer-resume
	 * 
	 * Run time dependencies:
	 * PHP >= 4.3.0 with CLI and PCRE - http://php.net/
	 * MPlayer - http://mplayerhq.hu/
	 *
	 * See README for documentation on usage
	 * Quick example: mplayer-resume --filename "movie.mkv"
	 *
	 */
 
	/**
	 * Sends a string to stdout with a terminating line break
	 *
	 * @param string
	 * @param boolean display "mplayer-resume:" before each string
	 * 
	 */
	function stdout($str, $leading_string = true) {
		if(is_string($str)) {
			$str = rtrim($str);
			if($leading_string)
				$str = "mplayer-resume: $str";
			fwrite(STDOUT, "$str\n");
			return true;
		} else
			return false;
	}
 
	/**
	 * Sends a string to stderr with a terminating line break
	 *
	 * @param string
	 * 
	 */
	function stderr($str) {
		if(is_string($str)) {
			$str = rtrim($str);
			fwrite(STDERR, "mplayer-resume: $str\n");
			return true;
		} else
			return false;
	}
 
	/**
	 * Parse CLI arguments
	 *
	 * If a value is unset, it will be set to true
	 *
	 * @param $argc argument count (system variable)
	 * @param $argv argument array (system variable)
	 * @return array
	 */
	function parseArguments($argc, $argv) {
 
		$arr = array();
 
		if($argc > 1) {
			// Drop the binary command
			array_shift($argv);
 
			for($x = 0; $x < count($argv); $x++) {
				if(preg_match('/^--\w+/', $argv[$x]) > 0) {
					$argv[$x] = preg_replace('/^--/', '', $argv[$x]);
					$arr[$argv[$x]] = true;
				} elseif(in_array($argv[($x-1)], array_keys($arr))) {
					$arr[$argv[($x-1)]] = $argv[$x];
				}
			}
 
			return $arr;
		}
		else
			return array();
	}
 
	// Get the mplayer-resume arguments (not mplayer's)
	$args = parseArguments($argc, $argv);
 
	if($args['help'] || !count($args) || is_bool($movie) || $argc === 1) {
		stdout("Usage:\tmplayer-resume --filename <filename> [options] [mplayer options]", false);
		stdout("", false);
		stdout("Basic options:", false);
		stdout("", false);
		stdout(" --filename <filename>\tFilename to play with MPlayer", false);
		stdout(" --help \t\tThis help output", false);
		stdout("", false);
		stdout("Extended options:", false);
		stdout("", false);
		stdout(" --use-dir-conf\t\tPass \"-include <dirname of filename>/mplayer.conf\" to", false);
		stdout("\t\t\tmplayer if config file exists in same directory of", false);
		stdout("\t\t\tmedia file.  Similar to -use-filedir-conf.", false);
		stdout("", false);
		stdout("Example: $ mplayer-resume --filename \"A Man For All Seasons.mkv\" -fs -ao alsa", false);
		stdout("", false);
		stdout("mplayer-resume works by capturing events sent to MPlayer's slave mode", false);
		stdout("when exiting.  You will need to setup your MPlayer configuration files", false);
		stdout("to send the correct events in slave mode before this script will work.", false);
		stdout("", false);
		stdout("See the webpage for documentation:", false);
		stdout("http://spaceparanoids.org/trac/bend/wiki/mplayer-resume", false);
		die;
	}
 
	// Movie filename (used internally)
 	$movie = $filename = $args['filename'];
 
 	// PHP doesn't parse ~ as $HOME, so fix it ourselves
 	if(substr($movie, 0, 1) === "~")
 		$movie = $home.substr($movie, 1);
 
 	// Filename (escaped, ready to send to mplayer)
 	$mplayer_filename = escapeshellarg($movie);
 
 	stdout($movie);
 	stdout($mplayer_filename);
 
	// Check to see the config directory exists.  If not, try and create it.
	if(!is_dir($save_files_to)) {
		if(!mkdir($save_files_to)) {
			stderr("Please make sure the directory $save_files_to exists and is both readable and writable by this user");
			exit(1);
		}
	}
 
	// Make sure I can save playback files
	if(!is_writable($save_files_to) || !is_readable($save_files_to)) {
		stderr("I can't read from and/or write to $save_files_to");
		exit(1);
	}
 
	// Correct *some* human error ;)
	if(substr($save_files_to, -1, 1) != '/')
		$save_files_to .= '/';
 
	// Drop the binary from list of arguments
 	array_shift($argv);
 
	// Put the arguments back together again
	$str_args = implode(' ', $argv);
 
	// Drop the filename command
	$str_args = str_replace('--filename', '', $str_args);
 
	// Keep the movie filename separate from args
  	$str_args = str_replace($filename, '', $str_args);
 
	// Remove arguments which will break the script
	// -really-quiet means no output to our slave commands
	$str_args = str_replace('-really-quiet', '', $str_args);
 
	// Where the seek position will be saved
	$basename = basename($movie);
	$txt = $save_files_to.$basename.".txt";
 
	// If there is already a playback file, read it and start
	// from that position.
	if(file_exists($txt)) {
		$ss = trim(file_get_contents($txt));
		// One more check for garbage
		if(is_numeric($ss) && $ss > 0)
			$flags = " -ss $ss ";
	}
 
	/** Custom MPlayer workarounds */
 
	$custom_flags = array();
	$str_custom_flags = "";
 
	// --use-dir-conf
	// This is similar to -use-filedir-conf in mplayer.  It checks
	// to see if an "mplayer.conf" file exists in the directory of the
	// media file, and includes that.
	if($args['use-dir-conf']) {
 
		$str_args = str_replace("--use-dir-conf", "", $str_args);
 
		$conf = dirname($movie)."/mplayer.conf";
		if(file_exists($conf))
			$custom_flags[] = "-include ".escapeshellarg("$conf");
	}
 
 	if(count($custom_flags))
 		$str_custom_flags = implode(" ", $custom_flags);
 
	// Build the execution string
	$exec = "mplayer $mplayer_filename -quiet ".escapeshellcmd("$flags $str_args")." $str_custom_flags";
	stdout($exec);
 
	// Execute the command, save output to an array, and grab
	// return code to see if mplayer throws an error.
	exec($exec, $arr, $return);
 
	// If mplayer dies with a positive exit code, then it failed.
	// Don't write to or delete the saved position, and die
	// with the same exit code.
	if($return !== 0) {
		stderr("mplayer died unexpectedly");
		exit($return);
	}
 
	// If the file didn't even exist, mplayer will die, and so will me
	if(!file_exists($movie)) {
		stderr("Couldn't find the filename $movie");
		exit(1);
	}
 
	// Grep out the details we need from output
	$key_position = current(preg_grep('/^ANS_TIME_POSITION\b/', $arr));
	$key_filename = current(preg_grep('/^ANS_FILENAME\b/', $arr));
 
	// Get the position (in seconds) of the movie
	// Original format is ANS_TIME_POSITION=123.45
	$endpos = preg_replace('/^ANS_TIME_POSITION\=/', '', $key_position);
 
	// Get the filename of the movie we were playing
	// Original format is ANS_FILENAME='Your_Movie.avi'
	$slave_filename = preg_replace(
		array('/^ANS_FILENAME\=\'/', '/\'$/'), 
		array('', ''),
		$key_filename);
 
	// If we are playing from a playlist, then the two filenames
	// will not match.  Change the filename to be what we were
	// actually playing versus what we started out playing.
	if($slave_filename != $movie && !empty($slave_filename)) {
		$movie =& $slave_filename;
		$txt = $save_files_to.basename($movie).".txt";
	}
 
	// If it's a negative value, that means you've seeked
	// past/to the end of the file, so just remove the old one.
	if($endpos < 1 && file_exists($txt))
		unlink($txt);
	// No end position passed to mplayer-resume, so exit quietly
	elseif(empty($endpos)) {
		stderr("No end position sent to save.");
		exit(0);
	}
	// Save the (positive) playback position to the file
	else {
		// Generic error message, fix your stupid permissions
		$error_msg = "Cannot save file, please check write permissions for this user in $save_files_to";
		fwrite(fopen($txt, 'w'), $endpos) or stderr($error_msg);
	}
?>

ChangeLog

2.0 (2009-08-11)
	
	- Add --filename argument to specify filename directly, instead
	  of trying to guess what it is.
	- Add optional argument --use-filedir-conf, to include mplayer.conf
	  in local directory, and pass -include mplayer.conf to mplayer.

1.6 (2008-12-16)

	- Check exit codes on mplayer, so that if mplayer quits
	  unexpectedly, mplayer-resume won't kill the resume point.
	- Send exit codes

1.5 (2008-01-12)
	
	- Allow spaces in filenames.  Use mplayer-resume "filename"
	- Get the filename from MPlayer on exit, and use that to save
	the file to.  Useful for playlists.  See README.

1.3 (2006-09-15)

	- Call env instead of php directly
	- Get $HOME environment variable to save files to

1.2

	- Documentation updates

Navigation