Migrating to core media

Last updated on
31 May 2024

This documentation needs review. See "Help improve this page" in the sidebar.

When using Video Embed Media

Update Video Embed to ^2.2, enable the Video Embed Media Oembed Migrate submodule, and run the migrate command it provides.

You must run the migrate command on your production environment. The module does not run this command automatically when it is installed. If you are developing directly on your production environment, without committing your configuration to version control, you can run these commands:

drush en vem_migrate_oembed
drush vem:migrate_oembed
drush pm-uninstall vem_migrate_oembed
drush pm-uninstall video_embed_media

If you are committing your configuration on a development environment and then deploying it to a production environment, you will need to take a different approach to perform these tasks in the correct sequence. For example, in a custom module's my_module.install file you could create an update hook:

/**
 * Migrate from Video Embed Field to core's standard oEmbed system.
 */
function my_module_update_8100() {
  \Drupal::service('module_installer')->install(['vem_migrate_oembed']);
  \Drupal::service('vem_migrate_oembed.migrate')->migrate();
  \Drupal::service('module_installer')->uninstall(['vem_migrate_oembed']);
}

On your development environment with a clone of your production database, you would then run 'drush updb' to trigger this update hook, 'drush pm-uninstall video_embed_media' to uninstall the unneeded module, and 'drush cex' to export the resulting configuration. When you deploy this code and configuration together to your production environment, your deployment process should run the update hooks before importing the configuration. 

If you search through your configuration, you will probably not find the string "video_embed_field" (unless you engaged in a more complex use case), and if this is the case you can probably also uninstall altogether with 'drush pm-uninstall video_embed_field'.

For the background to this feature, see #2997799: Include upgrade path from video_embed_field to oEmbed.

When using Video Embed WYSIWYG

Adapted from a blog post by Horizontal

Prerequisites

