2

I want to control the thumbnail image size for my media entities of type video.

The thumbnail image size being fetched by the media module from Youtube has 480 x 360px.

I want to use the 1280x720 images. Is there a way for me to control this?

3
  • Which module are you using? – Ismail Cherri Feb 14 '19 at 12:58
  • Drupal's core media module. – José Trindade Feb 14 '19 at 13:10
  • This takes core patching. See the answer below. – José Trindade Mar 25 '19 at 16:43
2

Drupal core issue regarding this problem: https://www.drupal.org/project/drupal/issues/3042423

After patching, I would implement the new hook_oembed_resource_data_alter:

function MY_MODULE_oembed_resource_data_alter(array &$data, $url) {
  if (strpos($url, 'youtube.com/oembed') !== FALSE) {
    $thumbnailUrl = str_replace('hqdefault', 'maxresdefault', $data['thumbnail_url']);
    $data['thumbnail_url'] = $thumbnailUrl;
  }
}

Then I would also run an update hook to update the existing thumbnails:

function MY_MODULE_update_N(&$sandbox) {
  /** @var \Drupal\media\MediaStorage $mediaStorage */
  $mediaStorage = \Drupal::entityTypeManager()->getStorage('media');
  if (!isset($sandbox['progress'])) {
    $result = $mediaStorage->getQuery()->condition('bundle', 'video');
    $mediaItems = $result->execute();
    $max = count($mediaItems) - 1;
    $sandbox['progress'] = 0;
    $sandbox['mediaItems'] = array_values($mediaItems);
    $sandbox['max'] = $max;
  }
  $progress = $sandbox['progress'];
  /** @var \Drupal\media\MediaInterface $media */
  $media = $mediaStorage->load($sandbox['mediaItems'][$progress]);
  if (!$media->get('thumbnail')->isEmpty()) {
    $media->get('thumbnail')->delete();
  }
  $media->updateQueuedThumbnail();
  $media->save();
  $sandbox['progress']++;
  $sandbox['#finished'] = empty($sandbox['max']) ? 1 : $sandbox['progress'] / $sandbox['max'];
}
1
  • 1
    While this approach works nicely, it has one major drawback. Your code in MY_MODULE_oembed_resource_data_alter forces all thumbnails to be obtained in maxresdefault quality. However, the reality is that not all videos from youtube have thumbnails available in this format. When a remote video is created which doesn't have a high quality thumbnail, no thumb is generated at all (although lower quality thumbnails do exist). I worked around this limitation by grabbing the hightest available, to choose from: 'maxresdefault', 'sddefault', 'hqdefault', 'mqdefault', 'default'. – bmunslow Jul 1 '19 at 13:55
0

Building on José Trindade's answer, here's my solution that uses Guzzle to find the highest resolution that YouTube actually provides for a given video:

use GuzzleHttp\Client as GuzzleClient;

/**
 * Implements hook_oembed_resource_data_alter().
 *
 * This tries to fetch the highest available YouTube video thumbnail by sending
 * requests via Guzzle for the various formats from highest to lowest, using the
 * first one that doesn't return a 404.
 *
 * @param array &$data
 *   The oEmbed data, parsed into an array.
 *
 * @param string $url
 *   The oEmbed URL that $data was retrieved from.
 *
 * @see https://www.drupal.org/project/drupal/issues/3042423
 *   This hook won't be invoked unless this Drupal core patch is applied.
 */
function YOUR_MODULE_oembed_resource_data_alter(array &$data, $url) {
  if (
    $data['provider_name'] === 'YouTube' &&
    strpos($data['thumbnail_url'], 'hqdefault.jpg') !== false
  ) {
    // Array of thumbnail sizes above 'hqdefault' to try, started with the
    // largest size.
    //
    // @see https://stackoverflow.com/a/20542029
    $thumbnailTypes = [
      'maxresdefault' => [
        'width'   => 1920,
        'height'  => 1080,
      ],
      'sddefault' => [
        'width'   => 640,
        'height'  => 480,
      ],
    ];

    $client = new GuzzleClient();

    foreach ($thumbnailTypes as $thumbnailName => $thumbnailDimensions) {
      // Replace 'hqdefault' in the thumbnail URL with the current type we're
      // testing for.
      $testThumbnailURL = str_replace(
        'hqdefault',
        $thumbnailName,
        $data['thumbnail_url']
      );

      // We need to wrap the request in a try {} catch {} because Guzzle will
      // throw an exception on a 404.
      try {
        $response = $client->request('GET', $testThumbnailURL);

      // Got an exception? Skip to the next thumbnail size, assuming this
      // returned a 404 or ran into some other error.
      } catch (Exception $exception) {
        continue;
      }

      // If this was a 200 response, update the thumbnail URL and dimensions
      // with the higher resolution and break out of the loop.
      if ($response->getStatusCode() === 200) {
        $data['thumbnail_url']    = $testThumbnailURL;
        $data['thumbnail_width']  = $thumbnailDimensions['width'];
        $data['thumbnail_height'] = $thumbnailDimensions['height'];

        break;
      }
    }
  }
}

Don't forget to replace YOUR_MODULE with the name of your custom module.

0

Thanks to Jose Trinidad and Ambient.Impact for their answers, they helped me a lot. To get the Jose's update-function to work in my project though I had to set the bundle to 'remote_video' instead of only video:

$result = $mediaStorage->getQuery()->condition('bundle', 'remote_video');

I think the video bundle refers to normally uploaded videos

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.