public function TranscoderAbstractionFactoryZencoder::processPostback in Video 7.2
Process postback jobs
Overrides TranscoderAbstractionFactory::processPostback
File
- transcoders/
TranscoderAbstractionFactoryZencoder.inc, line 583 - File containing class TranscoderAbstractionFactoryZencoder
Class
- TranscoderAbstractionFactoryZencoder
- Class that handles Zencoder transcoding.
Code
public function processPostback() {
if (strcasecmp($_SERVER['REQUEST_METHOD'], 'POST') !== 0) {
echo 'This is the Zencoder notification handler. It seems to work fine.';
return;
}
ignore_user_abort(TRUE);
libraries_load('zencoder');
$zencoder = new Services_Zencoder();
try {
$notification = $zencoder->notifications
->parseIncoming();
} catch (Services_Zencoder_Exception $e) {
watchdog('transcoder', 'Postback received from Zencoder could not be decoded: @errormsg', array(
'@errormsg' => $e
->getMessage(),
));
echo 'Bad request';
return;
}
if (!isset($notification->job->id)) {
watchdog('transcoder', 'Postback received from Zencoder is missing the job-id parameter');
echo 'Invalid data';
return;
}
// Check output/job state
$jobid = intval($notification->job->id);
$video_output = db_query('SELECT vid, original_fid, output_fid FROM {video_output} WHERE job_id = :job_id', array(
':job_id' => $jobid,
))
->fetch();
if (empty($video_output)) {
echo 'Not found';
return;
}
$fid = intval($video_output->original_fid);
watchdog('transcoder', 'Postback received from Zencoder for fid: @fid, Zencoder job id: @jobid.', array(
'@fid' => $fid,
'@jobid' => $jobid,
));
// Find the transcoding job.
$video = video_jobs::load($fid);
if (empty($video)) {
echo 'Transcoding job not found in database';
return;
}
// Zencoder API 2.1.0 and above use $notification->job->outputs.
// For now, only one output is supported.
$output = isset($notification->output) ? $notification->output : current($notification->job->outputs);
// Find all error situations
if ($output->state === 'cancelled') {
watchdog('transcoder', 'Video with fid @fid and job id @jobid is marked as failed because a cancellation notification was received from Zencoder.', array(
'@fid' => $fid,
'@jobid' => $jobid,
), WATCHDOG_WARNING);
video_jobs::setFailed($video);
echo 'Cancelled';
return;
}
if ($output->state === 'failed') {
$errorlink = t('no specific information given');
if (!empty($output->error_message)) {
if (!empty($output->error_link)) {
$errordetail = l(t($output->error_message), $output->error_link);
}
else {
$errordetail = t($output->error_message);
}
}
watchdog('transcoder', 'Zencoder reports errors in postback for fid @fid, job id @jobid: !errordetail', array(
'@fid' => $fid,
'@jobid' => $jobid,
'!errordetail' => $errordetail,
), WATCHDOG_ERROR);
video_jobs::setFailed($video);
echo 'Failure';
return;
}
if ($notification->job->state !== 'finished') {
echo 'Not finished';
return;
}
// Move the converted video to its final destination
$outputfile = file_load($video_output->output_fid);
if (empty($outputfile)) {
echo 'Output file not found in database';
return;
}
// Sometimes the long duration of the copy() call causes Zencoder
// to timeout and retry the notification postback later.
// So we only copy the file when it doesn't exist or has a different file size.
// file_save() invokes filesize(), which may return a cached value.
// Clear the stat cache to get a fresh value.
clearstatcache();
if (!file_exists($outputfile->uri) || filesize($outputfile->uri) != $output->file_size_in_bytes) {
if (!$this
->moveFile($output->url, $outputfile->uri)) {
watchdog('transcoder', 'While processing Zencoder postback, failed to copy @source-uri to @target-uri.', array(
'@source-uri' => $output->url,
'@target-uri' => $outputfile->uri,
), WATCHDOG_ERROR);
video_jobs::setFailed($video);
echo 'Error while moving';
return;
}
}
$outputfile->filesize = $output->file_size_in_bytes;
file_save($outputfile);
// Actual processing of the response
$video->duration = round($output->duration_in_ms / 1000);
video_jobs::setCompleted($video);
// Clear the field cache. Normally, node_save() does this, but that function is not invoked in all cases
video_utility::clearEntityCache($video->entity_type, $video->entity_id);
// If there are no thumbnails, quit now.
if (empty($output->thumbnails)) {
echo 'No thumbnails';
return;
}
// Retrieve the thumbnails from the notification structure
// Pre-2.1.0, each thumbnail list was an array, now it is an object
$thumbnails = is_array($output->thumbnails[0]) ? $output->thumbnails[0]['images'] : $output->thumbnails[0]->images;
if (empty($thumbnails)) {
echo 'No thumbnails 2';
return;
}
// Find the entity to which the file belongs
$entity = video_utility::loadEntity($video->entity_type, $video->entity_id);
if (empty($entity)) {
watchdog('transcoder', 'The entity to which the transcoded video belongs can\'t be found anymore. Entity type: @entity-type, entity id: @entity-id.', array(
'@entity-type' => $video->entity_type,
'@entity-id' => $video->entity_id,
), WATCHDOG_ERROR);
echo 'No entity';
return;
}
// The following information was saved in video_jobs::create()
$fieldname = $video->data['field_name'];
$field = field_info_field($fieldname);
$langcode = $video->data['langcode'];
$delta = $video->data['delta'];
// Insanity checks
if (empty($entity->{$fieldname}[$langcode][$delta])) {
// The field can't be found anymore. This may be a problem.
watchdog('transcoder', 'The field to which video @filename was uploaded doesn\'t seem to exist anymore. Entity type: @entity-type, entity id: @entity-id, field name: @fieldname, field language: @langcode, delta: @delta.', array(
'@filename' => $video->filename,
'@entity-type' => $video->entity_type,
'@entity-id' => $video->entity_id,
'@fieldname' => $fieldname,
'@langcode' => $langcode,
'@delta' => $delta,
), WATCHDOG_WARNING);
echo 'No field';
return;
}
if ($entity->{$fieldname}[$langcode][$delta]['fid'] != $video->fid) {
// The field does not contain the file we uploaded.
watchdog('transcoder', 'The field to which video @filename was uploaded doesn\'t seem to contain this video anymore. Entity type: @entity-type, entity id: @entity-id, field name: @fieldname, field language: @langcode, delta: @delta.', array(
'@filename' => $video->filename,
'@entity-type' => $video->entity_type,
'@entity-id' => $video->entity_id,
'@fieldname' => $fieldname,
'@langcode' => $langcode,
'@delta' => $delta,
), WATCHDOG_WARNING);
echo 'No field in entity';
return;
}
// Destination of thumbnails
$thumbscheme = !empty($field['settings']['uri_scheme_thumbnails']) ? $field['settings']['uri_scheme_thumbnails'] : 'public';
$thumburibase = $thumbscheme . '://' . variable_get('video_thumbnail_path', 'videos/thumbnails') . '/' . $video->fid . '/';
file_prepare_directory($thumburibase, FILE_CREATE_DIRECTORY);
$thumbwrapper = file_stream_wrapper_get_instance_by_scheme($thumbscheme);
// Turn the thumbnails into managed files.
// Because two jobs for the same video may finish simultaneously, lock here so
// there are no errors when inserting the files.
if (!lock_acquire('video_zencoder_thumbnails:' . $video->fid, count($thumbnails) * 30)) {
if (lock_wait('video_zencoder_thumbnails:' . $video->fid, count($thumbnails) * 30)) {
watchdog('transcoder', 'Failed to acquire lock to download thumbnails for @video-filename.', array(
'@video-filename' => $video->filename,
), WATCHDOG_ERROR);
return;
}
}
$existingthumbs = db_query('SELECT f.uri, f.fid, f.filesize FROM {file_managed} f INNER JOIN {video_thumbnails} t ON (f.fid = t.thumbnailfid) WHERE t.videofid = :fid', array(
':fid' => $video->fid,
))
->fetchAllAssoc('uri');
$thumbs = array();
$tnid = 0;
foreach ($thumbnails as $thumbnail) {
// Pre-2.1.0, each thumbnail was an array
$thumbnail = (object) $thumbnail;
$urlpath = parse_url($thumbnail->url, PHP_URL_PATH);
$ext = video_utility::getExtension($urlpath);
$thumb = new stdClass();
$thumb->uid = $outputfile->uid;
// $entity may not have a uid property, so take it from the output file.
$thumb->status = FILE_STATUS_PERMANENT;
$thumb->filename = 'thumbnail-' . $video->fid . '_' . sprintf('%04d', $tnid++) . '.' . $ext;
$thumb->uri = $thumburibase . $thumb->filename;
$thumb->filemime = $thumbwrapper
->getMimeType($thumb->uri);
$thumb->type = 'image';
// For the media module
$thumb->filesize = $thumbnail->file_size_bytes;
$thumb->timestamp = REQUEST_TIME;
$shouldcopy = TRUE;
if (isset($existingthumbs[$thumb->uri])) {
// If the thumbnail has the same size in the database compared to the notification data, don't copy
if (file_exists($thumb->uri) && $existingthumbs[$thumb->uri]->filesize == $thumb->filesize) {
$shouldcopy = FALSE;
}
$thumb->fid = intval($existingthumbs[$thumb->uri]->fid);
}
if ($shouldcopy && !$this
->moveFile($thumbnail->url, $thumb->uri)) {
watchdog('transcoder', 'Could not copy @thumbsrc to @thumbdest.', array(
'@thumbsrc' => $thumbnail->url,
'@thumbdest' => $thumb->uri,
), WATCHDOG_ERROR);
continue;
}
file_save($thumb);
// Saving to video_thumbnails and file_usage is only necessary when this is a new thumbnail
if (!isset($existingthumbs[$thumb->uri])) {
db_insert('video_thumbnails')
->fields(array(
'videofid' => $video->fid,
'thumbnailfid' => $thumb->fid,
))
->execute();
file_usage_add($thumb, 'file', $video->entity_type, $video->entity_id);
}
$thumbs[$thumb->fid] = $thumb;
}
lock_release('video_zencoder_thumbnails:' . $video->fid);
// Clear the field cache. Normally, node_save() does this, but that function is not invoked in all cases
video_utility::clearEntityCache($video->entity_type, $video->entity_id);
// Skip setting the thumbnail if there are no thumbnails or when the current value is already valid
$currentthumb = isset($entity->{$fieldname}[$langcode][$delta]['thumbnail']) ? intval($entity->{$fieldname}[$langcode][$delta]['thumbnail']) : 0;
if (empty($thumbs) || isset($thumbs[$currentthumb])) {
echo 'OK: Thumbnail already set';
return;
}
// Set a random thumbnail fid on the entity and save the entity
$entity->{$fieldname}[$langcode][$delta]['thumbnail'] = array_rand($thumbs);
switch ($video->entity_type) {
case 'node':
node_save($entity);
break;
case 'comment':
comment_save($entity);
break;
default:
// entity_save() is supplied by the entity module
if (function_exists('entity_save')) {
entity_save($video->entity_type, $entity);
}
break;
}
echo 'OK';
}