The hook examined in this blog post assumes you have done a bit of configuration to get your site ready to handle remote video as core Media. The essential pre-migration steps are:

  1. Enable the core Media module.
  2. Create a Media type for remote video called remote_video with a source field called field_media_oembed_video. (If you used a standard install, this media type should get created automatically. Otherwise, you'll have to create it yourself.)
  3. Configure your text formats to use the core "Embed media" filter. You're probably using ckeditor4 if you're still using Video Embed WYSIWYG, so these docs should help.
    • Optionally, enable core Medial Library, then enable the "Insert from Media Library" button.

Caveat

There's also one caveat: the code below won't work quite right for formatted text fields that have a cardinality other than 1. In my experience, though, almost all formatted text fields (like body) have a cardinality of 1.

The Essential Transformation

Ideally, you don't need to understand this section, but it will help if you end up having to tinker with the code below. Let's look at what we're trying to accomplish at a low level.

A video embedded using Video Embed WYSIWYG uses a json token that looks something like this:

{"preview_thumbnail":"[url to an image]","video_url":"[url to the video]","settings":{"responsive":1,"width":"854","height":"480","autoplay":0},"settings_summary":["Embedded Video (Responsive)."]}

We want to transform that into a <drupal-media> tag with the appropriate attributes.

<drupal-media data-align="center" data-entity-type="media" data-entity-uuid="[media uuid]"></drupal-media>

In order to perform that transformation, we need to create a Media entity based on the json token and use its uuid when creating the <drupal-media> tag. That's all the update hook does, nested within a few loops such that we find and update every video on your site.

Video Embed WYSIWYG Update Hook

When I did this work for a client, I put the following code in the install hook of a custom, but it could just as easily go in an update hook in one of your custom modules. It's totally up to you! Copy and paste and edit as you see fit. And good luck!

function my_module_install() {
  // Find all formatted text fields on all content entities (Node, Paragraph, etc).
  $text_long = \Drupal::service('entity_type.manager')->getStorage('field_storage_config')->loadByProperties(['type' => 'text_long']);
  $text_with_summary = \Drupal::service('entity_type.manager')->getStorage('field_storage_config')->loadByProperties(['type' => 'text_with_summary']);
  $fields = array_merge($text_long, $text_with_summary);
  // Iterate through each formatted text field.
  foreach ($fields as $field) {
    // Find all entities (e.g. Nodes) that have a video embedded in this field.
    $ids = \Drupal::entityTypeManager()
      ->getStorage($field->getTargetEntityTypeId())
      ->getQuery()
      ->condition($field->getName(), '%video_url%', 'LIKE')
      ->accessCheck(FALSE)
      ->execute();
    // Iterate through the entities with embedded videos in this field.
    foreach ($ids as $id) {
      // Attempt to load the entity.
      $entity = \Drupal::entityTypeManager()->getStorage($field->getTargetEntityTypeId())->load($id);
      if ($entity) {
        // Find the json representing the embedded videos using a regex match.
        // The regex pattern is taken from the Video Embed WYSIWYG filter plugin.
        // @see https://git.drupalcode.org/project/video_embed_field/-/blob/8.x-2.x/modules/video_embed_wysiwyg/src/Plugin/Filter/VideoEmbedWysiwyg.php#L133
        $matches = [];
        $text = $entity->{$field->getName()}->value;
        if (!preg_match_all('/(<p>)?(?<json>{(?=.*preview_thumbnail\b)(?=.*settings\b)(?=.*video_url\b)(?=.*settings_summary)(.*)})(<\/p>)?/', $text, $matches)) {
          continue;
        }
        // Iterate through each of the matches. It's possible for a given
        // entity to have more than one video embedded in a given field.
        foreach ($matches['json'] as $delta => $match) {
          // Ensure the JSON string is valid and decode it.
          $embed_data = json_decode($match, TRUE);
          if (!$embed_data || !is_array($embed_data)) {
            continue;
          }
          // Extract the embedded video url from the decoded json data.
          $video_url = $embed_data['video_url'];
          if (empty($video_url)) {
            continue;
          }
          // Depending on the form of $video_url, you may have to do additional
          // processing. The following forms for YouTube urls work well:
          // * https://youtu.be/ABCDEFG1234
          // * https://www.youtube.com/watch?v=ABCDEFG1234
          //
          // However, some other formats are not allowed by Drupal Media such as:
          // x https://www.youtube.com/embed/ABCDEFG1234
          //
          // Just in case, convert from YouTube "embed" url to a "watch" url.
          // You may have to add more custom processing depending on your source.
          $video_url = str_replace('youtube.com/embed/', 'youtube.com/watch?v=',$video_url);
          // Create and save media. (See the pre-requisites.)
          $media = \Drupal::entityTypeManager()->getStorage('media')->create([
            'bundle' => 'remote_video',
            'field_media_oembed_video' => $video_url,
          ]);
          try {
            $media->save();
          }
          catch (Drupal\Core\Entity\EntityStorageException $e) {
            \Drupal::logger('video_embed_wysiwyg_update')->error($e->getMessage());
            continue;
          }
          // Build the media embed code. Note that we align everything in the center for simplicity.
          // If your case is more demanding you can find additional properties on $embed_data.
          $embed = "<drupal-media data-align=\"center\" data-entity-type=\"media\" data-entity-uuid=\"{$media->uuid()}\"></drupal-media>";
          // Replace $match with the embed code in $text
          $text = str_replace($match, $embed, $text);
          // Save the entity with the updated field value.
          $entity->{$field->getName()}->setValue([
            'value' => $text,
            'format' => $entity->{$field->getName()}->format,
            'summary' => $entity->{$field->getName()}->summary,
          ]);
          $entity->save();
        }
      }
    }
  }
}

When using the Video Embed Field field type

If you're using Video Embed Field fields with your content types, paragraphs, etc. then the migration involves the creation of video Media entities and then the replacement of your original fields with references to the new entities. Depending on the number of fields\volume of videos this could be a laborious but you may be able to use the Video Embed Field Media migrate to do most of the heavy lifting. 

Help improve this page

Page status: Needs review

You can: