#]+)/", $input, $matches); if (!empty($matches[1])) { $video_id = $matches[1]; return $video_id; } return FALSE; } /** * Returns a list of YouTube video size options. * * @return array * An array of options, keyed by machine name with human readable values. */ function youtube_size_options() { return array( '420x315' => '450px by 315px', '480x360' => '480px by 360px', '640x480' => '640px by 480px', '960x720' => '960px by 720px', 'responsive' => 'responsive (full-width of container)', 'custom' => 'custom', ); } /** * Returns a list of thumbnail link types. * * @return array * An array of link types, keyed by machine name with human readable values. */ function youtube_thumbnail_link_types() { $link_types = array( 'content' => t('Content'), 'youtube' => t('YouTube'), ); // Allow other modules to add link types. drupal_alter('youtube_thumbnail_link_types', $link_types); return $link_types; } /** * Determines the height and width when given a player size. * * @param string $size * (optional) The machine name of the size from youtube_size_options(). * @param string $width * (optional) The width input for custom dimensions. * @param string $height * (optional) The height input for custom dimensions. * * @return array * An array keyed by 'width' and 'height' with the values to use when theming * the video player. */ function youtube_get_dimensions($size = NULL, $width = NULL, $height = NULL) { $dimensions = array(); if ($size == 'responsive') { $dimensions['width'] = '100%'; $dimensions['height'] = '100%'; } elseif ($size == 'custom') { $dimensions['width'] = strstr($width, '%') ? (int) $width . '%' : (int) $width; $dimensions['height'] = strstr($height, '%') ? (int) $height . '%' : (int) $height; } else { // Locate the 'x'. $strpos = strpos($size, 'x'); // Width is the first dimension. $dimensions['width'] = substr($size, 0, $strpos); // Height is the second dimension. $dimensions['height'] = substr($size, $strpos + 1, strlen($size)); } return $dimensions; } /** * Builds the URI to a given thumbnail or the module's thumbnail directory. * * @param string $video_id * (optional) The video ID to build the thumbnail URI for. * * @return string * If a $video_id was supplied, the URI to that video's thumbnail. Otherwise * the URI to the module's thumbnail directory. */ function youtube_build_thumbnail_uri($video_id = NULL) { $scheme = file_default_scheme(); $youtube_thumb_dir = variable_get('youtube_thumb_dir', 'youtube'); $youtube_thumb_uri = $scheme . '://' . $youtube_thumb_dir; if ($video_id) { return $youtube_thumb_uri . '/' . $video_id . '.jpg'; } return $youtube_thumb_uri; } /** * Retrieves the thumbnail image for a given video from YouTube. * * @param int|null $video_id * The video ID of the particular YouTube video. * @param bool $force_small * (optional) When TRUE, this function should return the standard size image * regardless of what the youtube_thumb_hires variable is set to. This is used * should the high resolution image be found to not exist for a particular * video. * * @return bool|object * Either the Drupal $file object of saved image, or FALSE if the save failed. */ function youtube_get_remote_image($video_id = NULL, $force_small = FALSE) { // This variable is TRUE when higher resolution thumbnails should be saved. // The only thumbnail resolution higher than the standard 480 is // 'maxresdefault'. This resolution image is not guaranteed to exist. If // there's an error retrieving the hi-res image, we try again for small. $youtube_thumb_hires = variable_get('youtube_thumb_hires', FALSE); // This boolean is TRUE if we're obtaining a hi-res thumbnail. $get_hires = ($youtube_thumb_hires && !$force_small); if ($get_hires) { $src = youtube_build_remote_image_path($video_id, 'maxresdefault'); } else { $src = youtube_build_remote_image_path($video_id); } // Download file and save it as managed Drupal file. $image = drupal_http_request($src); if ($image->code != 200) { // Silently retry for small image if hi-res did not exist, otherwise log an // error and return FALSE. if ($get_hires) { return youtube_get_remote_image($video_id, TRUE); } watchdog('youtube', 'HTTP request for video ID %id failed (error code: %err).', array('%id' => $video_id, '%err' => $image->code), WATCHDOG_ERROR); return FALSE; } // Set the path to thumbnails, and make sure it's usable. $youtube_thumb_uri = youtube_build_thumbnail_uri(); if (!file_prepare_directory($youtube_thumb_uri, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) { watchdog('youtube', 'Failed to create YouTube thumbnail directory: %dir', array('%dir' => $youtube_thumb_uri), WATCHDOG_ERROR); return FALSE; } $destination = youtube_build_thumbnail_uri($video_id); // Save the thumbnail and add to Drupal managed files. $file = file_save_data($image->data, $destination, FILE_EXISTS_REPLACE); if (!$file) { watchdog('youtube', 'Unable to save youtube thumbnail to filesystem for video %id', array('%id' => $video_id), WATCHDOG_ERROR); } return $file; } /** * Submit callback; delete all existing thumbnail image files. * * @see youtube_settings_form() */ function youtube_thumb_delete_all($form, &$form_state) { $youtube_thumb_uri = youtube_build_thumbnail_uri(); if (!file_prepare_directory($youtube_thumb_uri)) { return drupal_set_message(t('No files deleted.')); } $files = file_scan_directory($youtube_thumb_uri, '/^.*\.(jpg|png)$/'); $legacy_files = FALSE; foreach ($files as $raw_file) { // Check if file is being managed by Drupal. $uri = $youtube_thumb_uri . '/' . $raw_file->filename; $managed_file = db_select('file_managed', 'fm') ->fields('fm') ->condition('uri', '%' . $uri, 'LIKE') ->execute() ->fetchAssoc(); if (!$managed_file) { // Old files exist before module used managed files, notify user. $legacy_files = file_unmanaged_delete($raw_file->uri); } else { $file = file_load($managed_file['fid']); // file_delete() invokes hooks to refresh associated image files. file_delete($file); } } if ($legacy_files) { drupal_set_message(t('Note: Some unmanaged files from an older version of the module were deleted. Their associated image style derivatives were not deleted. Image style flush or `drush image-flush` can be used for that. New thumbnails will be managed files and will not have this issue.', array('@imagestyleflush' => 'https://www.drupal.org/project/imagestyleflush'))); } } /** * Get YouTube image path by building correctly formed URL. * * @param string|null $video_id * (optional) The ID of the video to grab the thumbnail from. * @param string|null $version * (optional) Which version of the thumbnail to grab. * * @return string|null * The youtube.com image path to the specified version/video. */ function youtube_build_remote_image_path($video_id = NULL, $version = '0') { // The different versions of the image made available by YouTube. // http://stackoverflow.com/questions/2068344/how-to-get-thumbnail-of-youtube-video-link-using-youtube-api $versions = array( '0', 'hqdefault', 'mqdefault', 'maxresdefault', 'default', '1', '2', '3', ); if (!$video_id || !in_array($version, $versions)) { return; } $version_path = 'http://img.youtube.com/vi/' . $video_id . '/' . $version . '.jpg'; return url($version_path); } /** * Implements hook_feeds_processor_targets_alter(). * * Adds a target option for YouTube fields to Feeds mapping options. */ function youtube_feeds_processor_targets_alter(&$targets, $entity_type, $bundle_name) { foreach (field_info_instances($entity_type, $bundle_name) as $name => $instance) { $info = field_info_field($name); if (in_array($info['type'], array('youtube'))) { $targets[$name] = array( 'name' => check_plain($instance['label']), 'callback' => 'youtube_set_target', 'description' => t('The @label field of the node.', array('@label' => $instance['label'])), ); } } } /** * Callback to set the Feeds target for a YouTube field. * * @param FeedsSource $source * Field mapper source settings. * @param object $entity * An entity object, for instance a node object. * @param string $target * A string identifying the target on the node. * @param string|array $value * The value to populate the target with. * @param array $mapping * Associative array of the mapping settings from the per mapping * configuration form. */ function youtube_set_target(FeedsSource $source, $entity, $target, $value, array $mapping) { if (empty($value)) { return; } if (!is_array($value)) { $value = array($value); } $info = field_info_field($target); $field = isset($entity->$target) ? $entity->$target : array(LANGUAGE_NONE => array()); // Allow for mappings to a multi-value field on the same target. $delta = count($field[LANGUAGE_NONE]); foreach ($value as $v) { if ($info['cardinality'] == $delta) { break; } if (is_object($v) && ($v instanceof FeedsElement)) { $v = $v->getValue(); } if (is_scalar($v)) { $video_id = youtube_get_video_id($v); if ($video_id) { $entity->{$target}[LANGUAGE_NONE][$delta] = array( 'input' => $v, 'video_id' => $video_id, ); $delta++; } } } } /** * Implements hook_token_info_alter(). * * Alters and adds tokens for each youtube field. */ function youtube_token_info_alter(&$data) { // Get all youtube fields. Gather entity_type and bundle information. $fields = field_info_fields(); $youtube_fields = array(); foreach ($fields as $name => $field) { if ($field['type'] == 'youtube') { foreach ($field['bundles'] as $type => $entity_type) { foreach ($entity_type as $bundle) { $youtube_fields[] = array( 'entity_type' => $type, 'bundle' => $bundle, 'field_name' => $name, ); } } } } foreach ($youtube_fields as $field) { $field_info = field_info_instance($field['entity_type'], $field['field_name'], $field['bundle']); $field_label = $field_info['label']; // Modify the default field token. $data['tokens'][$field['entity_type']][$field['field_name']] = array( 'name' => $field_label . t(": Default"), 'description' => t("The YouTube video field value's Default (or Token if exists) view mode output."), ); // Add two new tokens. $data['tokens'][$field['entity_type']][$field['field_name'] . '__youtube_video_url'] = array( 'name' => $field_label . t(": Video URL"), 'description' => t("The YouTube video field value's youtube.com URL."), ); $data['tokens'][$field['entity_type']][$field['field_name'] . '__youtube_image_url'] = array( 'name' => $field_label . t(": Image URL"), 'description' => t("The YouTube video field value's local image URL."), ); } } /** * Implements hook_tokens(). * * @see youtube_tokens_info_alter() */ function youtube_tokens($type, $tokens, array $data = array(), array $options = array()) { global $base_url; global $base_path; $url_options = array('absolute' => TRUE); if (isset($options['language'])) { $url_options['language'] = $options['language']; $language_code = $options['language']->language; } else { $language_code = NULL; } $sanitize = !empty($options['sanitize']); $replacements = array(); if ($type == 'node' && !empty($data['node'])) { $node = $data['node']; foreach ($tokens as $name => $original) { if (!strpos($name, '__youtube_')) { // This isn't a YouTube Field token! continue; } $token_pieces = explode('__', $name); if (count($token_pieces) != 2) { continue; } $field_name = $token_pieces[0]; $token_name = $token_pieces[1]; switch ($token_name) { case 'youtube_video_url': $replacements[$original] = ''; if ($field_value = field_get_items('node', $node, $field_name)) { $replacements[$original] = 'http://www.youtube.com/watch?v=' . $field_value[0]['video_id']; } break; case 'youtube_image_url': $replacements[$original] = ''; if ($field_value = field_get_items('node', $node, $field_name)) { $video_id = $field_value[0]['video_id']; $file_uri = youtube_build_thumbnail_uri($video_id); $file_url = file_create_url($file_uri); if (file_exists($file_uri) || youtube_get_remote_image($video_id)) { $replacements[$original] = $file_url; if ($style_name = variable_get('youtube_thumb_token_image_style', NULL)) { $derivative_uri = image_style_path($style_name, $file_uri); if (!file_exists($derivative_uri)) { $image_style = image_style_load($style_name); image_style_create_derivative($image_style, $file_uri, $derivative_uri); } $replacements[$original] = file_create_url($derivative_uri); } } } break; } } } return $replacements; }