Open dexter-adams opened 2 years ago
I have a user requesting for play button resizing too: 5330958-zen
, as the video is embedded by a remote iframe, and user couldn't change the styling via CSS or JS.
Request for the ability to use CSS to customize this came up in: https://wordpress.org/support/topic/how-to-customize-jetpack-videopress-player-ui/?view=all
Support References
This comment is automatically generated. Please do not edit it.
The best would be to allow the theme to inject css into the iframe, so all players on a given site could accommodate the rest of the appearance. Also it seems to be the lowest effort solution, as with CSS everyone pretty much gets the freedom they need instead of implementing individual configuration options like play button size, seek bar height, volume icon etc. In a professional site all of these would be customised anyway, so CSS is the most versatile solution imho.
Digging a bit in the code I've found the jetpack_videopress_player_use_iframe
filter here. So I assume it can be used to circumvent the IFRAME embed. I'll try to use that to see if that could be a way to apply the page CSS to the player.
Will come back to update it later.
Meanwhile I think the issue of "how to apply custom css" to the player is related, but in approach it is different than this one about only controlling the play button.
So it looks like jetpack_videopress_player_use_iframe
is kinda broken, because it only works for videos added using a shortcode, e.g. [videopress WrC5GDcm]. However if someone adds the video into the block editor as a video block, the resulting <!-- wp:video {"guid":["WrC5GDcm"],...
block code is not processed by the same code, but rather it is processed by WP_oEmbed
which itself has a pattern as #https?://videopress\.com/v/.*#
but also VideoPress_Shortcode::__construct()
registers an oEmbed handler for the same. And oEmbed simply defaults to IFRAME based rendering.
So on one hand full CSS customisation is possible using a shortcode, however it is rather inconvenient for the editors.
I've just made a content filter that simply replaces the block code with a shortcode, so it is rendered correctly by VideoPress, respecting jetpack_videopress_player_use_iframe
.
add_filter( 'the_content', 'content_check',0);
function content_check($content) {
$content = preg_replace('/<!--\s*wp:video\s*{[^}]*"guid":"([^\"]+)"[^}]*}\s*-->.*<!-- \/wp:video -->/s', '<!-- wp:shortcode -->[videopress $1]<!-- /wp:shortcode -->', $content);
return $content;
}
(Indeed it is very rudimental, but it is only a PoC and with some improvement it could also process the properties and pass them as shortcode attributes.)
Once it is in place the play button size can be limited easily, which is in fact an issue on mobiles and small screens.
.video-js.vjs-videopress .vjs-big-play-button .vjs-icon-placeholder {
max-width: 100px;
max-height: 100px;
background-size: contain;
}
The proper behaviour imho would be for VideoPress to register a pre_render_block
filter, and simply short circuit rendering of wp:video
blocks containing a "guid"
and render them properly instead of leaving it to WP_oEmbed
.
Probably I'll do that myself as well instead of the above workaround as filtering all content using regexp may cost some cpu... I'll post my solution once done for reference.
Another method could be to create another oEmbed provider such as https://public-api.wordpress.com/oembed/?for=develop.wordpress.com&url=https://videopress.com/v/{guid}
which instead of an iframe returns inline code. Then based on the jetpack_videopress_player_use_iframe
filter VideoPress_Shortcode::__construct()
could register either the current or the non-iframe one.
As mentioned above, a better workaround can be implemented using the pre_render_block
filter.
// This ensures that VideoPress shortcodes are rendered inline the DOM instead of as an IFRAME.
add_filter( 'jetpack_videopress_player_use_iframe', '__return_false', 1000);
/**
* We short circuit the rendering of the video blocks for VideoPress videos.
*
* @param string|null $pre_render The pre-rendered content. Default null.
* @param array $parsed_block The block being rendered.
* @param WP_Block|null $parent_block If this is a nested block, a reference to the parent block.
*/
function divert_videopress_render( $pre_render, $parsed_block, $parent_block ) {
if ( $parsed_block['blockName'] !== 'core/video' ) {
return null;
}
$guid = $parsed_block['attrs']['guid'] ?? false;
if ( !$guid ) {
return null;
}
if ( !class_exists('VideoPress_Shortcode') ) {
return null;
}
// We can pass the block attributes to the shortcode in lowercase
$block_attrs = array_change_key_case($parsed_block['attrs'], CASE_LOWER);
// useAverageColor can only be checked from the URL, it is not part of the block attributes.
$useAverageColor = strpos( $parsed_block['innerHTML'], 'useAverageColor=true') > 0;
if ( $useAverageColor ) {
$block_attrs['useaveragecolor'] = 'true';
}
// guid must be added as a first non-associative value for the shortcode to work.
array_unshift( $block_attrs, $guid );
// Render the shortcode
$result = VideoPress_Shortcode::initialize()->shortcode_callback($block_attrs);
// Unfortunately these do not work with the shortcode: seekbarPlayedColor, seekbarLoadingColor, seekbarColor
// but we can replicate custom color behaviour using inline styles targeting the player by unique id.
$styles = [];
if (!$useAverageColor) {
$preg_matches = [];
preg_match( '/<div id="(v-[\w-]+)"/m', $result, $preg_matches);
$video_container_id = $preg_matches[1] ?? null;
if ($video_container_id) {
if (!empty( $parsed_block['attrs']['seekbarColor'] )) {
$color = preg_replace('/[[:cntrl:]]/', '', str_replace([';','}'], '', $parsed_block['attrs']['seekbarColor']));
$styles[] = "#$video_container_id .video-js.vjs-videopress .vjs-slider { background: $color; }";
}
if (!empty( $parsed_block['attrs']['seekbarPlayedColor'] )) {
$color = preg_replace('/[[:cntrl:]]/', '', str_replace([';','}'], '', $parsed_block['attrs']['seekbarPlayedColor']));
$styles[] = "#$video_container_id .video-js.vjs-videopress .vjs-play-progress { background: $color; }";
}
if (!empty( $parsed_block['attrs']['seekbarLoadingColor'] )) {
$color = preg_replace('/[[:cntrl:]]/', '', str_replace([';','}'], '', $parsed_block['attrs']['seekbarLoadingColor']));
$styles[] = "#$video_container_id .video-js.vjs-videopress .vjs-load-progress { background: $color; }";
$styles[] = "#$video_container_id .video-js.vjs-videopress .vjs-load-progress div { background: $color; }";
}
}
}
// Add the custom styles if any
if ( $styles !== [] ) {
$result .= "<style>\n" . implode("\n", $styles) . "\n</style>";
}
// We replace the same part that the oEmbed would have replaced.
if ( !empty($result) ) {
return preg_replace(
[ '#^https?://videopress.com/v/.*#im', '|^https?://v\.wordpress\.com/([a-zA-Z\d]{8})(.+)?$|im' ],
[ $result, $result ],
$parsed_block['innerHTML']
);
}
}
add_filter( 'pre_render_block', 'divert_videopress_render', 99, 3);
/**
* We prevent oEmbed to render the video blocks.
*
* @param string|false $html The cached HTML result, stored in post meta.
* @param string $url The attempted embed URL.
* @param array $attr An array of shortcode attributes.
* @param int $post_ID Post ID.
*/
function prevent_videopress_oembed_override( $html, string $url, array $attr, int $post_ID ) {
// For the preview to work in the editor we need the oEmbed (queried over wp-json api).
if (!wp_is_json_request()) {
$patterns = [
'#https?://videopress\.com/v/.*#', // Default WP
'#^https?://videopress.com/v/.*#', // JetPack
'|^https?://v\.wordpress\.com/([a-zA-Z\d]{8})(.+)?$|i', // JetPack
];
foreach ( $patterns as $pattern ) {
if ( preg_match( $pattern, $url ) ) {
return $url; // We skip the embed, will handle when the block is rendered.
}
}
}
return $html;
}
add_filter( 'embed_oembed_html', 'prevent_videopress_oembed_override', 10, 4);
// Some demonstration of adding global styling for the player
function videopress_global_styles() { ?>
<style>
/* Limit play button size */
.video-js.vjs-videopress .vjs-big-play-button .vjs-icon-placeholder {
max-width: 100px;
max-height: 100px;
background-size: contain !important;
}
/* Make the shortcode player responsive */
.video-js.vjs-videopress .vjs-tech {
position: static;
display: block;
}
.video-js.vjs-videopress {
width: 100%;
height: auto;
}
.video-js.vjs-videopress .videopress-overlay {
top: 0;
}
.video-player {
padding-bottom: 56.25%; /* 16:9 as min height to prevent flashing during load */
height: 0;
}
</style>
<?php }
add_action('wp_head', 'videopress_global_styles');
One shortcoming of this is due to the limitation of the shortcode embed is that the video is not really responsive by default. Few css adjustments can make it responsive, however the video height may flash during load which is an issue too, so I've just set a 16:9 min height on the player by utilising the padding-bottom aspect ratio trick. But in other usecases this may be not enough, for example when a site deploys videos in various aspect ratios...
The issues are not impossible to fix, but it is also a matter of taste which approach is acceptable. I admit this one is a bit hackish, but afaics the oEmbed part only plays nicely as long as it is a fallback for a more controlled experience with higher customisability.
The solution can be turned into production code I think, but good few decisions should be made about how this thing should really work, mainly should 'jetpack_videopress_player_use_iframe' be respected in all cases, not just for shortcodes.
Also the shortcode player could use a fix to dynamically calculate the player size based on the known aspect ratio of the video using JS, pretty much the same as the iframe player does. Also it would be nice for the shortcode player to natively support seekbarPlayedColor
, seekbarLoadingColor
, seekbarColor
just for the sake of consistency.
For the time being, this workaround suits my needs and I hope others will find it useful as well.
There isn't a way for a user to change the enormous play button. It would be most ideal to provide hooks to override CSS attributes but at the very least an option for play button (icon) size.