diff --git a/assets/beep.css b/assets/beep.css index 9d8d899..4a342d4 100755 --- a/assets/beep.css +++ b/assets/beep.css @@ -736,6 +736,66 @@ 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 { width: 100%; height: auto; diff --git a/assets/beep.js b/assets/beep.js index 8804465..6c1dc08 100755 --- a/assets/beep.js +++ b/assets/beep.js @@ -428,10 +428,30 @@ return ''; } + /* ---------- 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 ---------- */ function onClick(e) { 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]'); 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 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 var sel = window.getSelection && window.getSelection(); if (sel && sel.toString()) return; @@ -636,6 +656,12 @@ } 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 if (e.key === 'Enter' && e.target.matches && e.target.matches('[data-beep-gif-search]')) { e.preventDefault(); diff --git a/beep.php b/beep.php index b54350a..6d47875 100755 --- a/beep.php +++ b/beep.php @@ -2,7 +2,7 @@ /** * Plugin Name: Beep * 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 PHP: 7.4 * Author: Sami Ahmed @@ -14,7 +14,7 @@ if (!defined('ABSPATH')) { exit; } -define('BEEP_VERSION', '1.9.0'); +define('BEEP_VERSION', '1.9.1'); define('BEEP_FILE', __FILE__); define('BEEP_DIR', plugin_dir_path(__FILE__)); define('BEEP_URL', plugin_dir_url(__FILE__)); diff --git a/includes/class-quotes.php b/includes/class-quotes.php index 8ceb15d..d320ded 100755 --- a/includes/class-quotes.php +++ b/includes/class-quotes.php @@ -87,6 +87,7 @@ class Beep_Quotes { '' . $time . '' . '' . '
' . $content . '
' . + beep_render_youtube_card($quoted['content']) . ''; } diff --git a/includes/helpers.php b/includes/helpers.php index 4a27acf..299a7b3 100755 --- a/includes/helpers.php +++ b/includes/helpers.php @@ -353,6 +353,7 @@ function beep_render_post($post, $args = []) {
+ post_content); ?>
@@ -611,6 +612,7 @@ function beep_render_reply($comment, $all_comments = [], $depth = 0, $post_id =
+ comment_content); ?>
@@ -710,6 +712,34 @@ function beep_format_content($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('#(?' + . '' + . '' + . '
'; +} + function beep_relative_time($timestamp) { $now = time(); $diff = $now - $timestamp; diff --git a/readme.txt b/readme.txt index 7f57d28..71e969c 100755 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: microblog, twitter, tweets, social, comments, polls, elementor Requires at least: 6.0 Tested up to: 6.9 Requires PHP: 7.4 -Stable tag: 1.9.0 +Stable tag: 1.9.1 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. @@ -46,6 +46,9 @@ Features: == 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 = * 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).