Mediastream Download Klasse
Media stream download class
This class is an easy to use wrapper around the commandline programm mimms
,
which must be installed on your (*nix) system. See the example how to use it.
Diese Klasse ist um das Kommandozeilenprogramm mimms
gestrickt, welches auf dem
(*nix) System installiert sein muss. Ein Blick in das nachfolgende Beispiel verrät
wie es funktioniert:
Sample source code
Anwendungsbeispiel
<?php
include_once('swlib/swlib.class.php');
use sw\MediaStreamLoader;
// Class configuration. This config is used for all instances.
MediaStreamLoader::config(array(
'mimms-bin' => '/usr/bin/mimms',
'default-download-directory' => '/tmp'
));
// Instantiate
$mimms = new MediaStreamLoader();
// Set a callback for the progress if you like ...
$mimms->setProgressCallback(function(array $progress){
//
// $progress['progress'] : The progress from 0 to 1 (not in percent)
// $progress['loaded'] : Bytes loaded
// $progress['total'] : Total bytes that have to be loaded
// $progress['remaining'] : Remaining time in seconds
//
print "Progress: {$progress['progress']} | " .
"Loaded {$progress['loaded']}/{$progress['total']} | " .
"Remaining: {$progress['remaining']}" .
"\n";
});
// The stream you like to fetch. It MUST start with mms://
$mimms->setStreamUrl('mms:// [...] .wmv');
// Dsbug: Say we're starting
print "[STARTING]\n";
// Start the download
$details = $mimms->download();
// We're finished
print "[FINISHED]\n\n";
// The variable $detaile contains everything you need to know about the success
// and result of the process ...
Output
Ausgabe
$ php mimms.test.php
[STARTING]
Progress: 0 | Loaded 244316/531816775 | Remaining: 22769
Progress: 0.001 | Loaded 463472/531816775 | Remaining: 12706
Progress: 0.001 | Loaded 683673/531816775 | Remaining: 9134
Progress: 0.002 | Loaded 903874/531816775 | Remaining: 7209
Progress: 0.002 | Loaded 1163919/531816775 | Remaining: 5929
Progress: 0.003 | Loaded 1352663/531816775 | Remaining: 5296
Progress: 0.003 | Loaded 1562378/531816775 | Remaining: 4802
Progress: 0.003 | Loaded 1793064/531816775 | Remaining: 4393
Progress: 0.004 | Loaded 2013265/531816775 | Remaining: 4088
Progress: 0.004 | Loaded 2243952/531816775 | Remaining: 3839
Progress: 0.005 | Loaded 2453667/531816775 | Remaining: 3635
Progress: 0.005 | Loaded 2673868/531816775 | Remaining: 3479
Progress: 0.005 | Loaded 2873098/531816775 | Remaining: 3364
Progress: 0.006 | Loaded 3103784/531816775 | Remaining: 3252
Progress: 0.006 | Loaded 3323985/531816775 | Remaining: 3152
Progress: 0.007 | Loaded 3544186/531816775 | Remaining: 3063
Progress: 0.007 | Loaded 3764387/531816775 | Remaining: 3008
Progress: 0.007 | Loaded 3984588/531816775 | Remaining: 2962
Progress: 0.008 | Loaded 4204789/531816775 | Remaining: 2905
Progress: 0.008 | Loaded 4435476/531816775 | Remaining: 2837
Progress: 0.009 | Loaded 4645191/531816775 | Remaining: 2815
Progress: 0.009 | Loaded 4865392/531816775 | Remaining: 2775
Progress: 0.01 | Loaded 5075107/531816775 | Remaining: 2764
Progress: 0.01 | Loaded 5295308/531816775 | Remaining: 2736
Progress: 0.01 | Loaded 5525995/531816775 | Remaining: 2711
Progress: 0.011 | Loaded 5756682/531816775 | Remaining: 2657
Progress: 0.011 | Loaded 5955911/531816775 | Remaining: 2660
Progress: 0.012 | Loaded 6165626/531816775 | Remaining: 2651
[...]
Progress: 1 | Loaded 531816775/531816775 | Remaining: 0
[FINISHED]
Class source code
Klassen-Quelltext
<?php
/**
* Exceptions for MIMMS media stream downloader class
*
* @gpackage de.atwillys.sw.php.swLib
* @author Stefan Wilhelm
* @copyright Stefan Wilhelm, 2007-2012
* @license GPL
* @version 1.0
*/
namespace sw;
class MediaStreamLoaderException extends LException {
}
<?php
/**
* MIMMS media stream downloader class
*
* @gpackage de.atwillys.sw.php.swLib
* @author Stefan Wilhelm
* @copyright Stefan Wilhelm, 2007-2012
* @license GPL
* @version 1.0
*/
namespace sw;
class MediaStreamLoader {
/**
* Class configuration
* @var array
*/
protected static $config = array(
'mimms-bin' => '/usr/bin/mimms',
'time-limit' => 0,
'default-download-directory' => '/tmp'
);
/**
* The progress callback function/method reference.
* @var mixed
*/
private $progressCallback = null;
/**
* Contains the url of the multimedia stream
* @var string
*/
protected $streamUrl = '';
/**
* Contains the path of the output file
* @var string
*/
protected $outputFile = '';
/**
* Returns the class configuration. If a configuration array is given, modifies
* the configuration by key merging.
* @param array $config
* @return array
*/
public static final function config(array $config = array()) {
if (!empty($config)) {
self::$config = array_merge(self::$config, $config);
Tracer::trace_r($config, '$config', 3);
}
if (empty(self::$config['mimms-bin']) || !FileSystem::isFile(self::$config['mimms-bin']) || !FileSystem::isExecutable(self::$config['mimms-bin'])) {
$mimms = trim(exec('which mimms'), "\t\n\r ");
Tracer::trace("mimms binary search result (which mimms)=$mimms", 2);
if (empty($mimms)) {
throw new MediaStreamLoaderException("Your configutation is incorrect: can't find the mimms binary: :binary", array(':binary' => self::$config['mimms-bin']));
} else {
Tracer::trace("Warning: The configured mimms binary path is wrong (" . self::$config['mimms-bin'] . "), but found binary '$mimms'");
}
self::$config['mimms-bin'] = $mimms;
}
return self::$config;
}
/**
* Converts string data size formats to SI (e.g. 100K to 100000)
* @param string $text
* @return int
*/
protected function sizeToBytes($text) {
if (preg_match('/([\d\.]+)[\s]*([\w]*)/i', $text, $matches)) {
array_shift($matches);
$n = doubleval(reset($matches));
$u = strtolower(trim(end($matches)));
if (strlen($u) > 1)
$u = substr($u, 0, 1);
switch ($u) {
case 't': $n *= 1024;
case 'g': $n *= 1024;
case 'm': $n *= 1024;
case 'k': $n *= 1024;
}
return intval($n);
} else {
return false;
}
}
/**
* Converts a time duration string (e.g. "01:45:00.0") to seconds
* @param string $timeString
* @return int
*/
protected function timeToSeconds($timeString) {
$timeString = explode(':', $timeString);
$t = 0;
foreach (array_reverse($timeString) as $k => $v) {
$t += $v * pow(60, $k);
}
return $t;
}
/**
* Sets the progress callback. The function/method pattern has to be as
* function(array $progress) { ... }
* @param mixed $callback
*/
public function setProgressCallback($callback) {
if (empty($callback)) {
$this->progressCallback = null;
} else if (!is_callable($callback)) {
throw new MediaStreamLoaderException('Specified progress callback is not callable.');
} else {
$this->progressCallback = $callback;
}
}
/**
* Sets the stream URI to download
* @param string $url
*/
public function setStreamUrl($url) {
$this->streamUrl = trim($url);
}
/**
* Returns the URI of the stream
* @return string
*/
public function getStreamUrl() {
return $this->streamUrl;
}
/**
* Returns the file basename that the stream url contains
* @return string
*/
public function getStreamFileName() {
return FileSystem::getBasename(parse_url($this->streamUrl, PHP_URL_PATH));
}
/**
* Sets the output file path
* @param string $path
*/
public function setOutputFile($path) {
$this->outputFile = trim($path);
}
/**
* Returns the output file path
* @return string
*/
public function getOutputFile() {
return $this->outputFile;
}
/**
* Internal ShellProcess onStdOut/inStdErr callback
* @param string& $text
*/
public function onStdOutCallback(&$text) {
$this->lastOutputTime = time();
$passthrough = array();
$progress = '';
foreach (explode("\n", trim($text, "\n\r\t ")) as $t) {
if (stripos($t, 'remaining') !== false) {
$progress = $t;
} else {
$passthrough[] = $t;
}
}
$text = implode("\n", $passthrough);
if (!empty($progress) && !empty($this->progressCallback)) {
list($loaded, $progress) = explode('/', $progress, 2);
list($toload, $progress) = explode('(', $progress, 2);
list($rate, $progress) = explode(',', $progress, 2);
$rmtime = trim(str_replace(')', '', $progress));
$progress = array(
'progress' => round($this->sizeToBytes($loaded) / $this->sizeToBytes($toload), 3),
'loaded' => $this->sizeToBytes($loaded),
'total' => $this->sizeToBytes($toload),
'speed' => $this->sizeToBytes($rate),
'remaining' => $this->timeToSeconds(str_replace(array('remaining', ' '), '', $rmtime))
);
call_user_func($this->progressCallback, $progress);
}
}
/**
*
*/
public function onProcessRunningCallback() {
if (isset($this->lastOutputTime)) {
if (time() - $this->lastOutputTime > 60) {
throw new MediaStreamLoaderException('mimms was hanging');
}
}
return true;
}
/**
* Downloads the stream
*/
public function download() {
if (empty($this->streamUrl)) {
throw new MediaStreamLoaderException("No stream URI specified.");
} else if (empty($this->outputFile)) {
$this->outputFile = FileSystem::getBasename(parse_url($this->streamUrl, PHP_URL_PATH));
}
if (empty($this->outputFile)) {
throw new MediaStreamLoaderException("Could not determine output file path from URI ':uri'", array(':uri' => $this->streamUrl));
} else if (strpos($this->outputFile, '/') !== 0) {
if (!FileSystem::isDirectory(self::$config['default-download-directory'])) {
throw new MediaStreamLoaderException("Download directory does not exist: ':dir'", array(':dir' => self::$config['default-download-directory']));
} else if (!FileSystem::isWritable(self::$config['default-download-directory']) || !FileSystem::isExecutable(self::$config['default-download-directory'])) {
throw new MediaStreamLoaderException("Download directory is not writable: ':dir'", array(':dir' => self::$config['default-download-directory']));
}
$this->outputFile = self::$config['default-download-directory'] . '/' . $this->outputFile;
}
if (FileSystem::isFile($this->outputFile)) {
throw new MediaStreamLoaderException("Output file exists already: ':file'", array(':file' => $this->outputFile));
}
$cmd = self::$config['mimms-bin'] . ' ' . escapeshellarg($this->streamUrl) . ' ' . escapeshellarg($this->outputFile) . ' 2>&1';
$proc = new ShellProcess();
$proc->setCommand($cmd);
$proc->setCallbacks(array('onStdOut' => array($this, 'onStdOutCallback'), 'onprocessrunning' => array($this, 'onProcessRunningCallback')));
$proc->setTerminateOnAbort(true);
$proc->setTimeout(self::$config['time-limit']);
$proc->setFetchOutput(true);
$proc->run();
Tracer::trace_r($proc, '$proc', 2);
return $proc->getStdOut();
}
}