Projet

Général

Profil

Paste
Télécharger (11,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / media_youtube / media_youtube.module @ 87dbc3bf

1
<?php
2

    
3
/**
4
 * @file media_youtube/media_youtube.module
5
 *
6
 * Media: YouTube provides a stream wrapper and formatters for videos provided
7
 * by YouTube, available at http://youtube.com/.
8
 *
9
 * @TODO:
10
 * Get the YouTube tab working.
11
 *  - Search by video name, user name, and channel.
12
 *  - Default to user's channel videos.
13
 * Review all code and remove cruft.
14
 * Assess if M:YT 3.x could be formatters added on to the OEmbed module.
15
 */
16

    
17
/**
18
 * This is the rest point for the YouTube api.
19
 *
20
 * Avoid using the gdata api url when possible. Too many calls will result in
21
 * throttling and 403 errors.
22
 */
23
define('MEDIA_YOUTUBE_REST_API', 'https://gdata.youtube.com/feeds/api/videos');
24

    
25
// Hooks and callbacks for integrating with File Entity module for display.
26
require_once dirname(__FILE__) . '/includes/media_youtube.formatters.inc';
27

    
28
/**
29
 * Implements hook_media_internet_providers().
30
 */
31
function media_youtube_media_internet_providers() {
32
  $info['MediaInternetYouTubeHandler'] = array(
33
    'title' => t('YouTube'),
34
  );
35

    
36
  return $info;
37
}
38

    
39
/**
40
 * Implements hook_stream_wrappers().
41
 */
42
function media_youtube_stream_wrappers() {
43
  return array(
44
    'youtube' => array(
45
      'name' => t('YouTube videos'),
46
      'class' => 'MediaYouTubeStreamWrapper',
47
      'description' => t('Videos provided by YouTube.'),
48
      'type' => STREAM_WRAPPERS_READ_VISIBLE,
49
    ),
50
  );
51
}
52

    
53
/**
54
 * Implements hook_theme().
55
 */
56
function media_youtube_theme($existing, $type, $theme, $path) {
57
  return array(
58
    'media_youtube_video' => array(
59
      'variables' => array('uri' => NULL, 'options' => array()),
60
      'file' => 'media_youtube.theme.inc',
61
      'path' => $path . '/themes',
62
      'template' => 'media-youtube-video',
63
    ),
64
  );
65
}
66

    
67
/**
68
 * Implements hook_media_parse().
69
 *
70
 * @todo This hook should be deprecated. Refactor Media module to not call it
71
 * any more, since media_internet should be able to automatically route to the
72
 * appropriate handler.
73
 */
74
function media_youtube_media_parse($embed_code) {
75
  $handler = new MediaInternetYouTubeHandler($embed_code);
76
  return $handler->parse($embed_code);
77
}
78

    
79
/**
80
 * Implements hook_file_mimetype_mapping_alter().
81
 *
82
 * Register the video/youtube mimetype.
83
 */
84
function media_youtube_file_mimetype_mapping_alter(&$mapping) {
85
  $mapping['mimetypes'][] = 'video/youtube';
86
}
87

    
88
/**
89
 * YouTube search tab for the Media browser.
90
 */
91

    
92
/**
93
 * Implements hook_media_browser_plugin_info().
94
 *
95
 * Commented out for release versions, active in dev versions. To enable the
96
 * YouTube media browser tab, uncomment this function.
97
 */
98
function media_youtube_media_browser_plugin_info() {
99
  $info['youtube'] = array(
100
    'title' => t('YouTube'),
101
    'class' => 'MediaYouTubeBrowser',
102
  );
103

    
104
  return $info;
105
}
106

    
107
/**
108
 * Provides a form for adding media items from YouTube search.
109
 */
110
function media_youtube_add($form, &$form_state = array()) {
111
  module_load_include('inc', 'media', 'includes/media.browser');
112

    
113
  // Our search term can come from the form, or from the pager.
114
  $term = isset($form_state['input']['search']) ? $form_state['input']['search'] : (isset($_GET['search']) ? $_GET['search'] : '');
115

    
116
  $form['search'] = array(
117
    '#type' => 'textfield',
118
    '#title' => t('Search'),
119
    '#description' => t('Input a phrase or tags to search.'),
120
    '#default_value' => $term,
121
  );
122
  $form['apply'] = array(
123
    '#type' => 'button',
124
    '#value' => t('Apply'),
125
  );
126

    
127
  // This is our half-assed pager.
128
  $page = isset($_GET['page-yt']) ? $_GET['page-yt'] : 0;
129
  if (isset($form_state['input']['search'])) {
130
    // Reset the pager when we press apply.
131
    $page = 0;
132
  }
133
  if (!empty($term)) {
134
    $search = media_youtube_video_search(array('q' => $term, 'max-results' => 12, 'start-index' => $page * 12 + 1));
135
  }
136
  $form['videos']['#prefix'] = '<div id="container"><div id="scrollbox"><ul id="media-browser-library-list" class="media-list-thumbnails">';
137
  $form['videos']['#suffix'] = '</ul><div id="status"></div></div></div>';
138

    
139
  $empty = FALSE;
140
  $files = array();
141
  if (!isset($search['entry'])) {
142
    $empty = TRUE;
143
  }
144
  else {
145
    foreach ($search['entry'] as $video) {
146
      try {
147
        $uri = media_parse_to_uri($video['link'][0]['@attributes']['href']);
148
      }
149
      catch (Exception $e) {
150
        // Ignore invalid videos.
151
        continue;
152
      }
153
      // Create a temporary file object for our retrieved video.
154
      $file = file_uri_to_object($uri);
155
      $file->type = 'video';
156
      if (!isset($file->fid)) {
157
        $file->fid = 0;
158
      }
159
      media_browser_build_media_item($file);
160
      $file->preview = l($file->preview, 'media/browser', array(
161
        'html' => TRUE,
162
        'attributes' => array(
163
          'data-uri' => $uri,
164
        ),
165
        'query' => array('render' => 'media-popup', 'uri' => $uri),
166
      ));
167
      $form['videos'][$uri] = array(
168
        '#markup' => $file->preview,
169
        '#prefix' => '<li>',
170
        '#suffix' => '</li>',
171
      );
172
      $files[$uri] = $file;
173
    }
174
  }
175

    
176
  if (!count($files)) {
177
    $empty= TRUE;
178
  }
179
  if ($empty) {
180
    $form['empty'] = array(
181
      '#markup' => '<div class="empty-message">' . t('No videos match your search criteria. Please try again.') . '</div>',
182
    );
183
  }
184

    
185
  $query = $_GET;
186
  if ($term !== '') {
187
    $query['search'] = $term;
188
  }
189

    
190
  $dest = $query['q'];
191
  unset($query['q']);
192
  $prev = $next = '';
193
  if ($page) {
194
    $query['page-yt'] = $page - 1;
195
    $prev = l(t('previous'), $dest, array('query' => $query));
196
  }
197
  $query['page-yt'] = $page + 1;
198
  if (!$empty) {
199
    $next = l(t('next'), $dest, array('query' => $query));
200
  }
201

    
202
  $form['pager']= array(
203
    '#markup' => $prev . ' ' . $next,
204
  );
205

    
206
  $form['submitted-video'] = array(
207
    '#type' => 'hidden',
208
    '#default_value' => FALSE,
209
  );
210

    
211
  // Add the files to JS so that they are accessible inside the browser
212
  drupal_add_js(array('media' => array('files' => $files)), 'setting');
213

    
214
  // Add media browser javascript and CSS.
215
  drupal_add_js(drupal_get_path('module', 'media_youtube') . '/js/media-youtube.browser.js');
216

    
217
  // @TODO: Remove deprecated library js and css. They're removed in Media,
218
  // so let's comment out for now.
219
  // drupal_add_js(drupal_get_path('module', 'media') . '/js/plugins/media.library.js');
220
  // drupal_add_css(drupal_get_path('module', 'media') . '/js/plugins/media.library.css');
221

    
222
  $form['actions'] = array('#type' => 'actions');
223
  $form['actions']['submit'] = array(
224
    '#type' => 'submit',
225
    '#value' => t('Submit'),
226
  );
227
  return $form;
228
}
229

    
230
/**
231
 * Allow stream wrappers to have their chance at validation.
232
 *
233
 * Any module that implements hook_media_parse will have an
234
 * opportunity to validate this.
235
 *
236
 * @see media_parse_to_uri()
237
 */
238
function media_youtube_add_validate($form, &$form_state) {
239
  if ($form_state['values']['op'] == t('Apply')) {
240
    return;
241
  }
242
  $uri = $form_state['values']['submitted-video'];
243
  try {
244
    $file = file_uri_to_object($uri, TRUE);
245
  }
246
  catch (Exception $e) {
247
    form_set_error('url', $e->getMessage());
248
    return;
249
  }
250

    
251
  if (!$file->uri) {
252
    form_set_error('url', t('Please select a video.'));
253
    return;
254
  }
255

    
256
  $validators = $form['#validators'];
257
  if ($validators) {
258
    // Check for errors. @see media_add_upload_validate calls file_save_upload().
259
    // this code is ripped from file_save_upload because we just want the validation part.
260
    // Call the validation functions specified by this function's caller.
261
    $errors = file_validate($file, $validators);
262

    
263
    if (!empty($errors)) {
264
      $message = t('%uri could not be added.', array('%uri' => $uri));
265
      if (count($errors) > 1) {
266
        $message .= theme('item_list', array('items' => $errors));
267
      }
268
      else {
269
        $message .= ' ' . array_pop($errors);
270
      }
271
      form_set_error('url', $message);
272
      return FALSE;
273
    }
274
  }
275
  // @TODO: Validate that if we have no $uri that this is a valid file to
276
  // save. For instance, we may only be interested in images, and it would
277
  // be helpful to let the user know they passed the HTML page containing
278
  // the image accidentally. That would also save us from saving the file
279
  // in the submit step.
280

    
281
  // This is kinda a hack of the same.
282

    
283
  // This should use the file_validate routines that the upload form users.
284
  // We need to fix the media_parse_to_file routine to allow for a validation.
285
}
286

    
287
/**
288
 * @TODO: Document this function.
289
 */
290
function media_youtube_add_submit($form, &$form_state) {
291
  $uri = $form_state['values']['submitted-video'];
292
  try {
293
    // Save the remote file
294
    $file = file_uri_to_object($uri, TRUE);
295
    file_save($file);
296
  }
297
  catch (Exception $e) {
298
    form_set_error('url', $e->getMessage());
299
    return;
300
  }
301

    
302
  if (!$file->fid) {
303
    form_set_error('url', t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $uri)));
304
    return;
305
  }
