Get first video from the post (both embed and video shortcodes)

The goal is to grab the first video (embed or shortcode) from the post. So we need to check post content if it has embedded videos ( supported by WP ) or [video] shortcodes. Multiple videos and embeds could exist in the single post. If found any - return the very first of them, not depending on order of given patterns to match, but on order they have been inserted into the post.

Here is my progress so far...

This one returns first [video] shortcode. Is there a way to make it look for not only video shortcodes, but for embeds also?

function theme_self_hosted_videos() {
global $post;
$pattern = get_shortcode_regex();
  if ( preg_match_all( '/'. $pattern .'/s', $post-post_content, $matches )  array_key_exists( 2, $matches )  in_array( 'video', $matches[2] ) ) {
    $videos = $matches[0];
    $i = 0;
    foreach ($videos as $video ) {
        if($i == 0) {
            echo do_shortcode($video);
        }
        $i++;
    }
  }
  return false;
}
add_action( 'wp', 'theme_self_hosted_videos' );

Here's my current progress of a function to return first video embed from the post. Not working as expected, though. Obviously depends on $pattern_array order and maybe on the patterns themselves...

function theme_oembed_videos() {

global $post;

// Here is a sample array of patterns for supported video embeds from wp-includes/class-wp-embed.php
$pattern_array = array(
    '#https://youtu\.be/.*#i',
    '#https://(www\.)?youtube\.com/playlist.*#i',
    '#https://(www\.)?youtube\.com/watch.*#i',
    '#http://(www\.)?youtube\.com/watch.*#i',
    '#http://(www\.)?youtube\.com/playlist.*#i',
    '#http://youtu\.be/.*#i',
    '#https?://wordpress.tv/.*#i',
    '#https?://(.+\.)?vimeo\.com/.*#i'
);

foreach ($pattern_array as $pattern) {

    if (preg_match_all($pattern, $post-post_content, $matches)) {
        return wp_oembed_get( $matches[0] );
    }
    return false;

}

}

Topic embed shortcode oembed videos Wordpress

Category Web


Based on @cybmeta answer, improved it a little bit and though I would share

function get_first_embed_media($post_id, $regex_filter = null)
{
    $post = get_post($post_id);
    $content = do_shortcode(apply_filters('the_content', $post->post_content));
    $embeds = get_media_embedded_in_content($content);

    if (!empty($embeds)) {
        // Loop
        foreach ($embeds as $embed) {
            if (
                $regex_filter == null                       ||
                preg_match($regex_filter, $embed)
            ) {
                return $embed;
            }
        }
    }
    
    // No matching embedded found
    return false;
}
$first_video = get_first_embed_media(1234, '/video|youtube|vimeo/')

You can use the get_media_embedded_in_content() function for both shortcode and embedded media (it looks for these HTML tags: audio, video, object, embed, or iframe), just be sure to use content with applied filters and shortcode executed.

For example:

$post_id = 125;
$post = get_post($post_id);

//Get the content, apply filters and execute shortcodes
$content = apply_filters( 'the_content', $post->post_content );
$embeds = get_media_embedded_in_content( $content );

//$embeds is an array and each item is the HTML of an embedded media.
//The first item of the array is the first embedded media in the content
$fist_embedded = $embeds[0];

Addapting te above code to a function similar to yours:

function get_first_embed_media($post_id) {

    $post = get_post($post_id);
    $content = do_shortcode( apply_filters( 'the_content', $post->post_content ) );
    $embeds = get_media_embedded_in_content( $content );

    if( !empty($embeds) ) {
        //return first embed
        return $embeds[0];

    } else {
        //No embeds found
        return false;
    }

}

To limit videos only you could do something like this:

function get_first_embed_media($post_id) {

    $post = get_post($post_id);
    $content = do_shortcode( apply_filters( 'the_content', $post->post_content ) );
    $embeds = get_media_embedded_in_content( $content );

    if( !empty($embeds) ) {
        //check what is the first embed containg video tag, youtube or vimeo
        foreach( $embeds as $embed ) {
            if( strpos( $embed, 'video' ) || strpos( $embed, 'youtube' ) || strpos( $embed, 'vimeo' ) ) {
                return $embed;
            }
        }

    } else {
        //No video embedded found
        return false;
    }

}

Note: There was a bug in get_media_embedded_in_content() that made this answer not working as expected bellow WP 4.2.


You could try mashing all the patterns together in one big regexp or and then doing a simple line by line parse of the content:

function theme_oembed_videos() {

    global $post;

    if ( $post && $post->post_content ) {

        global $shortcode_tags;
        // Make a copy of global shortcode tags - we'll temporarily overwrite it.
        $theme_shortcode_tags = $shortcode_tags;

        // The shortcodes we're interested in.
        $shortcode_tags = array(
            'video' => $theme_shortcode_tags['video'],
            'embed' => $theme_shortcode_tags['embed']
        );
        // Get the absurd shortcode regexp.
        $video_regex = '#' . get_shortcode_regex() . '#i';

        // Restore global shortcode tags.
        $shortcode_tags = $theme_shortcode_tags;

        $pattern_array = array( $video_regex );

        // Get the patterns from the embed object.
        if ( ! function_exists( '_wp_oembed_get_object' ) ) {
            include ABSPATH . WPINC . '/class-oembed.php';
        }
        $oembed = _wp_oembed_get_object();
        $pattern_array = array_merge( $pattern_array, array_keys( $oembed->providers ) );

        // Or all the patterns together.
        $pattern = '#(' . array_reduce( $pattern_array, function ( $carry, $item ) {
            if ( strpos( $item, '#' ) === 0 ) {
                // Assuming '#...#i' regexps.
                $item = substr( $item, 1, -2 );
            } else {
                // Assuming glob patterns.
                $item = str_replace( '*', '(.+)', $item );
            }
            return $carry ? $carry . ')|('  . $item : $item;
        } ) . ')#is';

        // Simplistic parse of content line by line.
        $lines = explode( "\n", $post->post_content );
        foreach ( $lines as $line ) {
            $line = trim( $line );
            if ( preg_match( $pattern, $line, $matches ) ) {
                if ( strpos( $matches[0], '[' ) === 0 ) {
                    $ret = do_shortcode( $matches[0] );
                } else {
                    $ret = wp_oembed_get( $matches[0] );
                }
                return $ret;
            }
        }
    }
}

Also you might want to use transients for the embeds - see this answer I gave.

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.