[grade=B] v1.9.1: YouTube links auto-render as video cards in beeps, replies, and quotes (click-to-play, nocookie embed)
This commit is contained in:
@@ -736,6 +736,66 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* YouTube card (v1.9.1) */
|
||||||
|
.beep-embed .beep-yt-card {
|
||||||
|
position: relative;
|
||||||
|
margin-top: 12px;
|
||||||
|
border-radius: 16px;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--beep-border);
|
||||||
|
cursor: pointer;
|
||||||
|
background: #000;
|
||||||
|
aspect-ratio: 16 / 9;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beep-embed .beep-yt-thumb {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beep-embed .beep-yt-play {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
width: 68px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: rgba(255, 0, 0, 0.85);
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
|
||||||
|
transition: transform 0.15s ease, background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beep-embed .beep-yt-card:hover .beep-yt-play,
|
||||||
|
.beep-embed .beep-yt-card:focus-visible .beep-yt-play {
|
||||||
|
background: #f00;
|
||||||
|
transform: scale(1.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.beep-embed .beep-yt-play svg {
|
||||||
|
fill: #fff;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.beep-embed .beep-yt-card iframe {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.beep-embed .beep-gif-embed img {
|
.beep-embed .beep-gif-embed img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
|
|||||||
+27
-1
@@ -428,10 +428,30 @@
|
|||||||
return '<svg viewBox="0 0 24 24" width="64" height="64" fill="#4a90c4" aria-hidden="true"><path d="M17.2 1.4c-.8-.1-1.5.2-2 .6C14.3.9 13.2.7 12.1 1c-1.4.4-2.3 1.7-2.1 3.2-2.1-.2-4.2-.9-5.7-2.5-.3-.3-.8-.3-1 0-.6.8-.9 1.7-.9 2.6 0 1.2.5 2.2 1.3 2.9-.3-.1-.6-.1-.8-.3-.4-.2-.9 0-.8.5.3 1.6 1.3 2.8 2.7 3.4-.3 0-.5 0-.8-.1-.4-.1-.8.3-.5.7.8 1.4 2.2 2.3 3.8 2.5-1.3.8-2.8 1.2-4.4 1.2-.5 0-.8.5-.5.9 1.1 1.1 2.9 1.6 5.5 1.6 5.4 0 9.8-4.4 9.8-9.8v-.2c.9-.6 1.6-1.4 2.1-2.4.2-.4-.2-.8-.6-.6-.5.2-1 .3-1.5.4.5-.5.9-1.1 1-1.8.1-.4-.4-.7-.7-.5-.7.4-1.5.6-2.3.7-.8-.7-1.8-1.1-2.9-1.1zm-.8 1.6c.7-.1 1.4.2 1.9.7.2.2.5.3.8.2.3-.1.6-.1.9-.3-.2.3-.5.5-.8.6-.3.1-.4.5-.3.7.2.3.1.5 0 .8v.2c0 4.7-3.8 8.5-8.5 8.5-1.6 0-2.9-.2-3.8-.6 1.7-.3 3.2-1 4.3-2.2.3-.3.1-.8-.3-.8-1.4 0-2.7-.6-3.5-1.7.4 0 .9-.1 1.3-.2.4-.1.4-.7 0-.8-1.5-.3-2.6-1.3-3-2.6.5.1 1 .2 1.5.1.4 0 .5-.6.1-.8C5.2 5.5 4.5 4.5 4.5 3.4c0-.4.1-.9.3-1.3 1.8 1.5 4 2.4 6.4 2.5.3 0 .5-.2.5-.5-.2-1.1.4-2.1 1.4-2.4.7-.2 1.4 0 1.9.4.3.2.7.1.9-.1.3-.4.8-.7 1.3-.7.4.1.8.2 1.2.4-.3.3-.5.6-.5 1 0 .3.3.5.5.4z"/></svg>';
|
return '<svg viewBox="0 0 24 24" width="64" height="64" fill="#4a90c4" aria-hidden="true"><path d="M17.2 1.4c-.8-.1-1.5.2-2 .6C14.3.9 13.2.7 12.1 1c-1.4.4-2.3 1.7-2.1 3.2-2.1-.2-4.2-.9-5.7-2.5-.3-.3-.8-.3-1 0-.6.8-.9 1.7-.9 2.6 0 1.2.5 2.2 1.3 2.9-.3-.1-.6-.1-.8-.3-.4-.2-.9 0-.8.5.3 1.6 1.3 2.8 2.7 3.4-.3 0-.5 0-.8-.1-.4-.1-.8.3-.5.7.8 1.4 2.2 2.3 3.8 2.5-1.3.8-2.8 1.2-4.4 1.2-.5 0-.8.5-.5.9 1.1 1.1 2.9 1.6 5.5 1.6 5.4 0 9.8-4.4 9.8-9.8v-.2c.9-.6 1.6-1.4 2.1-2.4.2-.4-.2-.8-.6-.6-.5.2-1 .3-1.5.4.5-.5.9-1.1 1-1.8.1-.4-.4-.7-.7-.5-.7.4-1.5.6-2.3.7-.8-.7-1.8-1.1-2.9-1.1zm-.8 1.6c.7-.1 1.4.2 1.9.7.2.2.5.3.8.2.3-.1.6-.1.9-.3-.2.3-.5.5-.8.6-.3.1-.4.5-.3.7.2.3.1.5 0 .8v.2c0 4.7-3.8 8.5-8.5 8.5-1.6 0-2.9-.2-3.8-.6 1.7-.3 3.2-1 4.3-2.2.3-.3.1-.8-.3-.8-1.4 0-2.7-.6-3.5-1.7.4 0 .9-.1 1.3-.2.4-.1.4-.7 0-.8-1.5-.3-2.6-1.3-3-2.6.5.1 1 .2 1.5.1.4 0 .5-.6.1-.8C5.2 5.5 4.5 4.5 4.5 3.4c0-.4.1-.9.3-1.3 1.8 1.5 4 2.4 6.4 2.5.3 0 .5-.2.5-.5-.2-1.1.4-2.1 1.4-2.4.7-.2 1.4 0 1.9.4.3.2.7.1.9-.1.3-.4.8-.7 1.3-.7.4.1.8.2 1.2.4-.3.3-.5.6-.5 1 0 .3.3.5.5.4z"/></svg>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- YouTube cards (v1.9.1) ---------- */
|
||||||
|
function loadYtPlayer(card) {
|
||||||
|
if (!card || card.getAttribute('data-beep-yt-loaded') === '1') return;
|
||||||
|
card.setAttribute('data-beep-yt-loaded', '1');
|
||||||
|
var vid = card.getAttribute('data-beep-yt');
|
||||||
|
if (!vid) return;
|
||||||
|
var iframe = document.createElement('iframe');
|
||||||
|
iframe.src = 'https://www.youtube-nocookie.com/embed/' + encodeURIComponent(vid) + '?autoplay=1&rel=0';
|
||||||
|
iframe.title = 'YouTube video player';
|
||||||
|
iframe.allow = 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share';
|
||||||
|
iframe.allowFullscreen = true;
|
||||||
|
iframe.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');
|
||||||
|
card.innerHTML = '';
|
||||||
|
card.appendChild(iframe);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Click delegation ---------- */
|
/* ---------- Click delegation ---------- */
|
||||||
function onClick(e) {
|
function onClick(e) {
|
||||||
var t = e.target;
|
var t = e.target;
|
||||||
|
|
||||||
|
// YouTube card: click swaps the thumbnail for the inline player
|
||||||
|
var ytCard = t.closest('.beep-yt-card');
|
||||||
|
if (ytCard) { e.preventDefault(); loadYtPlayer(ytCard); return; }
|
||||||
|
|
||||||
var likePost = t.closest('[data-beep-like-post]');
|
var likePost = t.closest('[data-beep-like-post]');
|
||||||
if (likePost) { e.preventDefault(); if (requireLogin()) handleLike(likePost, 'like/' + likePost.dataset.beepLikePost); return; }
|
if (likePost) { e.preventDefault(); if (requireLogin()) handleLike(likePost, 'like/' + likePost.dataset.beepLikePost); return; }
|
||||||
|
|
||||||
@@ -602,7 +622,7 @@
|
|||||||
|
|
||||||
// Click anywhere else on a feed post opens the conversation
|
// Click anywhere else on a feed post opens the conversation
|
||||||
var openPost = t.closest('[data-beep-open-thread]');
|
var openPost = t.closest('[data-beep-open-thread]');
|
||||||
if (openPost && !t.closest('a, button, label, input, textarea, audio, video, .beep-media-grid-item, .beep-gif-embed, [data-beep-reply-form]')) {
|
if (openPost && !t.closest('a, button, label, input, textarea, audio, video, .beep-media-grid-item, .beep-gif-embed, .beep-yt-card, [data-beep-reply-form]')) {
|
||||||
// Don't hijack text selection
|
// Don't hijack text selection
|
||||||
var sel = window.getSelection && window.getSelection();
|
var sel = window.getSelection && window.getSelection();
|
||||||
if (sel && sel.toString()) return;
|
if (sel && sel.toString()) return;
|
||||||
@@ -636,6 +656,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onKeydown(e) {
|
function onKeydown(e) {
|
||||||
|
// Enter/Space on a focused YouTube card plays it (card has tabindex="0")
|
||||||
|
if ((e.key === 'Enter' || e.key === ' ') && e.target.matches && e.target.matches('.beep-yt-card')) {
|
||||||
|
e.preventDefault();
|
||||||
|
loadYtPlayer(e.target);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Enter in the GIF search box runs the search
|
// Enter in the GIF search box runs the search
|
||||||
if (e.key === 'Enter' && e.target.matches && e.target.matches('[data-beep-gif-search]')) {
|
if (e.key === 'Enter' && e.target.matches && e.target.matches('[data-beep-gif-search]')) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
/**
|
/**
|
||||||
* Plugin Name: Beep
|
* Plugin Name: Beep
|
||||||
* Description: A Twitter-style microblog for WordPress, with likes, replies, and Gravatars. Use the [beep_feed] shortcode on any page.
|
* Description: A Twitter-style microblog for WordPress, with likes, replies, and Gravatars. Use the [beep_feed] shortcode on any page.
|
||||||
* Version: 1.9.0
|
* Version: 1.9.1
|
||||||
* Requires at least: 6.0
|
* Requires at least: 6.0
|
||||||
* Requires PHP: 7.4
|
* Requires PHP: 7.4
|
||||||
* Author: Sami Ahmed
|
* Author: Sami Ahmed
|
||||||
@@ -14,7 +14,7 @@ if (!defined('ABSPATH')) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
define('BEEP_VERSION', '1.9.0');
|
define('BEEP_VERSION', '1.9.1');
|
||||||
define('BEEP_FILE', __FILE__);
|
define('BEEP_FILE', __FILE__);
|
||||||
define('BEEP_DIR', plugin_dir_path(__FILE__));
|
define('BEEP_DIR', plugin_dir_path(__FILE__));
|
||||||
define('BEEP_URL', plugin_dir_url(__FILE__));
|
define('BEEP_URL', plugin_dir_url(__FILE__));
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ class Beep_Quotes {
|
|||||||
'<span class="beep-quoted-time">' . $time . '</span>' .
|
'<span class="beep-quoted-time">' . $time . '</span>' .
|
||||||
'</div>' .
|
'</div>' .
|
||||||
'<div class="beep-quoted-text">' . $content . '</div>' .
|
'<div class="beep-quoted-text">' . $content . '</div>' .
|
||||||
|
beep_render_youtube_card($quoted['content']) .
|
||||||
'</div></div>';
|
'</div></div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -353,6 +353,7 @@ function beep_render_post($post, $args = []) {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="beep-text"><?php echo $content; ?></div>
|
<div class="beep-text"><?php echo $content; ?></div>
|
||||||
|
<?php echo beep_render_youtube_card($post->post_content); ?>
|
||||||
<?php if (!empty($beep_images)) : ?>
|
<?php if (!empty($beep_images)) : ?>
|
||||||
<div class="beep-media-attachments">
|
<div class="beep-media-attachments">
|
||||||
<?php echo Beep_Media::render_image_gallery($beep_images); ?>
|
<?php echo Beep_Media::render_image_gallery($beep_images); ?>
|
||||||
@@ -611,6 +612,7 @@ function beep_render_reply($comment, $all_comments = [], $depth = 0, $post_id =
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<div class="beep-text"><?php echo $content; ?></div>
|
<div class="beep-text"><?php echo $content; ?></div>
|
||||||
|
<?php echo beep_render_youtube_card($comment->comment_content); ?>
|
||||||
|
|
||||||
<?php if (!empty($reply_images)) : ?>
|
<?php if (!empty($reply_images)) : ?>
|
||||||
<div class="beep-media-attachments">
|
<div class="beep-media-attachments">
|
||||||
@@ -710,6 +712,34 @@ function beep_format_content($text) {
|
|||||||
return $text;
|
return $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract the first YouTube video ID from a text string, if any.
|
||||||
|
* Supports youtube.com/watch?v=, youtu.be/, shorts/, embed/, live/.
|
||||||
|
* Returns the 11-char video ID or ''.
|
||||||
|
*/
|
||||||
|
function beep_youtube_id_from_text($text) {
|
||||||
|
if (!preg_match_all('#(?<![\w.-])(?:https?://)?(?:www\.|m\.)?(?:youtube\.com/(?:watch\?[^#\s]*v=|shorts/|embed/|live/)|youtu\.be/)([A-Za-z0-9_-]{11})(?![A-Za-z0-9_-])#i', (string) $text, $m)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return $m[1][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a click-to-play YouTube card for the first YouTube link in $text.
|
||||||
|
* Thumbnail (hqdefault) + play button; JS swaps in the iframe on click.
|
||||||
|
* Returns '' when no YouTube link is present.
|
||||||
|
*/
|
||||||
|
function beep_render_youtube_card($text) {
|
||||||
|
$vid = beep_youtube_id_from_text($text);
|
||||||
|
if ($vid === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return '<div class="beep-yt-card" data-beep-yt="' . esc_attr($vid) . '" role="button" tabindex="0" aria-label="Play embedded YouTube video">'
|
||||||
|
. '<img class="beep-yt-thumb" src="https://i.ytimg.com/vi/' . esc_attr($vid) . '/hqdefault.jpg" alt="" loading="lazy">'
|
||||||
|
. '<span class="beep-yt-play"><svg viewBox="0 0 24 24" width="28" height="28" fill="currentColor" aria-hidden="true"><path d="M8 5v14l11-7z"/></svg></span>'
|
||||||
|
. '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
function beep_relative_time($timestamp) {
|
function beep_relative_time($timestamp) {
|
||||||
$now = time();
|
$now = time();
|
||||||
$diff = $now - $timestamp;
|
$diff = $now - $timestamp;
|
||||||
|
|||||||
+4
-1
@@ -4,7 +4,7 @@ Tags: microblog, twitter, tweets, social, comments, polls, elementor
|
|||||||
Requires at least: 6.0
|
Requires at least: 6.0
|
||||||
Tested up to: 6.9
|
Tested up to: 6.9
|
||||||
Requires PHP: 7.4
|
Requires PHP: 7.4
|
||||||
Stable tag: 1.9.0
|
Stable tag: 1.9.1
|
||||||
License: GPLv2 or later
|
License: GPLv2 or later
|
||||||
|
|
||||||
A Twitter-style microblog for WordPress. Posts, likes, replies, polls, quotes, GIFs, and Gravatars. Use [beep_feed] shortcode or Elementor widget.
|
A Twitter-style microblog for WordPress. Posts, likes, replies, polls, quotes, GIFs, and Gravatars. Use [beep_feed] shortcode or Elementor widget.
|
||||||
@@ -46,6 +46,9 @@ Features:
|
|||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 1.9.1 =
|
||||||
|
* YouTube links in beeps, replies, and quoted beeps automatically render as a video card (thumbnail + play button; click to play inline). Supports youtube.com/watch, youtu.be, shorts, embed, and live URLs.
|
||||||
|
|
||||||
= 1.9.0 =
|
= 1.9.0 =
|
||||||
* Reply composer now supports full media: images (up to 4), GIFs, voice notes, quotes, and polls. The reply form has the same toolbar as the top-level composer.
|
* Reply composer now supports full media: images (up to 4), GIFs, voice notes, quotes, and polls. The reply form has the same toolbar as the top-level composer.
|
||||||
* Replies can be posted with media only — no text required (server gate mirrors the v1.8.1 post rule).
|
* Replies can be posted with media only — no text required (server gate mirrors the v1.8.1 post rule).
|
||||||
|
|||||||
Reference in New Issue
Block a user