306
  else {
307
    $form_state['file'] = $file;
308
  }
309

    
310
  // Redirect to the file edit page after submission.
311
  // @TODO: media_access() is a wrapper for file_entity_access(). Switch to the
312
  // new function when Media 1.x is deprecated.
313
  if (media_access('update', $file)) {
314
    $destination = array('destination' => 'admin/content/file');
315
    if (isset($_GET['destination'])) {
316
      $destination = drupal_get_destination();
317
      unset($_GET['destination']);
318
    }
319
    $form_state['redirect'] = array('file/' . $file->fid . '/edit', array('query' => $destination));
320
  }
321
  else {
322
    $form_state['redirect'] = 'admin/content/file';
323
  }
324
}
325

    
326
/**
327
 * @TODO: Document this function.
328
 */
329
function media_youtube_video_search($options = array()) {
330
  $options['v'] = 2;
331

    
332
  $request = drupal_http_request(url(MEDIA_YOUTUBE_REST_API, array('query' => $options)));
333
  if (!isset($request->error)) {
334
    $entry = simplexml_load_string($request->data);
335
  }
336
  else {
337
    throw new Exception("Error Processing Request. (Error: {$request->code}, {$request->error})");
338

    
339
    //if request wasn't successful, create object for return to avoid errors
340
    $entry = new SimpleXMLElement();
341
  }
342

    
343
  return media_youtube_unserialize_xml($entry);
344
}
345

    
346
/**
347
 * Recursively converts a SimpleXMLElement object into an array.
348
 *
349
 * @param object $xml
350
 *   The original XML object.
351
 */
