* @package WB * @subpackage vfs */ WBClass::load('WBLog' , 'WBVFS' , 'WBVFS_File'); /** * Virtual File System: Mime * * @version 1.1.0 * @package WB * @subpackage vfs */ abstract class WBVFS_Mime extends WBStdClass { /** * list of mime types that my convert to this one * @var array */ protected $convertable = array(); /** * file name * @var string */ protected $file = null; /** * virtual file * @var WBVFS_File */ protected $vfile; /** * file information * @var array */ protected $info = array(); /** * logger * @var WBLog */ protected $log; /** * major mime type * @var string */ protected $mimeMajor = ''; /** * minor mime type * @var string */ protected $mimeMinor = ''; /** * ask for special minor mime type * @var string */ protected $requestedMimeMinor = WBVFS::MIME_PLAIN; /** * file name extension * @var string */ protected $extension = 'bin'; /** * Whether file view may be incremented * @var bool */ protected $incrementable = false; /** * @var WBResponse */ protected $res; /** * @var WBRequest */ protected $req; /** * constructor * * Configure file utiltiy object * * @param array $parameter */ final public function __construct($parameter = array()) { $this->log = WBLog::start(__CLASS__); $this->mimeMajor = strtolower(substr(get_class($this), (strlen(__CLASS__) + 1))); $this->init(); } /** * 2nd constructor * */ protected function init() { } /** * tell mime handler wich file to work with * * @param string $file * @param string $minor * @param string $name */ final public function setFile($file, $minor, $name = '') { $this->file = $file; $this->mimeMinor = $minor; $this->onSetFile($name); } /** * inform when file was set * * @param string $name */ protected function onSetFile($name = '') { } /** * set viretual file * * This function return whether it is possible to convert the file * to target mime type * * @param WBVFS_File $file * @return bool */ final public function setVirtualFile(WBVFS_File $file) { $this->vfile = $file; $this->mimeMajor = $this->vfile->getMime(WBVFS_File::MIME_MAJOR); $this->mimeMinor = $this->vfile->getMime(WBVFS_File::MIME_MINOR); $this->onSetVirtualFile(); $myMime = strtolower(substr(get_class($this), strlen('WBVFS_Mime_'))); if ($this->mimeMajor == $myMime) { return true; } if (!in_array($this->mimeMajor, $this->convertable)) { return false; } return true; } /** * inform when virtual file was set * */ protected function onSetVirtualFile() { } /** * get viretual file * * @return WBVFS_File $file */ final public function getVirtualFile() { return $this->vfile; } /** * execute command * * Each mime type may implement commands like unpack for ziped archives * or rotating for images * * @param string $cmd * @param array $arg */ public function execute($cmd, $arg = array()) { $log = array( 'file' => $this->vfile->getId(), 'mime' => $this->mimeMajor, 'cmd' => $cmd, 'status' => 'notimplemented' ); $this->log->err($log); } /** * get file name of requested file * * This is just a template method that forward call * * @see doGetRequestedFile() * @throws WBException_Argument * @param WBRequest $req * @param WBResponse $res * @param bool $redirect whether to redirect * @param string|null requested minor mime type * @return string */ final public function getRequestedFile($req, $res, &$redirect, $minor = null) { $this->req = $req; $this->res = $res; $this->incrementable = true; if ($minor) { $this->requestedMimeMinor = $minor; } return $this->doGetRequestedFile($redirect); } /** * get file name of requested file * * @param bool $redirect whether to redirect * @return string */ protected function doGetRequestedFile(&$redirect) { $this->mimeMajor = $this->vfile->getMime(WBVFS_File::MIME_MAJOR); $this->mimeMinor = $this->vfile->getMime(WBVFS_File::MIME_MINOR); return $this->vfile->getPath(); } /** * Increment View? * * Tell whether file view should be incremented * @return bool */ public function isIncrementable() { return $this->incrementable; } /** * fetch dynamic mime type * * @param int $type * @return string */ final public function getMime($type = WBVFS_File::MIME_MAJOR) { if ($type == WBVFS_File::MIME_MAJOR) { return $this->mimeMajor; } if ($type == WBVFS_File::MIME_MINOR) { return $this->mimeMinor; } if ($type == (WBVFS_File::MIME_MAJOR | WBVFS_File::MIME_MINOR) ) { return $this->mimeMajor . '/' . $this->mimeMinor; } return null; } /** * get file extension * * Return Windows friendly file extension * * @param string * @return string */ public function getExtension($mimeMinor = null) { $this->resolveExtension($mimeMinor); return $this->extension; } /** * tell file name extension * * Calculate suitable file name extension accotding * to minor mime type * @param string minor mime type */ protected function resolveExtension($mimeMinor = null) { } /** * gather information about this file * * Get with and height of images or videos, exif headers, * or content of ZIP-archive. * * @return array */ public function getInfo() { return $this->info; } /** * import file * * Do whatever is required to import file, * of course, this may replace the original file */ public function import() { } /** * tell whether file needs to be queued * * If a file is put in queue, it waits for asynchronious processing. * This is very importand for tasks that just take too long to to * right on import * * @deprecated use event system * @return bool */ public function queue() { return false; } /** * make cache folder * * @return string */ protected function mkCacheDir() { $file = WBClass::create('WBFile'); /** @var $file WBFile */ $varDir = realpath(WBParam::get('wb/dir/base') . '/var'); $myMime = strtolower(substr(get_class($this), strlen('WBVFS_Mime_'))); $cacheDir = 'var/cache' . substr($this->vfile->getPath(), strlen($varDir)) . '/' . $myMime; $file->mkdir($cacheDir); return $file->realpath(); } /** * Execute Command * * @see exec() * @param string * @return */ protected function exec($cmd, $vars = array()) { $cmd = WBString::populate($cmd, $vars); $out = array(); $ret = null; $now = WBClock::now(); exec($cmd, $out, $ret); $log = array( 'action' => 'exec', 'status' => 'ok', 'elapsed' => WBClock::stop($now, 1000, 1) . 'ms', 'cmd' => $cmd ); if (0 < $ret) { $log['status'] = 'failed'; $this->log->warn($log); return false; } $this->log->info($log); return true; } /** * Parse output of FFprobe * * @param array * @return array */ protected function parseFfprobe($out) { $mediaInfo = array(); $mediaInfo['container'] = null; $mediaInfo['duration'] = null; $mediaInfo['seconds'] = 0; $mediaInfo['bitrate'] = null; $mediaInfo['streams'] = array(); // extract info from video stats $streamVideo = array(); $inInput = false; foreach ($out as $o) { // get rid of whitespaces $o = trim($o); if (!$inInput) { if (strncmp('Input #', $o, 7) != 0) { continue; } $inInput = true; // find container format $tmp = array_map('trim', explode(',', $o)); $mediaInfo['container']= $tmp[1]; continue; } // left output? if (strncmp('Output #', $o, 8) == 0) { $inInput = false; continue; } // duration and bitrate if (strncmp('Duration: ', $o, 10) == 0) { if (preg_match_all('/(\w+):\s([^,]+)/', $o, $match)) { if (strtolower( $match[1][0] ) == 'duration') { $mediaInfo['duration'] = $match[2][0]; } if (isset($match[1][2]) && strtolower($match[1][2]) == 'bitrate') { list($mediaInfo['bitrate']) = explode(' ', $match[2][2]); } } $time = array_reverse(explode(':', $mediaInfo['duration'])); $mul = 1; foreach ($time as $t) { $mediaInfo['seconds'] += $t * $mul; $mul *= 60; } // go to next line continue; } // find streams if (strncmp('Stream #', $o, 8) == 0 ) { $tmp = array_map('trim', explode(' ', $o)); $stream = array( 'name' => substr($tmp[1], 1, -1), 'type' => strtolower($tmp[2]) ); array_shift($tmp); array_shift($tmp); $tmp = implode(' ', $tmp); $tmp = array_map('trim', explode(':', $tmp)); $stream['type'] = strtolower($tmp[0]); $typeInfo = array_map('trim', explode(',', $tmp[1])); switch ($stream['type']) { case 'video': // extract dimensions $dim = explode('x', $typeInfo[2]); list($dim[1]) = explode(' ', $dim[1]); // try using regular expressions if (!is_numeric(($dim[0]))) { for ($i = 2; $i < count($typeInfo); ++$i) { if (!preg_match('/(\\d+)x(\\d+)/', $typeInfo[$i], $match)) { continue; } $dim = array($match[1], $match[2]); break; } } $stream['codec'] = $typeInfo[0]; $stream['size'] = implode('x', $dim); $stream['par'] = '1:1'; $stream['dar'] = '1:1'; if (isset($typeInfo[3])) { list($stream['fps']) = explode(' ', $typeInfo[3]); } // PAR if( preg_match( '/Video:.+PAR\s(\d+:\d+)/', $o, $match ) ) { $stream['par'] = $match[1]; } // DAR if( preg_match( '/Video:.+DAR\s(\d+:\d+)/', $o, $match ) ) { $stream['dar'] = $match[1]; } $streamVideo = $stream; $streamVideo['width'] = $dim[0]; $streamVideo['height'] = $dim[1]; break; case 'audio': $stream['codec'] = $typeInfo[0]; list($stream['samplingrate']) = explode(' ', $typeInfo[1]); $stream['channel'] = $typeInfo[2]; if (isset($typeInfo[3])) { list($stream['bitrate']) = explode(' ', $typeInfo[3]); } break; } $mediaInfo['streams'][] = $stream; } } // calc width and height according aspect ration if (empty($streamVideo)) { return $mediaInfo; } $mediaInfo['width'] = ''; $mediaInfo['height'] = ''; // calculate real size for anamorphic videos $size = array( $streamVideo['width'], $streamVideo['height'] ); $this->calcRealSize($size, $streamVideo['par']); $mediaInfo['width'] = $size[0]; $mediaInfo['height'] = $size[1]; return $mediaInfo; } /** * Calculate view size of video * * Check pixel asect ratio (PAR) and calculate actual video * size. This allows to transform from anamorphic to square pixel * videos. (Square pixel is what we prefer) * * @param array $size * @param string $par */ private function calcRealSize(&$size, $par) { if ('1:1' == $par) { return; } $par = explode(':', $par); $par = intval($par[0]) / intval($par[1]); $size[0] *= $par; $size[0] = round(($size[0] / 2), 0) * 2; } /** * Create temporary working dir * * Receive path of dir * * @return string */ protected function mkWorkingDir($prefix) { $tmpDir = WBParam::get('wb/dir/base') . '/var/tmp'; $cwd = tempnam($tmpDir, 'vfs' . $prefix); unlink($cwd); $cwd = $cwd . '.d'; mkdir($cwd); return $cwd; } /** * Remove working dir * * Delete folder recursively * * @see WBFile::removeDir() * @param string $cwd */ protected function rmWorkingDir($cwd) { $file = WBClass::create('WBFile'); $file->removeDir($cwd, false); } }