video_embed_field.handlers.inc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. <?php
  2. /**
  3. * @file
  4. * Provide some handlers for video embed field
  5. * Other modules can implement the hook_video_embed_handler_info to provide more
  6. * handlers.
  7. */
  8. /**
  9. * Implements hook_video_embed_handler_info().
  10. */
  11. function video_embed_field_video_embed_handler_info() {
  12. $handlers = array();
  13. $handlers['youtube'] = array(
  14. 'title' => 'Youtube',
  15. 'function' => 'video_embed_field_handle_youtube',
  16. 'thumbnail_function' => 'video_embed_field_handle_youtube_thumbnail',
  17. 'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/youtube.jpg',
  18. 'data_function' => 'video_embed_field_handle_youtube_data',
  19. 'form' => 'video_embed_field_handler_youtube_form',
  20. 'form_validate' => 'video_embed_field_handler_youtube_form_validate',
  21. 'domains' => array(
  22. 'youtube.com',
  23. 'youtu.be',
  24. ),
  25. 'defaults' => array(
  26. 'width' => 640,
  27. 'height' => 360,
  28. 'autoplay' => 0,
  29. 'vq' => 'large',
  30. 'rel' => 0,
  31. 'controls' => 1,
  32. 'autohide' => 2,
  33. 'showinfo' => 1,
  34. 'modestbranding' => 0,
  35. 'theme' => 'dark',
  36. 'iv_load_policy' => 1,
  37. ),
  38. );
  39. $handlers['vimeo'] = array(
  40. 'title' => 'Vimeo',
  41. 'function' => 'video_embed_field_handle_vimeo',
  42. 'thumbnail_function' => 'video_embed_field_handle_vimeo_thumbnail',
  43. 'thumbnail_default' => drupal_get_path('module', 'video_embed_field') . '/img/vimeo.jpg',
  44. 'form' => 'video_embed_field_handler_vimeo_form',
  45. 'form_validate' => 'video_embed_field_handler_vimeo_form_validate',
  46. 'domains' => array(
  47. 'vimeo.com',
  48. ),
  49. 'defaults' => array(
  50. 'width' => 640,
  51. 'height' => 360,
  52. 'color' => '00adef',
  53. 'portrait' => 1,
  54. 'title' => 1,
  55. 'byline' => 1,
  56. 'autoplay' => 0,
  57. 'loop' => 0,
  58. 'froogaloop' => 0,
  59. ),
  60. );
  61. return $handlers;
  62. }
  63. /**
  64. * Helper function to get the youtube video's id.
  65. *
  66. * @param string $url
  67. * The video URL.
  68. *
  69. * @return string|bool
  70. * The video ID, or FALSE in case the ID can't be retrieved from the URL.
  71. */
  72. function _video_embed_field_get_youtube_id($url) {
  73. // Find the ID of the video they want to play from the url.
  74. if (stristr($url, 'http://')) {
  75. $url = substr($url, 7);
  76. }
  77. elseif (stristr($url, 'https://')) {
  78. $url = substr($url, 8);
  79. }
  80. if (stristr($url, 'playlist')) {
  81. // Playlists need the appended ampersand to take the options properly.
  82. $url = $url . '&';
  83. $pos = strripos($url, '?list=');
  84. if ($pos !== FALSE) {
  85. $pos2 = stripos($url, '&');
  86. $pos2++;
  87. }
  88. else {
  89. return FALSE;
  90. }
  91. }
  92. // Alternate playlist link.
  93. elseif (stristr($url, 'view_play_list')) {
  94. $url = $url . '&';
  95. // All playlist ID's are prepended with PL.
  96. if (!stristr($url, '?p=PL')) {
  97. $url = substr_replace($url, 'PL', strpos($url, '?p=') + 3, 0);
  98. }
  99. // Replace the links format with the embed format.
  100. $url = str_ireplace('play_list?p=', 'videoseries?list=', $url);
  101. $pos = strripos($url, 'videoseries?list=');
  102. if ($pos !== FALSE) {
  103. $pos2 = stripos($url, '&');
  104. $pos2++;
  105. }
  106. else {
  107. return FALSE;
  108. }
  109. }
  110. else {
  111. $pos = strripos($url, 'v=');
  112. if ($pos !== FALSE) {
  113. $pos += 2;
  114. $pos2 = stripos($url, '&', $pos);
  115. $pos_hash = stripos($url, '#', $pos);
  116. $pos2 = _video_embed_get_min($pos2, $pos_hash);
  117. }
  118. else {
  119. $pos = strripos($url, '/');
  120. if ($pos !== FALSE) {
  121. $pos++;
  122. $pos2 = stripos($url, '?', $pos);
  123. $pos_hash = stripos($url, '#', $pos);
  124. $pos2 = _video_embed_get_min($pos2, $pos_hash);
  125. }
  126. }
  127. }
  128. if ($pos === FALSE) {
  129. return FALSE;
  130. }
  131. else {
  132. if ($pos2 > 0) {
  133. $id = substr($url, $pos, $pos2 - $pos);
  134. }
  135. else {
  136. $id = substr($url, $pos);
  137. }
  138. }
  139. return $id;
  140. }
  141. /**
  142. * Handler for Youtube videos.
  143. *
  144. * @param string $url
  145. * The video URL.
  146. * @param array $settings
  147. * The settings array.
  148. *
  149. * @return array
  150. * The video iframe render array.
  151. */
  152. function video_embed_field_handle_youtube($url, $settings) {
  153. $output = array();
  154. // Grab the minutes and seconds, and just convert it down to seconds.
  155. preg_match('/#t=((?P<min>\d+)m)?((?P<sec>\d+)s)?/', $url, $matches);
  156. // Give it some default data in case there is no #t=...
  157. $matches += array(
  158. "min" => 0,
  159. "sec" => 0,
  160. );
  161. $time = ($matches["min"] * 60) + $matches["sec"];
  162. $settings['start'] = $time;
  163. $id = _video_embed_field_get_youtube_id($url);
  164. if (!$id) {
  165. // We can't decode the URL - just return the URL as a link.
  166. $output['#markup'] = l($url, $url);
  167. return $output;
  168. }
  169. // Construct the embed code.
  170. $settings['wmode'] = 'opaque';
  171. $settings_str = _video_embed_code_get_settings_str($settings);
  172. $output['#markup'] = '<iframe width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//www.youtube.com/embed/' . $id . '?' . $settings_str . '" frameborder="0" allowfullscreen></iframe>';
  173. return $output;
  174. }
  175. /**
  176. * Gets the thumbnail url for youtube videos.
  177. *
  178. * @param string $url
  179. * The video URL.
  180. *
  181. * @return array
  182. * The video thumbnail information.
  183. */
  184. function video_embed_field_handle_youtube_thumbnail($url) {
  185. $info = array();
  186. $id = _video_embed_field_get_youtube_id($url);
  187. // Playlist.
  188. if (stristr($id, '?list=')) {
  189. // Strip out all but the ID, including the PL behind the ID.
  190. $start = strpos($id, '?list=PL') + 8;
  191. $length = strpos($id, '&') - $start;
  192. $id = substr($id, $start, $length);
  193. $info['id'] = $id;
  194. // Playlist info is stored in XML. The thumbnail is in there.
  195. $xml = drupal_http_request('http://gdata.youtube.com/feeds/api/playlists/' . $id);
  196. if (!isset($xml->error)) {
  197. $xml = new SimpleXMLElement($xml->data);
  198. $media = $xml->children('http://search.yahoo.com/mrss/');
  199. if ($media->group->thumbnail && $media->group->thumbnail[0]->attributes()) {
  200. $attrs = $media->group->thumbnail[0]->attributes();
  201. $info['url'] = (string) $attrs['url'];
  202. }
  203. }
  204. }
  205. // Regular video.
  206. elseif ($id) {
  207. $info['id'] = $id;
  208. $info['url'] = 'http://img.youtube.com/vi/' . $id . '/0.jpg';
  209. }
  210. return $info;
  211. }
  212. /**
  213. * Gets video data for a YouTube video URL.
  214. *
  215. * @param string $url
  216. * A YouTube video URL to get data for
  217. *
  218. * @return array|bool
  219. * An array of video data, or FALSE if unable to fetch data
  220. */
  221. function video_embed_field_handle_youtube_data($url) {
  222. // Get YouTube video ID from URL.
  223. $id = _video_embed_field_get_youtube_id($url);
  224. if ($id) {
  225. $response = drupal_http_request('http://gdata.youtube.com/feeds/api/videos/' . $id . '?v=2&alt=json');
  226. if (!isset($response->error)) {
  227. $data = json_decode($response->data);
  228. $data = isset($data->entry) ? (array) $data->entry : (array) $data->feed;
  229. return _video_embed_field_clean_up_youtube_data($data);
  230. }
  231. }
  232. return FALSE;
  233. }
  234. /**
  235. * Flattens out some unnecessary nesting in the youtube data.
  236. *
  237. * @param array $data
  238. * The unflattened data.
  239. *
  240. * @return array
  241. * The flattened data.
  242. */
  243. function _video_embed_field_clean_up_youtube_data($data) {
  244. // Make things a bit nicer for people trying to use the data.
  245. foreach ($data as $key => $value) {
  246. if (is_object($value)) {
  247. $temp = (array) $value;
  248. if (isset($temp['$t'])) {
  249. $data[$key] = $temp['$t'];
  250. }
  251. else {
  252. $data[$key] = _video_embed_field_clean_up_youtube_data($temp);
  253. }
  254. }
  255. elseif (is_array($value)) {
  256. $data[$key] = _video_embed_field_clean_up_youtube_data($value);
  257. }
  258. if ($key === 'category') {
  259. $terms = array();
  260. foreach ($data[$key] as $value) {
  261. if (isset($value['scheme']) && $value['scheme'] == 'http://schemas.google.com/g/2005#kind') {
  262. continue;
  263. }
  264. if (isset($value['term'])) {
  265. $terms[] = $value['term'];
  266. }
  267. }
  268. $data['terms'] = $terms;
  269. }
  270. }
  271. return $data;
  272. }
  273. /**
  274. * Defines the form elements for the Youtube configuration form.
  275. *
  276. * @param array $defaults
  277. * The form default values.
  278. *
  279. * @return array
  280. * The provider settings form array.
  281. */
  282. function video_embed_field_handler_youtube_form($defaults) {
  283. $form = array();
  284. $form['width'] = array(
  285. '#type' => 'textfield',
  286. '#size' => '5',
  287. '#title' => t('Player Width'),
  288. '#description' => t('The width of the youtube player.'),
  289. '#default_value' => $defaults['width'],
  290. );
  291. $form['height'] = array(
  292. '#type' => 'textfield',
  293. '#size' => '5',
  294. '#title' => t('Player Height'),
  295. '#description' => t('The height of the youtube player.'),
  296. '#default_value' => $defaults['height'],
  297. );
  298. $form['theme'] = array(
  299. '#type' => 'select',
  300. '#options' => array(
  301. 'dark' => t('Dark'),
  302. 'light' => t('Light'),
  303. ),
  304. '#title' => t('Player theme'),
  305. '#default_value' => $defaults['theme'],
  306. );
  307. $form['autoplay'] = array(
  308. '#type' => 'checkbox',
  309. '#title' => t('Autoplay'),
  310. '#description' => t('Play the video immediately.'),
  311. '#default_value' => $defaults['autoplay'],
  312. );
  313. $form['vq'] = array(
  314. '#type' => 'select',
  315. '#title' => t('Video quality'),
  316. '#options' => array(
  317. 'small' => t('Small (240p)'),
  318. 'medium' => t('Medium (360p)'),
  319. 'large' => t('Large (480p)'),
  320. 'hd720' => t('HD 720p'),
  321. 'hd1080' => t('HD 10800p'),
  322. ),
  323. '#default_value' => $defaults['vq'],
  324. '#description' => t('Attempt to play the video in certain quality if available.'),
  325. );
  326. $form['rel'] = array(
  327. '#type' => 'checkbox',
  328. '#title' => t('Show related videos'),
  329. '#description' => t('Show related videos after the video is finished playing.'),
  330. '#default_value' => $defaults['rel'],
  331. );
  332. $form['showinfo'] = array(
  333. '#type' => 'checkbox',
  334. '#title' => t('Show info'),
  335. '#description' => t('Display information like the video title and rating before the video starts playing.'),
  336. '#default_value' => $defaults['showinfo'],
  337. );
  338. $form['modestbranding'] = array(
  339. '#type' => 'checkbox',
  340. '#title' => t('Hide Youtube logo'),
  341. '#description' => t('Hide the Youtube logo button on the player'),
  342. '#default_value' => $defaults['modestbranding'],
  343. );
  344. $form['iv_load_policy'] = array(
  345. '#type' => 'radios',
  346. '#options' => array(
  347. 1 => t('Show video annotations.'),
  348. 3 => t('Hide video annotations.'),
  349. ),
  350. '#title' => t('Display annotations'),
  351. '#description' => t('Controls the display of annotations over the video content. Only works when using the flash player.'),
  352. '#default_value' => $defaults['iv_load_policy'],
  353. );
  354. $form['controls'] = array(
  355. '#type' => 'radios',
  356. '#options' => array(
  357. 0 => t('Hide video controls.'),
  358. 1 => t('Show video controls. Youtube default.'),
  359. 2 => t('Show video controls with performance improvement for iframe embeds.'),
  360. ),
  361. '#title' => t('Display Youtube player controls'),
  362. '#description' => t('This parameter indicates whether the video player controls will display.'),
  363. '#default_value' => $defaults['controls'],
  364. );
  365. $form['autohide'] = array(
  366. '#type' => 'radios',
  367. '#options' => array(
  368. 0 => t('The video progress bar and player controls will be visible throughout the video.'),
  369. 1 => t('Automatically slide the video progress bar and the player controls out of view a couple of seconds after the video starts playing. They will only reappear if the user moves her mouse over the video player or presses a keyboard key.'),
  370. 2 => t('The video progress bar will fade out but the player controls (play button, volume control, etc.) remain visible.'),
  371. ),
  372. '#title' => t('Autohide progress bar and the player controls'),
  373. '#description' => t('Controls the autohide behavior of the youtube player controls.'),
  374. '#default_value' => $defaults['autohide'],
  375. );
  376. return $form;
  377. }
  378. /**
  379. * Validates the form elements for the Youtube configuration form.
  380. *
  381. * @param array $element
  382. * The form element to validate.
  383. * @param array $form_state
  384. * The form to validate state.
  385. * @param array $form
  386. * The form to validate structure.
  387. */
  388. function video_embed_field_handler_youtube_form_validate($element, &$form_state, $form) {
  389. video_embed_field_validate_dimensions($element);
  390. }
  391. /**
  392. * Helper function to get the Vimeo video's data attributes.
  393. *
  394. * @param string $url
  395. * A Vimeo video URL to get the data from.
  396. *
  397. * @return integer|false
  398. * The video's data attributes, or FALSE if unable to get the video ID.
  399. */
  400. function _video_embed_field_get_vimeo_data($url) {
  401. // Set oembed endpoint
  402. $oembed_endpoint = 'http://vimeo.com/api/oembed';
  403. // Fetch vimeo data
  404. $response = drupal_http_request($oembed_endpoint . '.json?url=' . rawurlencode($url));
  405. try {
  406. return json_decode($response->data, TRUE);
  407. } catch (Exception $e) {
  408. return FALSE;
  409. }
  410. }
  411. /**
  412. * Helper function to get the Vimeo video's data attributes.
  413. *
  414. * @param string $url
  415. * A Vimeo video URL to get the ID of.
  416. *
  417. * @return integer|false
  418. * The video ID, or FALSE if unable to get the video ID.
  419. */
  420. function _video_embed_field_get_vimeo_id($vimeo_data) {
  421. try {
  422. $video_id = $vimeo_data['video_id'];
  423. } catch (Exception $e) {
  424. $video_id = FALSE;
  425. }
  426. return $video_id;
  427. }
  428. /**
  429. * Handler for Vimeo videos.
  430. *
  431. * @param string $url
  432. * The video URL.
  433. * @param array $settings
  434. * The settings array.
  435. *
  436. * @return string
  437. * The video iframe.
  438. */
  439. function video_embed_field_handle_vimeo($url, $settings) {
  440. $vimeo_data = _video_embed_field_get_vimeo_data($url);
  441. // Get ID of video from URL.
  442. $id = _video_embed_field_get_vimeo_id($vimeo_data);
  443. if (empty($id)) {
  444. return array(
  445. '#markup' => l($url, $url),
  446. );
  447. }
  448. // Construct the embed code.
  449. $settings['player_id'] = drupal_html_id('vimeo-' . $id);
  450. if (!empty($settings['froogaloop'])) {
  451. $settings['api'] = 1;
  452. }
  453. unset($settings['froogaloop']);
  454. $settings_str = _video_embed_code_get_settings_str($settings);
  455. return array(
  456. '#markup' => '<iframe id="' . $settings['player_id'] . '" width="' . check_plain($settings['width']) . '" height="' . check_plain($settings['height']) . '" src="//player.vimeo.com/video/' . $id .
  457. '?' . $settings_str . '" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowfullscreen></iframe>',
  458. );
  459. }
  460. /**
  461. * Gets the thumbnail url for vimeo videos.
  462. *
  463. * @param string $url
  464. * The video URL.
  465. *
  466. * @return array
  467. * The video thumbnail information.
  468. */
  469. function video_embed_field_handle_vimeo_thumbnail($url) {
  470. $vimeo_data = _video_embed_field_get_vimeo_data($url);
  471. // Get ID of video from URL.
  472. $id = _video_embed_field_get_vimeo_id($vimeo_data);
  473. $info = array(
  474. 'id' => $id,
  475. );
  476. try {
  477. $info['url'] = $vimeo_data['thumbnail_url'];
  478. } catch (Exception $e) {
  479. }
  480. return $info;
  481. }
  482. /**
  483. * Defines the form elements for the Vimeo configuration form.
  484. *
  485. * @param array $defaults
  486. * The form default values.
  487. *
  488. * @return array
  489. * The provider settings form array.
  490. */
  491. function video_embed_field_handler_vimeo_form($defaults) {
  492. $form = array();
  493. $form['width'] = array(
  494. '#type' => 'textfield',
  495. '#size' => '5',
  496. '#title' => t('Player Width'),
  497. '#description' => t('The width of the vimeo player.'),
  498. '#default_value' => $defaults['width'],
  499. );
  500. $form['height'] = array(
  501. '#type' => 'textfield',
  502. '#size' => '5',
  503. '#title' => t('Player Height'),
  504. '#description' => t('The height of the vimeo player.'),
  505. '#default_value' => $defaults['height'],
  506. );
  507. $form['color'] = array(
  508. '#type' => 'select',
  509. '#options' => array(
  510. '00adef' => t('Blue'),
  511. 'ff9933' => t('Orange'),
  512. 'c9ff23' => t('Lime'),
  513. 'ff0179' => t('Fuschia'),
  514. 'ffffff' => t('White'),
  515. ),
  516. '#title' => t('Player Color'),
  517. '#description' => t('The color to use on the vimeo player.'),
  518. '#default_value' => $defaults['color'],
  519. );
  520. $form['portrait'] = array(
  521. '#type' => 'checkbox',
  522. '#title' => t('Overlay Author Thumbnail'),
  523. '#description' => t("Overlay the author's thumbnail before the video is played."),
  524. '#default_value' => $defaults['portrait'],
  525. );
  526. $form['title'] = array(
  527. '#type' => 'checkbox',
  528. '#title' => t("Overlay Video's Title"),
  529. '#description' => t("Overlay the video's title before the video is played."),
  530. '#default_value' => $defaults['title'],
  531. );
  532. $form['byline'] = array(
  533. '#type' => 'checkbox',
  534. '#title' => t("Overlay Video's Byline"),
  535. '#description' => t("Overlay the video's description before the video is played."),
  536. '#default_value' => $defaults['byline'],
  537. );
  538. $form['overridable'] = array(
  539. '#prefix' => '<p class="note"><strong>' . t('Note') . ': </strong><em>',
  540. '#markup' => t('Color, portrait, title and byline can be restricted by Vimeo Plus videos.
  541. Such videos will ignore these settings.'),
  542. '#suffix' => '</em></p>',
  543. );
  544. $form['autoplay'] = array(
  545. '#type' => 'checkbox',
  546. '#title' => t('Autoplay'),
  547. '#description' => t('Play the video immediately.'),
  548. '#default_value' => $defaults['autoplay'],
  549. );
  550. $form['loop'] = array(
  551. '#type' => 'checkbox',
  552. '#title' => t('Loop'),
  553. '#description' => t("Loop the video's playback"),
  554. '#default_value' => $defaults['loop'],
  555. );
  556. $form['froogaloop'] = array(
  557. '#type' => 'checkbox',
  558. '#title' => t('Enable froogaloop support'),
  559. '#description' => t("Enables Froogallop Vimeo's library support"),
  560. '#default_value' => $defaults['loop'],
  561. );
  562. return $form;
  563. }
  564. /**
  565. * Validates the form elements for the Vimeo configuration form.
  566. *
  567. * @param array $element
  568. * The form element to validate.
  569. * @param array $form_state
  570. * The form to validate state.
  571. * @param array $form
  572. * The form to validate structure.
  573. */
  574. function video_embed_field_handler_vimeo_form_validate($element, &$form_state, $form) {
  575. video_embed_field_validate_dimensions($element);
  576. }
  577. /**
  578. * Calculates the min index for use in finding the id of a youtube video.
  579. *
  580. * @param string $pos1
  581. * The first index.
  582. * @param string $pos2
  583. * The second index.
  584. *
  585. * @return string
  586. * The min index.
  587. */
  588. function _video_embed_get_min($pos1, $pos2) {
  589. if (!$pos1) {
  590. return $pos2;
  591. }
  592. elseif (!$pos2) {
  593. return $pos1;
  594. }
  595. else {
  596. return min($pos1, $pos2);
  597. }
  598. }