352
function media_youtube_unserialize_xml($xml) {
353
  if ($xml instanceof SimpleXMLElement) {
354
    $xml = (array) $xml;
355
  }
356
  if (is_array($xml)) {
357
    foreach ($xml as $key => $item) {
358
      $xml[$key] = media_youtube_unserialize_xml($item);
359
    }
360
  }
361
  return $xml;
362
}
363

    
364
/**
365
 * Check to ensure that a given id is valid.
366
 *
367
 * @param string $id
368
 *   The YouTube video id.
369
 * @param boolean $refresh
370
 *   (Defaults to FALSE) If TRUE, then reset the value from the cache.
371
 * @return boolean
372
 *   Returns TRUE if the video is valid.
373
 *
374
 * @TODO: How does this compare to MediaInternetYouTubeHandler's validId
375
 * method, and can we refactor the code to rely on only one of them?
376
 */
377
function media_youtube_valid_id($id, $refresh = FALSE) {
378
  $ids = &drupal_static(__FUNCTION__, array());
379

    
380
  // Return our cached id if allowed, and it exists.
381
  if (!$refresh && isset($ids[$id])) {
382
    return $ids[$id];
383
  }
384
  elseif (!$refresh && !isset($ids[$id])) {
385
    return $id;
386
  }
387
  elseif (!$refresh && $cache = cache_get('media_youtube:id:' . $id, 'cache_media_xml')) {
388
    $ids[$id] = $cache->data;
389
    return $ids[$id];
390
  }
391

    
392
  $url = url(MEDIA_YOUTUBE_REST_API . '/' . $id);
393
  $response = drupal_http_request($url, array('method' => 'HEAD'));
394
  $ids[$id] = ($response->code == 200);
395
  cache_set('media_youtube:id:' . $id, $ids[$id], 'cache_media_xml', media_variable_get('xml_cache_expire', 3600));
396
  return $ids[$id];
397
}