Skip to content

Commit

Permalink
Issue #841: Native AMP video playlists.
Browse files Browse the repository at this point in the history
Create a custom shortcode handler for video playlists.
Use <amp-video> and <amp-state>, on Weston't suggestion.
This still doesn't support audio playlists.
  • Loading branch information
Ryan Kienstra committed Feb 11, 2018
1 parent 1ab37c6 commit 00a3f08
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 0 deletions.
1 change: 1 addition & 0 deletions includes/amp-helper-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ function amp_get_content_embed_handlers( $post = null ) {
'AMP_Vine_Embed_Handler' => array(),
'AMP_Facebook_Embed_Handler' => array(),
'AMP_Pinterest_Embed_Handler' => array(),
'AMP_Playlist_Embed_Handler' => array(),
'AMP_Reddit_Embed_Handler' => array(),
'AMP_Tumblr_Embed_Handler' => array(),
'AMP_Gallery_Embed_Handler' => array(),
Expand Down
1 change: 1 addition & 0 deletions includes/class-amp-autoloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class AMP_Autoloader {
'AMP_Issuu_Embed_Handler' => 'includes/embeds/class-amp-issuu-embed-handler',
'AMP_Meetup_Embed_Handler' => 'includes/embeds/class-amp-meetup-embed-handler',
'AMP_Pinterest_Embed_Handler' => 'includes/embeds/class-amp-pinterest-embed',
'AMP_Playlist_Embed_Handler' => 'includes/embeds/class-amp-playlist-embed-handler',
'AMP_Reddit_Embed_Handler' => 'includes/embeds/class-amp-reddit-embed-handler',
'AMP_SoundCloud_Embed_Handler' => 'includes/embeds/class-amp-soundcloud-embed',
'AMP_Tumblr_Embed_Handler' => 'includes/embeds/class-amp-tumblr-embed-handler',
Expand Down
112 changes: 112 additions & 0 deletions includes/embeds/class-amp-playlist-embed-handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php
/**
* Class AMP_Playlist_Embed_Handler
*
* @package AMP
* @since 0.7
*/

/**
* Class AMP_Playlist_Embed_Handler
*/
class AMP_Playlist_Embed_Handler extends AMP_Base_Embed_Handler {

/**
* The ID of the individual playlist.
*
* @var int
*/
public static $playlist_id = 0;

/**
* Registers the playlist shortcode.
*/
public function register_embed() {
add_shortcode( 'playlist', array( $this, 'shortcode' ) );
}

/**
* Unregisters the playlist shortcode.
*
* @return void.
*/
public function unregister_embed() {
remove_shortcode( 'playlist' );
}

/**
* Outputs an AMP-compliant playlist shortcode.
*
* Uses the JSON that wp_playlist_shortcode() produces.
* But outputs an <amp-video>, and an <amp-state> to change the current video.
*
* @global content_width.
* @param array $attr The playlist attributes.
* @return string Playlist shortcode markup.
*/
public function shortcode( $attr ) {
global $content_width;

$markup = wp_playlist_shortcode( $attr );
preg_match( '/(?s)\<script [^>]* class="wp-playlist-script"\>[^<]*?(.*).*?\<\/script\>/', $markup, $matches );
if ( empty( $matches[1] ) ) {
return;
}
$data = json_decode( $matches[1], true );
if ( ! isset( $data['tracks'], $data['tracks'][0]['src'] ) ) {
return;
}

$amp_state = array(
'currentVideo' => '0',
);
foreach ( $data['tracks'] as $index => $track ) {
$amp_state[ $index ] = array(
'videoUrl' => isset( $track['src'] ) ? $track['src'] : '',
'thumb' => isset( $track['thumb']['src'] ) ? $track['thumb']['src'] : '',
);
}
$playlist = 'playlist' . self::$playlist_id++;
$dimensions = isset( $data['tracks'][0]['dimensions']['resized'] ) ? $data['tracks'][0]['dimensions']['resized'] : null;
$width = isset( $dimensions['width'] ) ? $dimensions['width'] : $content_width;
$height = isset( $dimensions['height'] ) ? $dimensions['height'] : null;

ob_start();
?>
<div class="wp-playlist wp-video-playlist wp-playlist-light">
<amp-state id="<?php echo esc_attr( $playlist ); ?>">
<script type="application/json">
<?php echo wp_unslash( wp_json_encode( $amp_state ) ); // WPCS: XSS ok. ?>
</script>
</amp-state>
<amp-video id="amp-video" src="<?php echo esc_url( $data['tracks'][0]['src'] ); ?>" [src]="<?php echo esc_attr( $playlist ); ?>[<?php echo esc_attr( $playlist ); ?>.currentVideo].videoUrl" width="<?php echo esc_attr( $width ); ?>" height="<?php echo isset( $height ) ? esc_attr( $height ) : ''; ?>" controls></amp-video>
<div class="wp-playlist-tracks">
<?php
$i = 1;
foreach ( $data['tracks'] as $index => $track ) {
if ( ! empty( $track['caption'] ) ) {
$title = $track['caption'];
} elseif ( ! empty( $track['title'] ) ) {
$title = $track['title'];
}
?>
<div class="wp-playlist-item">
<a class="wp-playlist-caption" on="tap:AMP.setState({<?php echo esc_attr( $playlist ); ?>: {currentVideo: <?php echo esc_attr( $index ); ?>}})">
<?php echo esc_html( $i . '.' ); ?> <span class="wp-playlist-item-title"><?php echo isset( $title ) ? esc_html( $title ) : ''; ?></span>
</a>
<?php if ( isset( $track['meta']['length_formatted'] ) ) : ?>
<div class="wp-playlist-item-length"><?php echo esc_html( $track['meta']['length_formatted'] ); ?></div>
<?php endif; ?>
</div>
<?php
$i++;
}
?>
</div>
</div>
<?php
return ob_get_clean(); // WPCS: XSS ok.
}

}

91 changes: 91 additions & 0 deletions tests/test-class-amp-playlist-embed-handler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
/**
* Tests for AMP_Playlist_Embed_Handler.
*
* @package AMP
* @since 0.7
*/

/**
* Tests for AMP_Playlist_Embed_Handler.
*
* @covers AMP_Playlist_Embed_Handler
*/
class Test_AMP_Playlist_Embed_Handler extends WP_UnitTestCase {

/**
* Instance of the tested class.
*
* @var AMP_Playlist_Embed_Handler.
*/
public $instance;

/**
* Set up test.
*/
public function setUp() {
parent::setUp();
$this->instance = new AMP_Playlist_Embed_Handler();
}

/**
* Tear down test.
*/
public function tearDown() {
wp_dequeue_style( 'wp-mediaelement' );
}

/**
* Test register_embed.
*
* @covers AMP_Playlist_Embed_Handler::register_embed()
*/
public function test_register_embed() {
global $shortcode_tags;
$shortcode = 'playlist';
$this->assertFalse( isset( $shortcode_tags[ $shortcode ] ) );
$this->instance->register_embed();
$this->assertEquals( 'AMP_Playlist_Embed_Handler', get_class( $shortcode_tags[ $shortcode ][0] ) );
$this->assertEquals( 'shortcode', $shortcode_tags[ $shortcode ][1] );
$this->instance->unregister_embed();
}

/**
* Test unregister_embed.
*
* @covers AMP_Playlist_Embed_Handler::unregister_embed()
*/
public function test_unregister_embed() {
global $shortcode_tags;
$shortcode = 'playlist';
$this->instance->unregister_embed();
$this->assertFalse( isset( $shortcode_tags[ $shortcode ] ) );
}

/**
* Test shortcode.
*
* Logic for creating the upload object copied from Tests_Media.
*
* @covers AMP_Playlist_Embed_Handler::shortcode()
*/
public function test_shortcode() {
$id_mp4 = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/uploads/small-video.mp4' );
$id_mkv = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/uploads/small-video.mkv' );
$ids = array(
$id_mp4,
$id_mkv,
);
$attr = array(
'ids' => implode( ',', $ids ),
'type' => 'video',
);
$playlist = $this->instance->shortcode( $attr );
$this->assertContains( '<amp-video', $playlist );
$this->assertContains( '<amp-state', $playlist );
$this->assertContains( 'small-video', $playlist );
$this->assertContains( '[src]="playlist0[playlist0.currentVideo].videoUrl"', $playlist );
$this->assertContains( 'on="tap:AMP.setState({playlist0: {currentVideo: 0}})"', $playlist );
}

}

0 comments on commit 00a3f08

Please sign in to comment.