| 1 |
<?php
|
| 2 |
// $Id$
|
| 3 |
|
| 4 |
/*
|
| 5 |
* @file cdn2.module
|
| 6 |
*/
|
| 7 |
|
| 8 |
// Find node nid matching video ID in cck field
|
| 9 |
// If status is error, throw a watchdog entry
|
| 10 |
// If status is complete,
|
| 11 |
// Push file to CDN
|
| 12 |
// Update CCK field to hold CDN URL
|
| 13 |
// Publish Entry
|
| 14 |
// Respond with "OK"
|
| 15 |
//
|
| 16 |
|
| 17 |
define('CDN2_DEFAULT_ENDPOINT', 'http://soap.cdn2.net:8080/services/transcodeService?wsdl');
|
| 18 |
define('CDN2_DEFAULT_FORM', 'http://upload1.cdn2.net:8080/fileUpload.html');
|
| 19 |
|
| 20 |
require_once('cdn2_field.inc');
|
| 21 |
|
| 22 |
/**
|
| 23 |
* parent function to render out the video node. Directs calls out to individual video assets.
|
| 24 |
*/
|
| 25 |
function theme_cdn2_video($node, $field, $items, $teaser, $page) {
|
| 26 |
$output = '';
|
| 27 |
$presets = _cdn2_get_available_presets();
|
| 28 |
$output_items = array();
|
| 29 |
if (count($items)) {
|
| 30 |
foreach ($items as $item) {
|
| 31 |
if (isset($item['assets']) && count($item['assets'])) {
|
| 32 |
for ($asset_key = 0; $asset_key < count($item['assets']); $asset_key++) {
|
| 33 |
$asset = $item['assets'][$asset_key];
|
| 34 |
if (user_access('view cdn2 video transcoded with '. $asset['preset_name']) && $presets[$asset['preset_name']]->fileFormat != 'JPEG') {
|
| 35 |
$output_items[] = '<div id="node-'. $node->nid .'-'. $asset['preset_name'] .'">'. theme('cdn2_video_asset', $node, $asset, $presets[$asset['preset_name']]) .'</div>';
|
| 36 |
$preset_list[$asset['preset_name']] = '<a href="#node-'. $node->nid .'-'. $asset['preset_name'] .'"><span>'. $presets[$asset['preset_name']]->friendlyName .'</span></a>';
|
| 37 |
}
|
| 38 |
}
|
| 39 |
}
|
| 40 |
}
|
| 41 |
}
|
| 42 |
if (count($preset_list)) {
|
| 43 |
$output .= '<div class="cdn2_tabs">';
|
| 44 |
$output .= '<ul>';
|
| 45 |
foreach ($preset_list as $preset_name => $preset_list_item) {
|
| 46 |
if (strpos($preset_name, 'thumb') == FALSE) {
|
| 47 |
$output .= '<li>'. $preset_list_item .'</li>';
|
| 48 |
}
|
| 49 |
}
|
| 50 |
$output .= '</ul>';
|
| 51 |
$output .= implode("\n", $output_items);
|
| 52 |
$output .= '</div>';
|
| 53 |
}
|
| 54 |
return $output;
|
| 55 |
}
|
| 56 |
|
| 57 |
/**
|
| 58 |
* parent function to render wrapper around a video asset. Delegates to
|
| 59 |
* appropriate theming functions based on preset short name, followed by
|
| 60 |
* file format, then finally the generic type.
|
| 61 |
*/
|
| 62 |
function theme_cdn2_video_asset(&$node, &$asset, $preset) {
|
| 63 |
$output = '';
|
| 64 |
$output .= '<div class="cdn2_video_asset">';
|
| 65 |
if ($asset['preset_name'] && user_access('view cdn2 video transcoded with '. $preset->shortName)) {
|
| 66 |
if (theme_get_function('cdn2_video_asset_'. drupal_strtolower($preset->shortName))) {
|
| 67 |
$output .= theme('cdn2_video_asset_'. drupal_strtolower($preset->shortName), $node, $asset, $preset);
|
| 68 |
}
|
| 69 |
else if (theme_get_function('cdn2_video_asset_'. drupal_strtolower($preset->fileFormat))) {
|
| 70 |
$output .= theme('cdn2_video_asset_'. drupal_strtolower($preset->fileFormat), $node, $asset, $preset);
|
| 71 |
}
|
| 72 |
else {
|
| 73 |
$output .= theme('cdn2_video_asset_generic', $node, $asset, $preset);
|
| 74 |
}
|
| 75 |
$output .= '</div>';
|
| 76 |
$output .= '<div class="cdn2_metadata">';
|
| 77 |
$output .= theme('cdn2_video_asset_metadata', $node, $asset, $preset);
|
| 78 |
$output .= '</div>';
|
| 79 |
$output .= '<div class="clear-block" style="clear: left"><!-- --></div>';
|
| 80 |
|
| 81 |
return $output;
|
| 82 |
}
|
| 83 |
}
|
| 84 |
|
| 85 |
/**
|
| 86 |
* themes the video length as a time formatted as (hh:mm:ss)
|
| 87 |
*/
|
| 88 |
function theme_cdn2_asset_video_length($video_length) {
|
| 89 |
if (!is_numeric($video_length)) {
|
| 90 |
return $video_length;
|
| 91 |
}
|
| 92 |
|
| 93 |
$hms = '';
|
| 94 |
$hours = intval(intval($video_length) / 3600);
|
| 95 |
$hms .= str_pad($hours, 2, '0', STR_PAD_LEFT) .':';
|
| 96 |
$minutes = intval(($video_length / 60) % 60);
|
| 97 |
$hms .= str_pad($minutes, 2, '0', STR_PAD_LEFT) .':';
|
| 98 |
$seconds = intval($video_length % 60);
|
| 99 |
$hms .= str_pad($seconds, 2, '0', STR_PAD_LEFT);
|
| 100 |
|
| 101 |
return $hms;
|
| 102 |
}
|
| 103 |
|
| 104 |
/**
|
| 105 |
* themes file size in kb, mb, etc.
|
| 106 |
*/
|
| 107 |
function theme_cdn2_asset_file_size($file_size) {
|
| 108 |
$output = '';
|
| 109 |
if (!is_numeric($file_size)) {
|
| 110 |
// try stripping off 'kb' from the end
|
| 111 |
//
|
| 112 |
$file_size = str_replace('kB', '', $file_size);
|
| 113 |
if (!is_numeric($file_size)) {
|
| 114 |
return $file_size;
|
| 115 |
}
|
| 116 |
}
|
| 117 |
if ($file_size < 1024) {
|
| 118 |
$output .= $file_size .' KBytes';
|
| 119 |
}
|
| 120 |
elseif ($file_size < 1048576) {
|
| 121 |
$file_size = $file_size / 1024;
|
| 122 |
$file_size = round($file_size, 2);
|
| 123 |
$output .= $file_size .' MBytes';
|
| 124 |
}
|
| 125 |
else {
|
| 126 |
$file_size = $file_size / 1048576;
|
| 127 |
$file_size = round($file_size, 2);
|
| 128 |
$output .= $file_size .' GBytes';
|
| 129 |
}
|
| 130 |
return $output;
|
| 131 |
}
|
| 132 |
|
| 133 |
/**
|
| 134 |
* theme metadata about a video asset
|
| 135 |
*/
|
| 136 |
function theme_cdn2_video_asset_metadata(&$node, $asset, $preset) {
|
| 137 |
$items = array();
|
| 138 |
$title = t('Video Information:');
|
| 139 |
$item = t('Duration: ');
|
| 140 |
// TODO: theme the video length into DD:MM:SS
|
| 141 |
$item .= ($asset['video_length'] === NULL) ? 'unknown' : theme('cdn2_asset_video_length', $asset['video_length']);
|
| 142 |
$items[] = $item;
|
| 143 |
$item = t('File Size: ');
|
| 144 |
// TODO:
|
| 145 |
$item .= ($asset['file_size'] === NULL) ? 'unknown' : theme('cdn2_asset_file_size', $asset['file_size']);
|
| 146 |
$items[] = $item;
|
| 147 |
$item = t('File Format: ');
|
| 148 |
switch ($preset->fileFormat) {
|
| 149 |
case 'FLASH':
|
| 150 |
$item .= t('Flash Video (FLV)');
|
| 151 |
break;
|
| 152 |
case 'MOV':
|
| 153 |
$item .= t('Quicktime (MOV)');
|
| 154 |
break;
|
| 155 |
default:
|
| 156 |
$item .= $preset->fileFormat;
|
| 157 |
break;
|
| 158 |
}
|
| 159 |
$items[] = $item;
|
| 160 |
$items[] = l('Download raw video file', $asset['asset_fetch_url']);
|
| 161 |
|
| 162 |
return theme('item_list', $items, $title);
|
| 163 |
}
|
| 164 |
|
| 165 |
/**
|
| 166 |
* custom theming function for quicktime videos
|
| 167 |
*/
|
| 168 |
function theme_cdn2_video_asset_mov(&$node, &$asset, $preset, $width = 320, $height = 240) {
|
| 169 |
$output = '
|
| 170 |
<object classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab" width="%s" height="%s">
|
| 171 |
<param name="src" value="%s">
|
| 172 |
<param name="autoplay" value="false">
|
| 173 |
<param name="type" value="video/quicktime" width="%s" height="%s">
|
| 174 |
<param name="scale" value="tofit">
|
| 175 |
<embed src="%s" width="%s" height="%s" scale="tofit" autoplay="false" type="video/quicktime" pluginspage="http://www.apple.com/quicktime/download/">
|
| 176 |
</object>';
|
| 177 |
$output = sprintf($output, $width, $height, $asset['asset_fetch_url'], $width, $height, $asset['asset_fetch_url'], $width, $height);
|
| 178 |
return $output;
|
| 179 |
}
|
| 180 |
|
| 181 |
function theme_cdn2_video_asset_mov_hd_low_res(&$node, &$asset, $preset) {
|
| 182 |
return theme('cdn2_video_asset_mov', $node, $asset, $preset, 640, 360);
|
| 183 |
}
|
| 184 |
|
| 185 |
function theme_cdn2_video_asset_mov_hd_high_res(&$node, &$asset, $preset) {
|
| 186 |
return theme('cdn2_video_asset_mov', $node, $asset, $preset, 640, 360);
|
| 187 |
}
|
| 188 |
|
| 189 |
/**
|
| 190 |
* custom theming function for flv types
|
| 191 |
*/
|
| 192 |
function theme_cdn2_video_asset_flash(&$node, &$asset, $preset) {
|
| 193 |
$player = variable_get('cdn2_flash_player', 'flowplayer');
|
| 194 |
|
| 195 |
$players = module_invoke_all('cdn2_player', 'flash');
|
| 196 |
$player = $players[$player];
|
| 197 |
$theme_function = $player['theme function'];
|
| 198 |
|
| 199 |
return theme($theme_function, $node, $asset, $preset, '320', '240');
|
| 200 |
}
|
| 201 |
|
| 202 |
/**
|
| 203 |
* custom theming function for flv hd types
|
| 204 |
*/
|
| 205 |
function theme_cdn2_video_asset_flash_flv_hd_low(&$node, &$asset, $preset) {
|
| 206 |
$player = variable_get('cdn2_flash_player', 'flowplayer');
|
| 207 |
|
| 208 |
$players = module_invoke_all('cdn2_player', 'flash');
|
| 209 |
$player = $players[$player];
|
| 210 |
$theme_function = $player['theme function'];
|
| 211 |
|
| 212 |
return theme($theme_function, $node, $asset, $preset, '640', '360');
|
| 213 |
}
|
| 214 |
|
| 215 |
function theme_cdn2_video_asset_Flash_flv_hd_high(&$node, &$asset, $preset) {
|
| 216 |
return theme('cdn2_video_asset_flash_flv_hd_low', $node, $asset, $preset);
|
| 217 |
}
|
| 218 |
|
| 219 |
|
| 220 |
/**
|
| 221 |
* theming function for a thumbnail
|
| 222 |
*/
|
| 223 |
function theme_cdn2_video_asset_320x240_thumb(&$node, &$asset, $preset) {
|
| 224 |
// return '<img src="'. check_url($asset['asset_fetch_url']) .'" alt="'. check_plain($node->title) .'"/>';
|
| 225 |
}
|
| 226 |
|
| 227 |
/**
|
| 228 |
* theming function for other (unknown) types that renders them as
|
| 229 |
* a link to the downloadable file.
|
| 230 |
*/
|
| 231 |
function theme_cdn2_video_asset_generic(&$node, &$asset, $preset) {
|
| 232 |
return l($preset->friendlyName, $asset['asset_fetch_url']);
|
| 233 |
}
|
| 234 |
|
| 235 |
|
| 236 |
/**
|
| 237 |
* Implementation of hook_perm().
|
| 238 |
*/
|
| 239 |
function cdn2_perm() {
|
| 240 |
$perms = array('administer cdn2', 'administer cdn2 formats', 'delete cdn2 video');
|
| 241 |
$enabled_presets = variable_get('cdn2_allowed_presets', array());
|
| 242 |
foreach ($enabled_presets as $preset) {
|
| 243 |
if ($preset) {
|
| 244 |
$perms[] = 'transcode cdn2 video to '. $preset;
|
| 245 |
$perms[] = 'view cdn2 video transcoded with '. $preset;
|
| 246 |
}
|
| 247 |
}
|
| 248 |
return $perms;
|
| 249 |
}
|
| 250 |
|
| 251 |
|
| 252 |
function _cdn2_get_assets_for_node($nid) {
|
| 253 |
$query = 'SELECT id, nid, video_token, preset_name, status, asset_fetch_url FROM {cdn2_videos} WHERE nid=%d';
|
| 254 |
$resultset = db_query($query, $nid);
|
| 255 |
$results = array();
|
| 256 |
while ($row = db_fetch_array($resultset)) {
|
| 257 |
$results[] = $row;
|
| 258 |
}
|
| 259 |
return $results;
|
| 260 |
}
|
| 261 |
|
| 262 |
/**
|
| 263 |
* Implementation of hook_widget_settings().
|
| 264 |
*/
|
| 265 |
function cdn2_widget_settings($op, $widget) {
|
| 266 |
switch ($op) {
|
| 267 |
case 'callbacks':
|
| 268 |
break;
|
| 269 |
case 'form':
|
| 270 |
$form = array();
|
| 271 |
return $form;
|
| 272 |
case 'validate':
|
| 273 |
break;
|
| 274 |
case 'save':
|
| 275 |
return array('cdn2');
|
| 276 |
}
|
| 277 |
}
|
| 278 |
|
| 279 |
function cdn2_xmlrpc() {
|
| 280 |
//video_token, preset_name, status_as_string, asset_fetch_url
|
| 281 |
|
| 282 |
// format of this response:
|
| 283 |
/*
|
| 284 |
xmlrpc method name (e.g. module.function)
|
| 285 |
callback function (e.g. module_function)
|
| 286 |
method signature:
|
| 287 |
first argument is the return type.
|
| 288 |
remainder are arguments to the function.
|
| 289 |
*/
|
| 290 |
return array(
|
| 291 |
array(
|
| 292 |
'cdn2.updateStatus',
|
| 293 |
'cdn2_update_status',
|
| 294 |
array('boolean', // return value
|
| 295 |
'string', // dateTime
|
| 296 |
'string', // calculatedHash
|
| 297 |
'string', // videoToken
|
| 298 |
'string', // preset shortName
|
| 299 |
'string', // status
|
| 300 |
'string', // fileName
|
| 301 |
'string', // fileSize
|
| 302 |
'string'), // videoLength
|
| 303 |
t('Update the status of a CDN2 video'),
|
| 304 |
)
|
| 305 |
);
|
| 306 |
}
|
| 307 |
|
| 308 |
|
| 309 |
function _cdn2_update_video_asset_status($video_token, $preset_name, $status, $asset_fetch_url, $file_size, $video_length) {
|
| 310 |
$content_types = _cdn2_get_content_types();
|
| 311 |
|
| 312 |
foreach ($content_types as $content_type) {
|
| 313 |
$nid = _cdn2_get_nid_by_video_token($content_type, $video_token);
|
| 314 |
// if the node has not yet been submitted, it's possible that we'll get a status message back before then. So
|
| 315 |
// we persist the videotoken with a nid of zero and the node save will handle the population. Hence why we ignore the nid here.
|
| 316 |
//
|
| 317 |
|
| 318 |
// fetch the existing video_nid if it exists
|
| 319 |
//
|
| 320 |
$query = "SELECT nid FROM {cdn2_video_node} where video_token='%s'";
|
| 321 |
$video_nid = db_result(db_query($query, $video_token));
|
| 322 |
|
| 323 |
|
| 324 |
$query = "SELECT id FROM {cdn2_videos} WHERE video_token = '%s' AND preset_name = '%s'";
|
| 325 |
$result = db_result(db_query($query, $video_token, $preset_name));
|
| 326 |
|
| 327 |
if (!$result) {
|
| 328 |
// creating a new record for the video asset. If the video's nid is set,
|
| 329 |
// we pass that, too
|
| 330 |
//
|
| 331 |
$id = db_next_id('cdn2_videos_id');
|
| 332 |
$query = "INSERT INTO {cdn2_videos} (id, nid, video_token, preset_name, status, asset_fetch_url, file_size, video_length)
|
| 333 |
VALUES(%d, %d, '%s', '%s', '%s', '%s', '%s', '%s')";
|
| 334 |
db_query($query, $id, $video_nid, $video_token, $preset_name, $status, $asset_fetch_url, $file_size, $video_length);
|
| 335 |
}
|
| 336 |
else {
|
| 337 |
// update the video asset independent of node context
|
| 338 |
$query = "UPDATE {cdn2_videos} SET status='%s', asset_fetch_url='%s', file_size='%s', video_length='%s' WHERE preset_name='%s' AND video_token='%s'";
|
| 339 |
db_query($query, $status, $asset_fetch_url, $preset_name, $video_token);
|
| 340 |
}
|
| 341 |
}
|
| 342 |
|
| 343 |
// check to see if the video was already submitted. If it was, we update the node with nid associated in cdn2_video_node. If not, we do nothing as
|
| 344 |
// it will get picked up by the node save itself.
|
| 345 |
//
|
| 346 |
if ($video_nid) {
|
| 347 |
// here we update the cdn2_videos table with the correct nid (in case the node was already submitted)
|
| 348 |
//
|
| 349 |
$query = "UPDATE {cdn2_videos} SET nid=%d WHERE video_token='%s'";
|
| 350 |
db_query($query, $video_nid, $video_token);
|
| 351 |
|
| 352 |
// forcing a re-save of the node to clear cache
|
| 353 |
//
|
| 354 |
$node = node_load($video_nid);
|
| 355 |
|
| 356 |
$content_type = $node->type;
|
| 357 |
$field_name = _cdn2_get_field_names_by_content_type($content_type);
|
| 358 |
|
| 359 |
if ($update = variable_get('cdn2_workflow_settings', 0)) {
|
| 360 |
// update the node's status based on workflow settings
|
| 361 |
|
| 362 |
if (!$node->status) {
|
| 363 |
$node->status = 1;
|
| 364 |
watchdog('cdn2', 'Marked '. $node->title .' as published');
|
| 365 |
}
|
| 366 |
}
|
| 367 |
node_save($node);
|
| 368 |
}
|
| 369 |
watchdog('cdn2', 'Completed transcoding '. $preset_name .' for video token '. $video_token);
|
| 370 |
return TRUE;
|
| 371 |
}
|
| 372 |
|
| 373 |
|
| 374 |
/**
|
| 375 |
* invoked when xmlrpc status callback is run
|
| 376 |
*/
|
| 377 |
function cdn2_update_status($date_time, $calculated_hash, $video_token, $preset_name, $status, $asset_fetch_url, $file_size, $video_length) {
|
| 378 |
include_once('Crypt/HMAC.php');
|
| 379 |
if (!class_exists('Crypt_HMAC')) {
|
| 380 |
watchdog("cdn2", 'PEAR Package Crypt_HMAC is required to use the CDN2 module.', WATCHDOG_ERROR);
|
| 381 |
return FALSE;
|
| 382 |
}
|
| 383 |
require_once('lib/libcdn2.inc');
|
| 384 |
|
| 385 |
$cdn2_secret_key = variable_get("cdn2_secret_key", "");
|
| 386 |
|
| 387 |
$hmac = new CDN2_Crypt_HMAC($cdn2_secret_key);
|
| 388 |
$hmac->setKey($cdn2_secret_key);
|
| 389 |
$hmac->setFunction("sha1");
|
| 390 |
|
| 391 |
$message = variable_get("cdn2_client_id", "") . $date_time;
|
| 392 |
$verified_hash = $hmac->hash($message);
|
| 393 |
if ($verified_hash != $calculated_hash) {
|
| 394 |
// failed hash check.. Do nothing
|
| 395 |
watchdog("cdn2", "Invalid hash specified. Ignoring xmlrpc request.", WATCHDOG_ERROR);
|
| 396 |
return FALSE;
|
| 397 |
}
|
| 398 |
watchdog("cdn2", "Received valid authentication hash. Processing request.");
|
| 399 |
switch ($status) {
|
| 400 |
case 'error':
|
| 401 |
drupal_mail('admin_error_mail', variable_get('site_mail'), 'Error', 'There was an error in the transcoding process');
|
| 402 |
break;
|
| 403 |
case 'complete':
|
| 404 |
watchdog('cdn2', sprintf('Updating status of video: %s with preset %s', $video_token, $preset_name), 'info');
|
| 405 |
_cdn2_update_video_asset_status($video_token, $preset_name, $status, $asset_fetch_url, $file_size, $video_length);
|
| 406 |
break;
|
| 407 |
case 'deleted':
|
| 408 |
$content_types = _cdn2_get_content_types();
|
| 409 |
|
| 410 |
foreach ($content_types as $content_type) {
|
| 411 |
$nid = _cdn2_get_nid_by_video_token($content_type, $video_token);
|
| 412 |
$query = "SELECT id FROM {cdn2_videos} WHERE nid = %d";
|
| 413 |
$result = db_result(db_query($query, $nid));
|
| 414 |
|
| 415 |
if ($result) {
|
| 416 |
$query = "DELETE FROM {cdn2_videos} WHERE nid=%d ";
|
| 417 |
db_query($query, $nid);
|
| 418 |
}
|
| 419 |
else {
|
| 420 |
watchdog("cdn2", t('Error on deletion: nid %d, not found in cdn2_videos table', $nid), WATCHDOG_ERROR);
|
| 421 |
}
|
| 422 |
}
|
| 423 |
break;
|
| 424 |
}
|
| 425 |
// TODO: make sure this still works
|
| 426 |
module_invoke_all('cdn2_video_asset_status', $video_token, $preset_name, $status, $asset_fetch_url, $file_size, $video_length);
|
| 427 |
return TRUE;
|
| 428 |
}
|
| 429 |
|
| 430 |
function _cdn2_get_nid_by_video_token($content_type, $video_token) {
|
| 431 |
$field_name = _cdn2_get_field_names_by_content_type($content_type);
|
| 432 |
$query = "SELECT nid FROM {%s} WHERE %s = '%s'";
|
| 433 |
$result = db_query($query, 'content_type_'. $content_type, $field_name[0] .'_value', $video_token);
|
| 434 |
$nid = db_fetch_object($result);
|
| 435 |
return $nid->nid;
|
| 436 |
}
|
| 437 |
/*
|
| 438 |
* cdn2_menu
|
| 439 |
* hook_menu implementation
|
| 440 |
*
|
| 441 |
*/
|
| 442 |
function cdn2_menu() {
|
| 443 |
$items = array();
|
| 444 |
$items[] = array(
|
| 445 |
'path' => 'admin/settings/cdn2',
|
| 446 |
'title' => t('CDN2 Settings'),
|
| 447 |
'description' => t('Configure CDN2'),
|
| 448 |
'callback' => 'drupal_get_form',
|
| 449 |
'callback arguments' => 'cdn2_settings',
|
| 450 |
'type' => MENU_NORMAL_ITEM,
|
| 451 |
'access' => user_access('administer site configuration'),
|
| 452 |
);
|
| 453 |
$items[] = array(
|
| 454 |
'path' => 'admin/settings/cdn2/config',
|
| 455 |
'title' => t('CDN2 Settings'),
|
| 456 |
'description' => t('Configure CDN2'),
|
| 457 |
'callback' => 'drupal_get_form',
|
| 458 |
'callback arguments' => 'cdn2_settings',
|
| 459 |
'type' => MENU_DEFAULT_LOCAL_TASK,
|
| 460 |
'access' => user_access('administer site configuration'),
|
| 461 |
);
|
| 462 |
|
| 463 |
$items[] = array(
|
| 464 |
'path' => 'admin/settings/cdn2/formats',
|
| 465 |
'title' => t('CDN2 Formats'),
|
| 466 |
'description' => t('Select which formats to allow users to transcode to.'),
|
| 467 |
'callback' => 'drupal_get_form',
|
| 468 |
'callback arguments' => 'cdn2_format_settings',
|
| 469 |
'type' => MENU_LOCAL_TASK,
|
| 470 |
'access' => user_access('administer cdn2 formats')
|
| 471 |
);
|
| 472 |
$items[] = array(
|
| 473 |
'path' => 'admin/settings/cdn2/workflow',
|
| 474 |
'title' => t('CDN2 Workflow Settings'),
|
| 475 |
'description' => t('General workflow settings for CDN2'),
|
| 476 |
'callback' => 'drupal_get_form',
|
| 477 |
'callback arguments' => 'cdn2_workflow_settings',
|
| 478 |
'type' => MENU_LOCAL_TASK,
|
| 479 |
'access' => user_access('administer site configuration'),
|
| 480 |
);
|
| 481 |
$items[] = array(
|
| 482 |
'path' => 'cdn2/uploadform',
|
| 483 |
'title' => t('Video upload form'),
|
| 484 |
'access' => user_access('access content'),
|
| 485 |
'type' => MENU_CALLBACK,
|
| 486 |
'callback' => 'cdn2_upload_form'
|
| 487 |
);
|
| 488 |
$items[] = array(
|
| 489 |
'path' => 'cdn2/xmlrpc',
|
| 490 |
'title' => t('XMLRPC callback'),
|
| 491 |
'access' => user_access('access content'),
|
| 492 |
'type' => MENU_CALLBACK,
|
| 493 |
'callback' => 'cdn2_xmlrpc',
|
| 494 |
);
|
| 495 |
$items[] = array(
|
| 496 |
'path' => 'cdn2/complete',
|
| 497 |
'title' => t('Upload Complete'),
|
| 498 |
'access' => user_access('access content'),
|
| 499 |
'type' => MENU_CALLBACK,
|
| 500 |
'callback' => 'cdn2_upload_complete'
|
| 501 |
);
|
| 502 |
|
| 503 |
$items[] = array(
|
| 504 |
'path' => 'cdn2/uploadform/status',
|
| 505 |
'title' => t('Upload Status'),
|
| 506 |
'access' => user_access('access content'),
|
| 507 |
'type' => MENU_CALLBACK,
|
| 508 |
'callback' => 'cdn2_get_upload_status',
|
| 509 |
'callback arguments' => array(arg(3)),
|
| 510 |
);
|
| 511 |
return $items;
|
| 512 |
}
|
| 513 |
|
| 514 |
function cdn2_workflow_settings() {
|
| 515 |
$form['cdn2_workflow_settings'] = array(
|
| 516 |
'#type' => 'radios',
|
| 517 |
'#title' => t('Workflow Settings'),
|
| 518 |
'#default_value' => variable_get('cdn2_workflow_settings', 0),
|
| 519 |
'#options' => array('1' => t('Publish node after transcoding is completed'), '0' => t('Do not automatically publish')),
|
| 520 |
);
|
| 521 |
return system_settings_form($form);
|
| 522 |
}
|
| 523 |
|
| 524 |
function cdn2_format_settings() {
|
| 525 |
$form = array();
|
| 526 |
drupal_add_css(drupal_get_path('module', 'cdn2') .'/cdn2.css');
|
| 527 |
drupal_add_js('/misc/jquery.js');
|
| 528 |
drupal_add_js(drupal_get_path('module', 'cdn2') .'/js/jquery.selectboxes.pack.js');
|
| 529 |
drupal_add_js(drupal_get_path('module', 'cdn2') .'/js/cdn2_admin.js');
|
| 530 |
$form['cdn2_markup'] = array(
|
| 531 |
'#type' => 'markup',
|
| 532 |
'#value' => t('Select the video formats that users should be able to transcode upload video to. Note that all formats checked below will be available users. You can configure permissions for which users have access to view and upload to certain formats by visiting the ') . l('user roles', 'admin/user/roles') . t(' page.')
|
| 533 |
);
|
| 534 |
$form['cdn2_presets'] = array(
|
| 535 |
'#type' => 'fieldset',
|
| 536 |
'#title' => t('Transcode Presets'),
|
| 537 |
'#attributes' => array('id' => 'cdn2_presets_fieldset')
|
| 538 |
);
|
| 539 |
$presets = _cdn2_get_available_presets();
|
| 540 |
$options = array();
|
| 541 |
$selection_options = array();
|
| 542 |
$selection_options['Select...'] = 'Select...';
|
| 543 |
$cdn2_allowed_presets = variable_get('cdn2_allowed_presets', array());
|
| 544 |
$js_list = array();
|
| 545 |
$js_list[] = 'var aPresetList = new Array();';
|
| 546 |
if (count($presets)) {
|
| 547 |
foreach ($presets as $preset) {
|
| 548 |
if ($preset->fileFormat != 'JPEG') {
|
| 549 |
$options[$preset->shortName] = sprintf(
|
| 550 |
'<div style="display:inline;">%s<br/><div class="description">Dimensions: %s<br/>Video Codec: %s (%sbps)<br/>Audio Codec: %s (%sbps)</div></div>',
|
| 551 |
$preset->friendlyName,
|
| 552 |
$preset->videoDimensions,
|
| 553 |
$preset->videoCodec,
|
| 554 |
$preset->videoBitRate,
|
| 555 |
$preset->audioCodec,
|
| 556 |
$preset->audioBitrate);
|
| 557 |
$selection_options[$preset->shortName] = $preset->friendlyName;
|
| 558 |
$js_list[] = sprintf('aPresetList[\'%s\'] = \'%s\';', $preset->shortName, $preset->friendlyName);
|
| 559 |
}
|
| 560 |
}
|
| 561 |
}
|
| 562 |
$js_output .= implode("\n", $js_list) ."\n";
|
| 563 |
drupal_add_js($js_output, 'inline');
|
| 564 |
$form['cdn2_presets']['cdn2_allowed_presets'] = array(
|
| 565 |
'#type' => 'checkboxes',
|
| 566 |
'#title' => 'Allowed Presets',
|
| 567 |
'#default_value' => $cdn2_allowed_presets,
|
| 568 |
'#description' => t('Set the types of presets that are transcoded by default. Items of this list may be exposed by allowing them permissions on the drupal permissions page.'),
|
| 569 |
'#options' => $options,
|
| 570 |
'#suffix' => '<div class="clear-block"></div>'
|
| 571 |
);
|
| 572 |
|
| 573 |
$top_keys = array_keys($selection_options);
|
| 574 |
$top_key = $top_keys[1];
|
| 575 |
$cdn2_default_preset = variable_get('cdn2_default_preset', $top_key);
|
| 576 |
$form['cdn2_presets']['cdn2_default_preset'] = array(
|
| 577 |
'#type' => 'select',
|
| 578 |
'#title' => t('Show format by default'),
|
| 579 |
'#default_value' => $cdn2_default_preset,
|
| 580 |
'#options' => $selection_options
|
| 581 |
);
|
| 582 |
|
| 583 |
|
| 584 |
$cdn2_flash_players = module_invoke_all('cdn2_player', 'flash');
|
| 585 |
$cdn2_flash_player_options = array();
|
| 586 |
if (count($cdn2_flash_players)) {
|
| 587 |
$form['cdn2_playback'] = array(
|
| 588 |
'#type' => 'fieldset',
|
| 589 |
'#title' => t('Playback Settings')
|
| 590 |
);
|
| 591 |
|
| 592 |
foreach ($cdn2_flash_players as $name => $player) {
|
| 593 |
$cdn2_flash_player_options[$name] = $player['title'] .' ('. l($player['source url'], $player['source url'], array('target' => '_new')) .')';
|
| 594 |
}
|
| 595 |
|
| 596 |
$form['cdn2_playback']['cdn2_flash_player'] = array(
|
| 597 |
'#type' => 'radios',
|
| 598 |
'#title' => t('Preferred Flash Player'),
|
| 599 |
'#options' => $cdn2_flash_player_options,
|
| 600 |
'#default_value' => variable_get('cdn2_flash_player', 'flowplayer')
|
| 601 |
);
|
| 602 |
}
|
| 603 |
|
| 604 |
$output = system_settings_form($form);
|
| 605 |
|
| 606 |
return $output;
|
| 607 |
}
|
| 608 |
|
| 609 |
/*
|
| 610 |
* cdn2_settings
|
| 611 |
* Admin settings hook
|
| 612 |
*
|
| 613 |
* Form for the cdn2 module's settings.
|
| 614 |
*/
|
| 615 |
function cdn2_settings() {
|
| 616 |
$form['cdn2_uploader_endpoint'] = array(
|
| 617 |
'#type' => 'textfield',
|
| 618 |
'#title' => t('SOAP Endpoint'),
|
| 619 |
'#default_value' => variable_get('cdn2_uploader_endpoint', CDN2_DEFAULT_ENDPOINT),
|
| 620 |
'#required' => TRUE,
|
| 621 |
'#description' => t('The URL of the uploader\'s SOAP service'),
|
| 622 |
);
|
| 623 |
$form['cdn2_uploader_form'] = array(
|
| 624 |
'#type' => 'textfield',
|
| 625 |
'#title' => t('Upload form URL'),
|
| 626 |
'#default_value' => variable_get('cdn2_uploader_form', CDN2_DEFAULT_FORM),
|
| 627 |
'#required' => TRUE,
|
| 628 |
'#description' => t('The URL of the uploader\'s HTML form'),
|
| 629 |
);
|
| 630 |
$form['cdn2_client_id'] = array(
|
| 631 |
'#type' => 'textfield',
|
| 632 |
'#title' => t('Client ID'),
|
| 633 |
'#default_value' => variable_get('cdn2_client_id', ''),
|
| 634 |
'#required' => TRUE,
|
| 635 |
'#description' => t('Your client ID for the cdn2 service.'),
|
| 636 |
);
|
| 637 |
|
| 638 |
$form['cdn2_secret_key'] = array(
|
| 639 |
'#type' => 'textfield',
|
| 640 |
'#title' => t('Secret Key'),
|
| 641 |
'#default_value' => variable_get('cdn2_secret_key', ''),
|
| 642 |
'#required' => TRUE,
|
| 643 |
'#description' => t('The Secret Key provided to you for the cdn2 service.'),
|
| 644 |
);
|
| 645 |
cache_clear_all();
|
| 646 |
|
| 647 |
$output = system_settings_form($form);
|
| 648 |
|
| 649 |
return $output;
|
| 650 |
}
|
| 651 |
|
| 652 |
function _cdn2_filter_presets_by_user($account, $assoc_presets, $show_all) {
|
| 653 |
$user_presets = array();
|
| 654 |
$cdn2_allowed_presets = variable_get('cdn2_allowed_presets', array());
|
| 655 |
foreach ($assoc_presets as $short_name => $preset) {
|
| 656 |
if (user_access('transcode cdn2 video to '. $short_name, $account) && $cdn2_allowed_presets[$short_name] !== 0) {
|
| 657 |
$user_presets[$short_name] = $preset;
|
| 658 |
}
|
| 659 |
}
|
| 660 |
return $user_presets;
|
| 661 |
}
|
| 662 |
|
| 663 |
function _cdn2_get_available_presets($account = NULL, $show_all = TRUE) {
|
| 664 |
static $assoc_presets;
|
| 665 |
if (count($assoc_presets)) {
|
| 666 |
if ($account) {
|
| 667 |
return _cdn2_filter_presets_by_user($account, $assoc_presets);
|
| 668 |
}
|
| 669 |
return $assoc_presets;
|
| 670 |
}
|
| 671 |
$presets = unserialize(cache_get('cdn2_presets')->data);
|
| 672 |
if (!$presets) {
|
| 673 |
$cdn2 = _cdn2_get_soap_client();
|
| 674 |
if (!$cdn2) {
|
| 675 |
return NULL;
|
| 676 |
}
|
| 677 |
$presets = $cdn2->getFormatPresets();
|
| 678 |
if ($presets['error']) {
|
| 679 |
drupal_set_message('Error occurred: '. $presets['error'], 'error');
|
| 680 |
return NULL;
|
| 681 |
}
|
| 682 |
}
|
| 683 |
$assoc_presets = array();
|
| 684 |
foreach ($presets as $preset) {
|
| 685 |
$assoc_presets[$preset->shortName] = $preset;
|
| 686 |
}
|
| 687 |
cache_set('cdn2_presets', 'cache', serialize($assoc_presets), time() + 86400);
|
| 688 |
if ($account) {
|
| 689 |
return _cdn2_filter_presets_by_user($account, $assoc_presets, $show_all);
|
| 690 |
}
|
| 691 |
return $assoc_presets;
|
| 692 |
}
|
| 693 |
|
| 694 |
function _cdn2_content_types() {
|
| 695 |
$type_list = array(0 => 'None');
|
| 696 |
$result = db_query("SELECT distinct(type_name) as name FROM {node_field_instance} ORDER BY type_name");
|
| 697 |
while ($row = db_fetch_object($result)) {
|
| 698 |
$type_list[$row->name] = $row->name;
|
| 699 |
}
|
| 700 |
return $type_list;
|
| 701 |
}
|
| 702 |
|
| 703 |
function _cdn2_cck_fields() {
|
| 704 |
$field_list = array(0 => 'None');
|
| 705 |
$result = db_query("SELECT distinct(field_name) as name, label FROM {node_field_instance} where type_name = '%s' ORDER BY label", variable_get('cdn2_content_type', ''));
|
| 706 |
while ($row = db_fetch_object($result)) {
|
| 707 |
$field_list[$row->name] = $row->label;
|
| 708 |
}
|
| 709 |
return $field_list;
|
| 710 |
}
|
| 711 |
|
| 712 |
function cdn2_upload_form($token=0, $messages = '') {
|
| 713 |
global $user;
|
| 714 |
$redirect_url = url('cdn2/complete', NULL, NULL, TRUE);
|
| 715 |
$action = variable_get('cdn2_uploader_form', CDN2_DEFAULT_FORM);
|
| 716 |
$jquery = '/misc/jquery.js';
|
| 717 |
$jquery_progressbar = '/'. drupal_get_path('module', 'cdn2') .'/js/jquery.progressbar.min.js';
|
| 718 |
$our_jquery = '/'. drupal_get_path('module', 'cdn2') .'/js/upload.js';
|
| 719 |
$style_css = "/". path_to_theme() ."/style.css";
|
| 720 |
$cdn2_progress_image = '/'. drupal_get_path('module', 'cdn2') .'/images/progressbg_blue.gif';
|
| 721 |
$cdn2_progress_boximage = '/'. drupal_get_path('module', 'cdn2') .'/images/progressbar.gif';
|
| 722 |
|
| 723 |
$presets = _cdn2_get_available_presets($user);
|
| 724 |
$preset_inputs = '';
|
| 725 |
$i = 0;
|
| 726 |
foreach ($presets as $short_name => $preset) {
|
| 727 |
# Note: each input name here must be prefixed by 'selectedPresets' to be properly processed by the backend service
|
| 728 |
# If the backend service does not properly parse these args, it will not approve the selected formats for
|
| 729 |
# processing, and no transcoding will occur.
|
| 730 |
$inputName = "selectedPresets".$i;
|
| 731 |
$preset_inputs .= sprintf('<input type="checkbox" name="%s" value="%s" checked/>%s<br/>', $inputName, $short_name, $preset->friendlyName);
|
| 732 |
$i++;
|
| 733 |
}
|
| 734 |
if ($messages) {
|
| 735 |
$messages = '<div class="error">'. $messages .'</div>'."\n";
|
| 736 |
}
|
| 737 |
$output = <<<HTML
|
| 738 |
<html class="js" lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
| 739 |
<head>
|
| 740 |
<title></title>
|
| 741 |
<style type="text/css" media="all">@import "/modules/node/node.css";</style>
|
| 742 |
<style type="text/css" media="all">@import "/modules/system/defaults.css";</style>
|
| 743 |
<style type="text/css" media="all">@import "/modules/system/system.css";</style>
|
| 744 |
<style type="text/css" media="all">@import "$style_css";</style>
|
| 745 |
<style>
|
| 746 |
#progress-bar {
|
| 747 |
display: none;
|
| 748 |
}
|
| 749 |
#progress-info {
|
| 750 |
display: none;
|
| 751 |
}
|
| 752 |
body {
|
| 753 |
background: transparent;
|
| 754 |
}
|
| 755 |
</style>
|
| 756 |
<script language="javascript" type="text/javascript">
|
| 757 |
var cdn2_progress_image = '$cdn2_progress_image';
|
| 758 |
var cdn2_progress_boximage = '$cdn2_progress_boximage';
|
| 759 |
</script>
|
| 760 |
<script type="text/javascript" src="$jquery"></script>
|
| 761 |
<script type="text/javascript" src="$jquery_progressbar"></script>
|
| 762 |
<script type="text/javascript" src="$our_jquery"></script>
|
| 763 |
</head>
|
| 764 |
<body>
|
| 765 |
$messages
|
| 766 |
<form id="upload" action="$action" method="post" enctype="multipart/form-data">
|
| 767 |
<div class="form-item">
|
| 768 |
<p><label>Upload a video: </label>
|
| 769 |
<input type="hidden" name="videoToken" id="videoToken" value="$token"/>
|
| 770 |
<input type="hidden" name="redirectUrl" value="$redirect_url"/>
|
| 771 |
<input type="file" name="file"/></p>
|
| 772 |
<p>
|
| 773 |
<label>Show this file in the following formats:</label>
|
| 774 |
$preset_inputs
|
| 775 |
</p>
|
| 776 |
<input type="submit" name="Send" value="Upload"/>
|
| 777 |
</div>
|
| 778 |
</form>
|
| 779 |
<div id="progress-info"><p>Your video is uploading. Please do not submit your form or navigate away from this page until it is complete.</p>
|
| 780 |
<b>Upload Progress:</b>
|
| 781 |
</div>
|
| 782 |
<div id="progress-bar"></div>
|
| 783 |
</body>
|
| 784 |
</html>
|
| 785 |
HTML;
|
| 786 |
|
| 787 |
print $output;
|
| 788 |
}
|
| 789 |
|
| 790 |
/**
|
| 791 |
* shows the upload form in an iframe
|
| 792 |
*/
|
| 793 |
function cdn2_upload_complete($video_token = '') {
|
| 794 |
$error = $_REQUEST['e'];
|
| 795 |
$video_token = check_plain($video_token);
|
| 796 |
if ($error) {
|
| 797 |
// show the upload form again, with the error inline.
|
| 798 |
//
|
| 799 |
print cdn2_upload_form($video_token, $error);
|
| 800 |
return;
|
| 801 |
}
|
| 802 |
|
| 803 |
// save the video token in the database. Note that it will not yet be associated with a node. That's okay.
|
| 804 |
//
|
| 805 |
$vid = db_next_id("cdn2_video_vid");
|
| 806 |
|
| 807 |
$query = "INSERT INTO {cdn2_video_node} (vid, nid, video_token, is_submitted) values(%d, 0, '%s', %d)";
|
| 808 |
db_query($query, $vid, $video_token, 1);
|
| 809 |
|
| 810 |
$jquery = '/misc/jquery.js';
|
| 811 |
$style_css = "/". path_to_theme() ."/style.css";
|
| 812 |
$output = <<<HTML
|
| 813 |
<html class="js" lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
| 814 |
<head>
|
| 815 |
<title></title>
|
| 816 |
<style type="text/css" media="all">@import "/modules/node/node.css";</style>
|
| 817 |
<style type="text/css" media="all">@import "/modules/system/defaults.css";</style>
|
| 818 |
<style type="text/css" media="all">@import "/modules/system/system.css";</style>
|
| 819 |
<style type="text/css" media="all">@import "$style_css";</style>
|
| 820 |
<script type="text/javascript" src="$jquery"></script>
|
| 821 |
<p>Your video has been uploaded. It can take some time to process, so be patient.</p>
|
| 822 |
|
| 823 |
<p>Please make sure you <b>submit</b> this page before continuing, otherwise your video will be lost.</p>
|
| 824 |
<script language="javascript">
|
| 825 |
$(document).ready(function(){
|
| 826 |
$(window.parent.document).find('input#edit-cdn2-is-submitted').val('true');
|
| 827 |
$(window.parent.document).find('input#edit-submit').removeAttr('disabled');
|
| 828 |
return;
|
| 829 |
});
|
| 830 |
</script>
|
| 831 |
</body>
|
| 832 |
</html>
|
| 833 |
HTML;
|
| 834 |
|
| 835 |
print $output;
|
| 836 |
}
|
| 837 |
|
| 838 |
function cdn2_get_upload_status($vidid) {
|
| 839 |
$cdn2 = _cdn2_get_soap_client();
|
| 840 |
// this PRINT_R is NOT FOR DEBUGGING. Don't remove it, mmkay?
|
| 841 |
print_r($cdn2->getVideoUploadStatus($vidid));
|
| 842 |
}
|
| 843 |
|
| 844 |
function _cdn2_get_soap_client() {
|
| 845 |
include_once('Crypt/HMAC.php');
|
| 846 |
if (!class_exists('Crypt_HMAC')) {
|
| 847 |
drupal_set_message('PEAR Package Crypt_HMAC is required to use the CDN2 module.', 'error');
|
| 848 |
return;
|
| 849 |
}
|
| 850 |
require_once('lib/libcdn2.inc');
|
| 851 |
|
| 852 |
$site_token = variable_get('cdn2_client_id', 'workhabit');
|
| 853 |
$secret_key = variable_get('cdn2_secret_key', '');
|
| 854 |
$endpoint_url = variable_get('cdn2_uploader_endpoint', CDN2_DEFAULT_ENDPOINT);
|
| 855 |
try {
|
| 856 |
return new ErrorHandlingSoapClient(CDN2SoapClientImpl::getInstance($endpoint_url, $site_token, $secret_key));
|
| 857 |
} catch (Exception $e) {
|
| 858 |
drupal_set_message($e->getMessage(), 'error');
|
| 859 |
watchdog('error', $e->getMessage());
|
| 860 |
}
|
| 861 |
return NULL;
|
| 862 |
}
|
| 863 |
|
| 864 |
|
| 865 |
function _cdn2_get_content_types() {
|
| 866 |
$widget_info = cdn2_widget_info();
|
| 867 |
$content_types = array();
|
| 868 |
foreach ($widget_info as $key => $value) {
|
| 869 |
$query = "SELECT type_name FROM {node_field_instance} WHERE widget_type='%s'";
|
| 870 |
$result = db_query($query, $key);
|
| 871 |
while ($row = db_fetch_object($result)) {
|
| 872 |
$content_types[] = $row->type_name;
|
| 873 |
}
|
| 874 |
}
|
| 875 |
return $content_types;
|
| 876 |
}
|
| 877 |
|
| 878 |
function _cdn2_get_field_names_by_content_type($content_type) {
|
| 879 |
$query = "SELECT field_name FROM {node_field_instance} WHERE type_name='%s'";
|
| 880 |
$result = db_query($query, $content_type);
|
| 881 |
while ($row = db_fetch_object($result)) {
|
| 882 |
$field_names[] = $row->field_name;
|
| 883 |
}
|
| 884 |
return $field_names;
|
| 885 |
}
|