function _receiveJob() { $result = ''; $dir_jobs_queued = $this->_options['DirJobsQueued']; if (!$dir_jobs_queued) { throw new UnexpectedValueException('Configuration option DirJobsQueued required'); } $path = end_with_slash($dir_jobs_queued); $job_id = ''; if (file_exists($path) && is_dir($path)) { if ($dh = opendir($path)) { while (($file = readdir($dh)) !== false) { if (substr($file, 0, 1) != '.') { $xml = file_get($path . $file); $this->_jobID = substr($file, 0, strpos($file, '.')); $xml = @simplexml_load_string($xml); if (!$xml) { throw new RuntimeException('Could not parse job XML'); } $this->_jobXML = $xml; break; // just do one } } closedir($dh); } } }
function _post($type) { $dir_jobs_queued = $this->_options['DirJobsQueued']; $job_id = empty($this->_options['JobID']) ? '' : $this->_options['JobID']; // make sure needed configuration is there if (!$dir_jobs_queued) { throw new UnexpectedValueException('Configuration option DirJobsQueued required'); } // if no JobID configuration option was set make up an ID if (!$job_id) { $job_id = unique_id($type . 'job'); } $path = end_with_slash($dir_jobs_queued); $path .= $job_id . '.xml'; // make sure we have a directory to write the job xml file to if (!safe_path($path)) { throw new RuntimeException('Could not create path: ' . $path); } // build job xml and write to file $xml_str = $this->_xmlBody($type); if (!@file_put_contents($path, $xml_str)) { throw new RuntimeException('Could not create file: ' . $path); } return $job_id; }
function _get($type, $job_id) { $dir_cache = $this->getOption('DirCache'); if (!$dir_cache) { throw new UnexpectedValueException('Configuration option DirCache required'); } $path = end_with_slash($dir_cache) . $job_id . '/media.xml'; $raw_xml = file_get($path); $result = ''; //$progress = -1; $status = 'Progress or PercentDone tags not found in response'; $progress = 10; // now if there is any problem we set the percent back to one and retry later if (!$raw_xml) { $status = 'Queued'; } else { $raw_xml = '<moviemasher>' . $raw_xml . '</moviemasher>'; $xml = $this->_parseResponse($raw_xml); if (!empty($xml->Progress) && is_object($xml->Progress)) { $data = $xml->Progress[sizeof($xml->Progress) - 1]; if (!empty($data->PercentDone)) { $progress = (string) $data->PercentDone; if (!empty($data->Status)) { $status = (string) $data->Status; } else { $progress = 10; $status = 'Status tag empty'; } } else { $status = 'PercentDone tag empty'; } } else { $status = 'Progress tag empty'; } } if (strpos($status, 'empty') !== FALSE) { $this->log($status . ' in response: ' . $raw_xml); } $result = array('percent' => $progress, 'status' => $status); return $result; }
function MovieMasher($config = array()) { $this->_options = array(); reset($config); foreach ($config as $k => $v) { $this->_options[$k] = $v; } $this->_populateDefaults(); foreach ($this->_configDefaults as $k => $v) { if (!isset($this->_options[$k])) { $this->_options[$k] = isset($v['default']) ? $v['default'] : ''; } } foreach ($this->_optionsDefaults as $k => $v) { if (!isset($this->_options[$k])) { $this->_options[$k] = isset($v['default']) ? $v['default'] : ''; } } // make sure paths have trailing slashes $this->_options['DirLog'] = end_with_slash($this->_options['DirLog']); $this->_options['DirCache'] = end_with_slash($this->_options['DirCache']); $this->_options['DirHost'] = end_with_slash($this->_options['DirHost']); $this->_options['DirTemporary'] = end_with_slash($this->_options['DirTemporary']); }
function _post($type) { $result = FALSE; $headers = array(); $rest_endpoint = $this->_options['RESTEndPoint']; $rest_key_private = $this->_options['RESTKeyPrivate']; $job_id = empty($this->_options['JobID']) ? '' : $this->_options['JobID']; // make sure we have an endpoint to post to if (!$rest_endpoint) { throw new UnexpectedValueException('Configuration option RESTEndPoint required'); } // generate the job xml $xml_req = $this->_xmlBody($type); if (!empty($rest_key_private)) { // generate headers with signature $gmd = gmdate('D, d M Y H:i:s O'); $sig = array(); $params = $this->_jobParameters($type); ksort($params); foreach ($params as $k => $v) { $sig[] = "{$k}={$v}"; } $sig = $gmd . join('&', $sig); $sig = private_signature($rest_key_private, $sig); if (!$sig) { throw new RuntimeException('Could not sign request'); } $headers[] = 'X-Moviemasher-Date: ' . $gmd; $headers[] = 'Authorization: ' . $sig; } $url = end_with_slash($rest_endpoint) . 'mm.' . $this->getVersion() . '/rest/' . $type . '/'; if (!empty($this->_options['LogRequests'])) { $this->log('SENDING REST Request: ' . $xml_req); } $xml_string = http_post_xml($url, $xml_req, $headers); // make sure we got a response, log it and parse into SimpleXML object if (!$xml_string) { throw new RuntimeException('Could not post xml to: ' . $url . ' ' . $xml_req); } if (!empty($this->_options['LogResponses'])) { $this->log('RECEIVING REST Response ' . $xml_string); } $xml = $this->_parseResponse($xml_string); // determine job ID and return if (!empty($xml->JobID)) { $result = (string) $xml->JobID; } else { if (!empty($job_id)) { $result = $job_id; } } return $result; }
function __buildVisuals($start, $stop) { $path = $this->_options['CoderBaseURL']; $result = array(); $clips = MovieMasher_Coder_Decoder::videoBetween($this->__mashData['clips'], $start, $stop); $fps = floatval($this->_options['DecoderFPS']); $z = sizeof($clips); for ($i = 0; $i < $z; $i++) { $clip = $clips[$i]; // id, type, tag, start, stop, duration (as floats) $clip_trim = $this->__clipTrim($clip, $start, $stop, $fps); if (!$clip_trim) { throw new UnexpectedValueException('Could not determine clip trim: ' . $clip['id']); } $media_tag = $this->__mashData['media'][$clip['id']]; $cmd = ''; $cmd .= $this->_options['PathFFmpeg']; $dims = explode('x', $this->_options['DecoderDimensions']); $url = (string) $media_tag['source']; if (!$url) { $url = (string) $media_tag['url']; } $url = absolute_url(end_with_slash($this->_options['CoderBaseURL']), $url); $url = MovieMasher_Coder::cleanURL($url); switch ($clip['type']) { case 'image': $file_path = cache_url($url, $this->_options['DirCache']); if (!$file_path) { throw new RuntimeException('Could not cache image: ' . $url); } $orig_dims = get_file_info('dimensions', $file_path, $this->_options['PathFFmpeg']); if (!$orig_dims) { throw new RuntimeException('Could not determine image dimensions: ' . $url); } $orig_dims = explode('x', $orig_dims); $switches = scale_switches($orig_dims, $dims, $clip['fill']); $cmd .= ' -loop_input -vframes ' . floor($fps * $clip_trim['trimlength']); $cmd .= ' -s ' . $orig_dims[0] . 'x' . $orig_dims[1]; $cmd .= ' -i ' . $file_path; if ($switches) { $cmd .= ' ' . stringFromSwitches($switches); } else { $cmd .= ' -s ' . $this->_options['DecoderDimensions']; } break; case 'video': $video = (string) $media_tag['source']; $video = MovieMasher_Coder::cleanURL($video); if ($video) { $url = absolute_url(end_with_slash($this->_options['CoderBaseURL']), $video); } $ext = file_extension($url); if ($ext) { // grab the video file $input_path = $file_path = cache_url($url, $this->_options['DirCache']); if (!$file_path) { throw new RuntimeException('Could not cache video: ' . $url); } } else { // grab image sequence - very untested $clip_fps = floatval((string) $media_tag['fps']); $fps_secs = floatval(1) / $clip_fps; $clip_trim['trimstart'] = floor($clip_trim['trimstart'] / $fps_secs) * $fps_secs; $cmd .= ' -r ' . $clip_fps; $file_path = url_file_path($url, $this->_options['DirCache']); $orig_dims = get_file_info('dimensions', $file_path, $this->_options['PathFFmpeg']); if (!$orig_dims) { throw new RuntimeException('Could not determine image sequence dimensions: ' . $url); } $input_path = frame_file_path($file_path, $orig_dims, $clip_fps); $input_path .= '%' . intval($media_tag['zeropadding']) . 'd.jpg'; $fps = $clip_fps; } $orig_dims = get_file_info('dimensions', $file_path, $this->_options['PathFFmpeg']); if (!$orig_dims) { throw new RuntimeException('Could not determine dimensions: ' . $file_path . ' ' . $this->_options['PathFFmpeg']); } $orig_dims = explode('x', $orig_dims); $switches = scale_switches($orig_dims, $dims, $clip['fill']); $cmd .= ' -vframes ' . floor($fps * $clip_trim['trimlength']); $cmd .= ' -s ' . $orig_dims[0] . 'x' . $orig_dims[1]; $cmd .= ' -i ' . $input_path; if ($clip_trim['trimstart']) { $cmd .= ' -ss ' . $clip_trim['trimstart']; } $cmd .= ' -an'; // no audio if ($switches) { $cmd .= ' ' . stringFromSwitches($switches); } else { $cmd .= ' -s ' . $this->_options['DecoderDimensions']; } break; } $cmd .= ' -t ' . $clip_trim['trimlength']; $result[] = $cmd; } return $result; }
} elseif (!is_uploaded_file($file['tmp_name'])) { $err = 'Not an uploaded file'; } } // make sure file extension is valid if (!$err) { $file_name = $file['name']; $file_ext = file_extension($file_name); if ($file_ext != 'tgz') { $err = 'Unsupported extension: ' . $file_ext; } } // extract the archive to temp directory if (!$err) { set_time_limit(0); $tmp_dir = end_with_slash($tmp_dir); $tmp_path = $tmp_dir . unique_id('archive'); $archive_dir = $tmp_path . '/' . $filename . '/'; if (!extract_archive($file['tmp_name'], $archive_dir)) { $err = 'Could not extract to ' . $archive_dir; } } // move select files from the archive to media directory if (!$err) { switch ($type) { case 'audio': case 'video': // move any soundtrack $frag = 'media/audio.' . $encoder_audio_extension; $media_path = $media_dir . $frag; $archive_path = $archive_dir . $frag;
function url_file_path($url, $dir_path = '') { $result = FALSE; if ($url) { $result = end_with_slash($dir_path) . md5($url) . '/'; $extension = file_extension($url); if ($extension) { $result .= 'media.' . $extension; } } return $result; }
function put($options = array()) { $result = FALSE; $url = empty($options['url']) ? '' : $options['url']; $path = empty($options['path']) ? '' : $options['path']; if (!($url && $path)) { throw new InvalidArgumentException('Send array with url and path keys as first argument'); } $ignore = empty($options['ignore']) ? array() : $options['ignore']; $is_folder = is_dir($url); $urls = array(); if ($is_folder) { $path = end_with_slash($path); $urls = MovieMasher_File::files($url); } else { $urls[] = $url; } $z = sizeof($urls); //if ($this->getOption('Verbose')) $this->log(__METHOD__ . ' putting ' . $z . ' file' . (($z == 1) ? '' : 's') . ': ' . join("\n", $urls)); $errors = 0; // TODO: these could be config options $max_errors = 3; $wait_seconds = 30; for ($i = 0; $i < $z; $i++) { $options['url'] = $urls[$i]; if (in_array($options['url'], $ignore)) { continue; } if ($is_folder) { $options['path'] = $path . substr($options['url'], strlen(end_with_slash($url))); } try { $this->_put($options); $errors = 0; // one success resets error and result $result = TRUE; } catch (Exception $ex) { $result = FALSE; $errors++; if ($errors > $max_errors) { throw $ex; } $i--; $this->log(__METHOD__ . ' waiting ' . $wait_seconds . ' seconds after error ' . $errors . ': ' . $ex->getMessage()); sleep($wait_seconds); } } return $result; }
function remove_dir_and_files($path) { $result = FALSE; if ($path && file_exists($path) && is_dir($path)) { $path = end_with_slash($path); if ($handle = opendir($path)) { $result = TRUE; while ($result && FALSE !== ($file = readdir($handle))) { if ($file != "." && $file != "..") { if (is_dir($path . $file)) { $result = remove_dir_and_files($path . $file); } else { $result = @unlink($path . $file); } } } closedir($handle); } if ($result) { $result = @rmdir($path); } } return $result; }
function MovieMasher_Daemon($config) { parent::MovieMasher($config); $this->_options['DirPID'] = end_with_slash($this->_options['DirPID']); $this->_options['DirJobsQueued'] = end_with_slash($this->_options['DirJobsQueued']); }