Newer
Older
<?php
/**
* @file
* Adds a media recorder widget and add form to the media module.
*/
/**
* Implements hook_libraries_info().
*/
function media_recorder_libraries_info() {
'name' => 'SWFObject',
'description' => 'SWFObject is an easy-to-use and standards-friendly method to embed Flash content, which utilizes one small JavaScript file.',
'vendor url' => 'http://code.google.com/p/swfobject',
'download url' => 'http://swfobject.googlecode.com/files/swfobject_2_2.zip',
'version arguments' => array(
'file' => 'swfobject.js',
'pattern' => '@v([0-9a-zA-Z\.-]+)@',
),
'files' => array(
'js' => array(
'swfobject.js' => array(
'type' => 'file',
'scope' => 'header',
'group' => JS_LIBRARY,
),
),
$libraries['recorder.js'] = array(
'name' => 'recorder.js',
'description' => 'JavaScript library to record audio in browsers as used in the SoundCloud Javascript SDK.',
'vendor url' => 'https://github.com/jwagener/recorder.js',
'download url' => 'https://github.com/jwagener/recorder.js/zipball/master',
'version arguments' => array(
'file' => 'recorder.js',
'pattern' => '@version: ([0-9a-zA-Z\.-]+),@',
),
'dependencies' => array(
),
'files' => array(
'js' => array('recorder.js'),
),
);
$libraries['Recorderjs'] = array(
'name' => 'Recorderjs',
'description' => 'A plugin for recording/exporting the output of Web Audio API nodes',
'vendor url' => 'https://github.com/mattdiamond/Recorderjs',
'download url' => 'https://github.com/mattdiamond/Recorderjs/zipball/master',
'files' => array(
'js' => array('recorder.js'),
return $libraries;
}
/**
* Implements hook_menu().
*/
function media_recorder_menu() {
kenianbei
committed
$items = array();
'title' => 'Record',
'description' => 'Add recordings to your media library.',
'page callback' => 'drupal_get_form',
'page arguments' => array('media_recorder_add'),
'access callback' => 'file_entity_access',
'access arguments' => array('create'),
kenianbei
committed
$items['media_recorder/record/file'] = array(
'title' => 'Record',
'description' => 'Record a video or audio file.',
kenianbei
committed
'page callback' => 'media_recorder_record_file',
'access callback' => 'file_entity_access',
'access arguments' => array('create'),
'type' => MENU_CALLBACK,
);
$items['media_recorder/record/stream/start'] = array(
'title' => 'Record',
'description' => 'Record a video or audio file as a stream.',
'page callback' => 'media_recorder_record_stream_start',
'access callback' => 'file_entity_access',
'access arguments' => array('create'),
'type' => MENU_CALLBACK,
);
$items['media_recorder/record/stream/record'] = array(
'title' => 'Record',
'description' => 'Record a video or audio file as a stream.',
'page callback' => 'media_recorder_record_stream_record',
'access callback' => 'file_entity_access',
'access arguments' => array('create'),
'type' => MENU_CALLBACK,
);
$items['media_recorder/record/stream/finish'] = array(
'title' => 'Record',
'description' => 'Record a video or audio file as a stream.',
'page callback' => 'media_recorder_record_stream_finish',
'access callback' => 'file_entity_access',
'access arguments' => array('create'),
kenianbei
committed
$items['admin/config/media/mediarecorder'] = array(
kenianbei
committed
'title' => 'Media recorder',
kenianbei
committed
'description' => 'Configure the media recorder.',
'page callback' => 'drupal_get_form',
'page arguments' => array('media_recorder_admin_form'),
'access arguments' => array('administer files'),
'file' => 'includes/media_recorder.admin.inc',
/**
* Implements hook_help().
*/
function media_recorder_help($path, $arg) {
if ($path == 'admin/help#media_recorder') {
$output = file_get_contents(drupal_get_path('module', 'media_recorder') . '/README.txt');
return nl2br($output);
}
}
kenianbei
committed
/**
* Implements hook_theme().
*/
function media_recorder_theme($existing, $type, $theme, $path) {
return array(
'media_recorder' => array(
'variables' => array(),
'template' => 'media-recorder',
'path' => drupal_get_path('module', 'media_recorder') . '/theme',
),
);
}
/**
* Menu callback for recording a media file.
*/
kenianbei
committed
function media_recorder_record_stream_start() {
// Set session status as open.
$_SESSION['media_recorder']['status'] = TRUE;
kenianbei
committed
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
// Delete the file if for some reason file_unmanaged_move was not successful.
if (file_exists($_SESSION['media_recorder']['tempnam'])) {
unlink($_SESSION['media_recorder']['tempnam']);
}
// Reset temporary file name in case this wasn't reset after stream finished.
unset($_SESSION['media_recorder']['tempnam']);
// Reset file format.
unset($_SESSION['media_recorder']['format']);
// Create a new temporary file to save streamed data.
try {
$_SESSION['media_recorder']['tempnam'] = drupal_tempnam('temporary://', 'mediaRecorder_');
if (!$_SESSION['media_recorder']['tempnam']) {
throw new Exception("Unable to create temporary file.");
}
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
exit;
}
// Get file format.
try {
$_SESSION['media_recorder']['format'] = $_POST['format'];
if (!$_SESSION['media_recorder']['format']) {
throw new Exception("Unable to get file format.");
}
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
exit;
}
}
/**
* Menu callback for recording a media file.
*/
function media_recorder_record_stream_record() {
// Return if there is no streaming session open.
if (!$_SESSION['media_recorder']['status']) {
return;
}
// Get file blob url.
$url = isset($_FILES['mediaRecorder']['tmp_name']) ? $_FILES['mediaRecorder']['tmp_name'] : '';
// Append streaming data to temporary file while recording is not finished.
if (!empty($url)) {
try {
$data = file_get_contents($url);
if (!$data) {
throw new Exception("Streaming data file is empty.");
}
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
echo drupal_json_output($url);
exit;
}
try {
$fp = fopen($_SESSION['media_recorder']['tempnam'], 'a');
if (!$fp) {
throw new Exception("Unable to open temporary file. Please check that your file permissions are set correctly.");
}
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
exit;
}
if ($data && $fp) {
try {
fwrite($fp, $data);
fclose($fp);
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
exit;
}
}
echo drupal_json_output($_FILES['mediaRecorder']);
}
else {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output('Temporary file not found.');
echo drupal_json_output($url);
exit;
}
}
/**
* Menu callback for recording a media file.
*/
function media_recorder_record_stream_finish() {
// Check that file exists.
if (file_exists($_SESSION['media_recorder']['tempnam'])) {
// Change the file name.
file_unmanaged_move($_SESSION['media_recorder']['tempnam'], $_SESSION['media_recorder']['tempnam'] . '.' . $_SESSION['media_recorder']['format']);
$file = file_uri_to_object($_SESSION['media_recorder']['tempnam'] . '.' . $_SESSION['media_recorder']['format']);
$file->status = 0;
file_save($file);
// Delete the file if for some reason file_unmanaged_move was not successful.
if (file_exists($_SESSION['media_recorder']['tempnam'])) {
unlink($_SESSION['media_recorder']['tempnam']);
}
$_SESSION['media_recorder']['status'] = FALSE;
// Return file information.
echo drupal_json_output($file);
}
else {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output(t('File is empty.'));
exit;
}
}
/**
* Menu callback for recording a media file.
*/
function media_recorder_record_file() {
// Get file blob url.
$url = isset($_FILES['mediaRecorder']['tmp_name']) ? $_FILES['mediaRecorder']['tmp_name'] : '';
// Attempt to save file data.
if (!empty($url)) {
// Create a new temporary file to save data.
try {
$uri = drupal_tempnam('temporary://', 'mediaRecorder_');
if (!$uri) {
throw new Exception("Unable to create temporary file.");
}
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
exit;
}
try {
$data = file_get_contents($url);
if (!$data) {
throw new Exception("There was no data sent.");
}
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
echo drupal_json_output($url);
exit;
}
try {
$fp = fopen($uri, 'a');
if (!$fp) {
throw new Exception("Unable to open temporary file. Please check that your file permissions are set correctly.");
}
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
exit;
}
if ($data && $fp) {
try {
fwrite($fp, $data);
fclose($fp);
}
catch (Exception $e) {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output($e->getMessage());
exit;
}
}
kenianbei
committed
}
kenianbei
committed
else {
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output('No file was sent.');
exit;
}
// Check that file exists.
if (file_exists($uri)) {
file_unmanaged_move($uri, $uri . '.wav');
kenianbei
committed
$file = file_uri_to_object($uri . '.wav');
$file->status = 0;
file_save($file);
kenianbei
committed
// Delete the file if for some reason file_unmanaged_move was not successful.
if (file_exists($uri)) {
unlink($uri);
}
// Return file information.
echo drupal_json_output($file);
}
kenianbei
committed
kenianbei
committed
header('HTTP/1.0 419 Custom Error');
echo drupal_json_output(t('There was an error saving the file.'));
exit;
/**
* Implements hook_media_browser_plugin_info().
*/
function media_recorder_media_browser_plugin_info() {
$info['media_recorder'] = array(
'title' => t('Recorder'),
'class' => 'MediaRecorderBrowser',
);
return $info;
}
* Provides a form for adding media items using the media recorder.
kenianbei
committed
function media_recorder_add($form, &$form_state) {
// Set field variables.
$field_name = 'field_media_recorder';
$langcode = LANGUAGE_NONE;
$items = array();
$delta = 0;
$field = array(
'field_name' => $field_name,
'cardinality' => 1,
'settings' => array(
'uri_scheme' => 'public',
'display_default' => 0,
),
);
kenianbei
committed
$instance = array(
'settings' => array(
'file_directory' => variable_get('media_recorder_upload_directory', ''),
kenianbei
committed
'file_extensions' => 'wav mp3 m4a mov m4v mp4 mpeg mpg avi ogg oga ogv webm',
kenianbei
committed
),
'widget' => array(
'settings' => array(
'progress_indicator' => 'throbber',
),
),
kenianbei
committed
);
$element['#field_name'] = $field_name;
$element['#language'] = $langcode;
$element['#delta'] = $delta;
$element['#id'] = 'edit-field-media-recorder-und-0';
kenianbei
committed
$element['#attributes']['class'][] = 'field-widget-media-recorder';
kenianbei
committed
$element['#field_parents'] = array();
$element['#columns'] = array('fid', 'display', 'description');
$element['#title'] = t('Media Recorder');
$element['#description'] = '';
kenianbei
committed
$element['#required'] = TRUE;
$element['#upload_location'] = 'public://' . variable_get('media_recorder_upload_directory', '');
kenianbei
committed
$element['#upload_validators']['file_validate_extensions'][] = 'wav mp3 m4a mov m4v mp4 mpeg mpg avi ogg oga ogv webm';
kenianbei
committed
$form['#title'] = t('Title');
kenianbei
committed
// Add title field.
$form['title']['#type'] = 'textfield';
$form['title']['#title'] = t('Title');
$form['title']['#required'] = TRUE;
kenianbei
committed
// Add media recorder element.
$form[$field_name]['#type'] = 'container';
$form[$field_name]['#tree'] = TRUE;
$form[$field_name][$langcode] = media_recorder_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
// Remove file field widget process, since this is not an actual field widget.
$key = array_search('file_field_widget_process', $form[$field_name][$langcode][0]['#process']);
unset($form[$field_name][$langcode][0]['#process'][$key]);
// Add media browser javascript and CSS.
drupal_add_js(drupal_get_path('module', 'media_recorder') . '/js/media-recorder.browser.js');
kenianbei
committed
kenianbei
committed
// Add a submit button.
$form['actions']['submit']['#type'] = 'submit';
$form['actions']['submit']['#value'] = t('Save Recording');
kenianbei
committed
return $form;
kenianbei
committed
}
/**
* Submit handler for media_recorder_add form.
*/
function media_recorder_add_submit($form, &$form_state) {
$fid = $form_state['values']['field_media_recorder'][LANGUAGE_NONE][0]['fid'];
if ($fid != 0) {
$file = file_load($fid);
if (!$file->status) {
$file->status = FILE_STATUS_PERMANENT;
$file = file_save($file);
}
drupal_set_message(t('The file <em>!filename</em> was successfully loaded.', array('!filename' => l(check_plain($file->filename), 'file/' . $file->fid))), 'status');
}
else {
drupal_set_message(t('An unrecoverable error occurred. Try reloading the page and submitting again.'), 'error');
}
}
/**
* Implements hook_field_widget_info().
*/
function media_recorder_field_widget_info() {
return array(
'media_recorder' => array(
'label' => t('Media Recorder'),
'field types' => array('file'),
'settings' => array(
'progress_indicator' => 'throbber',
'allowed_schemes' => array('public', 'private'),
),
'behaviors' => array(
'multiple values' => FIELD_BEHAVIOR_CUSTOM,
'default value' => FIELD_BEHAVIOR_NONE,
),
),
);
}
/**
* Implements hook_field_widget_settings_form().
*/
function media_recorder_field_widget_settings_form($field, $instance) {
// Get default file widget settings form.
$form = file_field_widget_settings_form($field, $instance);
return $form;
}
/**
* Implements hook_field_widget_form().
*/
function media_recorder_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
kenianbei
committed
// Get default file widget form.
$elements = file_field_widget_form($form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
kenianbei
committed
// Alter process callbacks.
foreach (element_children($elements) as $delta) {
$elements[$delta]['#process'][] = 'media_recorder_field_widget_form_process';
kenianbei
committed
return $elements;
}
kenianbei
committed
/**
* An element #process callback.
* @see media_recorder_field_widget_form()
*/
function media_recorder_field_widget_form_process($element, &$form_state, $form) {
kenianbei
committed
kenianbei
committed
// Alter file elements.
$element['fid']['#attributes']['class'][] = 'media-recorder-fid';
$element['upload']['#attributes']['class'][] = 'media-recorder-upload';
$element['upload_button']['#attributes']['class'][] = 'media-recorder-upload-button';
$element['remove_button']['#attributes']['class'][] = 'media-recorder-remove-button';
$element['remove_button']['#weight'] = 100;
kenianbei
committed
// Add validation handler to beginning of validation handler stack.
array_unshift($element['#element_validate'], 'media_recorder_field_widget_form_process_record_validate');
kenianbei
committed
// Add media recorder.
$element['record'] = array(
'#theme' => 'media_recorder',
'#title' => 'Record',
);
kenianbei
committed
// Add hidden refresh submit, which is triggered on record finish.
// This will rebuild the form with new file preview.
$element['refresh'] = array(
'#type' => 'submit',
'#executes_submit_callback' => FALSE,
kenianbei
committed
'#limit_validation_errors' => array(),
'#value' => t('Refresh'),
'#ajax' => array(
'callback' => 'media_recorder_field_widget_form_process_ajax_refresh',
kenianbei
committed
'wrapper' => $element['#id'] . '-file-preview-ajax-wrapper',
),
'#attributes' => array(
kenianbei
committed
'style' => 'display: none;',
'class' => array('media-recorder-refresh'),
),
);
kenianbei
committed
// Add file preview to render array.
$element['preview'] = array(
'#type' => 'container',
'#weight' => 50,
'#attributes' => array(
'class' => array('media-recorder-file-preview'),
kenianbei
committed
),
kenianbei
committed
'#id' => $element['#id'] . '-file-preview-ajax-wrapper',
kenianbei
committed
);
kenianbei
committed
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
// Only display file if fid exists.
$fid = isset($element['#value']['fid']) ? $element['#value']['fid'] : (isset($form_state['values'][$field_name][$langcode][$delta]['fid']) ? $form_state['values'][$field_name][$langcode][$delta]['fid'] : 0);
if ($fid) {
$file = file_load($fid);
$element['preview']['file'] = file_view($file);
$element['preview']['#type'] = 'fieldset';
$element['preview']['#title'] = 'File Preview';
}
// Add attached files and settings.
$element['#attached'] = array(
'libraries_load' => array(
array('swfobject'),
),
'js' => array(
array(
'type' => 'file',
'data' => drupal_get_path('module', 'media_recorder') . '/js/media-recorder.js',
'scope' => 'footer',
),
array(
'type' => 'setting',
'data' => array(
'mediaRecorder' => array(
'swfurl' => libraries_get_path('recorder.js'),
'html5url' => libraries_get_path('Recorderjs'),
'modulePath' => drupal_get_path('module', 'media_recorder') . '/js',
),
),
'scope' => 'header',
),
),
'css' => array(
array(
'type' => 'file',
'data' => drupal_get_path('module', 'media_recorder') . '/css/media-recorder.css',
),
),
);
kenianbei
committed
return $element;
* Ajax callback for form element rebuild.
* @see media_recorder_field_widget_form_process()
function media_recorder_field_widget_form_process_ajax_refresh($form, &$form_state) {
kenianbei
committed
// Rebuild the form.
$form_state['rebuild'] = TRUE;
kenianbei
committed
// Get the file field element.
$parents = $form_state['triggering_element']['#array_parents'];
array_pop($parents);
$element = drupal_array_get_nested_value($form, $parents);
kenianbei
committed
kenianbei
committed
return $element['preview'];
kenianbei
committed
}
/**
* Custom validation callback.
* @see media_recorder_field_widget_form_process()
kenianbei
committed
*/
function media_recorder_field_widget_form_process_record_validate(&$element, &$form_state) {
kenianbei
committed
// Get field information.
$field_name = $element['#parents'][0];
$langcode = $element['#parents'][1];
$delta = isset($element['#parents'][2]) ? $element['#parents'][2] : 0;
// Get the fid.
$fid = !empty($form_state['values'][$field_name][$langcode][$delta]['fid']) ? $form_state['values'][$field_name][$langcode][$delta]['fid'] : 0;
// Load file from fid.
if ($fid != 0) {
kenianbei
committed
$file = file_load($fid);
}
// Grab title from entity if available.
$title = '';
kenianbei
committed
if (isset($form_state['values']['title']) && !empty($form_state['values']['title'])) {
$title = $form_state['values']['title'];
}
// Add custom logic for comments.
elseif (isset($form_state['comment']) && is_object($form_state['comment'])) {
// Use the comment subject value if present.
if (isset($form_state['values']['subject']) && !empty($form_state['values']['subject'])) {
$title = $form_state['values']['subject'];
}
// Otherwise use the node title.
elseif (isset($form_state['comment']->nid) && is_numeric($form_state['comment']->nid)) {
$node = node_load($form_state['values']['nid']);
$title = t('Comment on @title', array('@title' => $node->title));
}
kenianbei
committed
}
// Process file.
if (isset($file->fid)) {
kenianbei
committed
// Validate file extensions, since managed file validates client side.
kenianbei
committed
$file->filename = drupal_basename($file->uri);
$file_validate_size_errors = file_validate($file, $element['#upload_validators']);
if (empty($file_validate_size_errors)) {
// Prepare directory.
if (module_exists('token')) {
$upload_location = token_replace($element['#upload_location']);
kenianbei
committed
}
else {
$upload_location = $element['#upload_location'];
kenianbei
committed
}
if (file_prepare_directory($upload_location, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
// Add new title.
if (!empty($title)) {
$file->filename = trim($title);
}
// Move the file to the field's upload location.
$file = file_move($file, $upload_location);
// Set file field input & values.
drupal_array_set_nested_value(
$form_state['input'],
$element['#parents'],
array(
'fid' => $file->fid,
'display' => TRUE,
);
drupal_array_set_nested_value(
$form_state['values'],
$element['#parents'],
array(
'fid' => $file->fid,
'display' => TRUE,
kenianbei
committed
// Report file validation errors.
else {
form_set_error('media_recorder', implode('<br />', $file_validate_size_errors));
return;
}