Skip to content

VideoPress: add a playlist block - #51332

Merged
heydemoura merged 25 commits into
trunkfrom
add/videopress-playlist-block
Aug 18, 2026
Merged

heydemoura merged 25 commits into
trunkfrom
add/videopress-playlist-block

Conversation

@heydemoura

@heydemoura heydemoura commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #17402
Fixes VIDP-370

Proposed changes

Adds a new dynamic block, Video Playlist (videopress/playlist), to the videopress package (shipping through both the Jetpack and standalone VideoPress plugins): a VideoPress-powered list of videos in a single customizable player, built from your VideoPress library or video links.

Features included

Editor

  • The canvas is a live, non-editable preview mirroring the front end exactly (same markup and stylesheet); clicking an entry switches the previewed video. All management lives in the block sidebar.
  • Adding videos: paste a VideoPress URL or GUID (Enter or the Add button, which stays disabled until the field has content) or pick several videos at once from the Media Library. Title, thumbnail, duration and resolution are read from the video data — nothing is hand-edited.
  • Failure states: unrecognized links and unreadable video data show a "No video found at that link" error and add nothing; adding a duplicate asks first with explicit "Add anyway" / "Cancel"; an "Adding…" busy state shows a "Reading metadata…" skeleton row.
  • Playlist manager: drag-and-drop reordering with a drop indicator, keyboard reordering (focus a handle, press ↑ / ↓), per-entry remove, entry count and total duration; playlists over 8 entries gain a filter input and position numbers.
  • Settings tab: Add a video, Playlist, Playback — Autoplay next, Mute by default, Loop playlist (looping implies autoplay: the toggle shows checked and locked) — and Show on each entry (Thumbnail, Title, Resolution, Duration, Position number, Total runtime).
  • Styles tab: a three-way Layout picker with fixed-aspect mini wireframes (Side rail / Grid / Strip) plus a Dark player surface toggle, and a Typography panel selecting the entry-title font from the theme's theme.json presets (hidden on themes without font presets).
  • Centered empty-state placeholder with the URL form, an "or" divider, and the Media Library button.

Front end

  • Dynamic PHP render: GUIDs validated, everything escaped, stored display fields ignored, empty playlists render nothing; the JWT token bridge is enqueued for private-video playback.
  • Three layouts from one markup structure:
    • Side rail: player beside an "Up next" rail (count · total duration in its header) that never grows past the player column — the entry list scrolls internally.
    • Grid: thumbnail grid with a "Playing" badge and outline on the current card, plus a right-aligned playlist runtime row.
    • Strip: compact numbered horizontal scroller with a "1 / 24 · 5:47:10 total" progress counter, built for long playlists.
  • Scroll-hint fades: a gradient at the side rail's bottom and the strip's trailing edge (RTL-aware) appears only while more entries hide beyond the scroll position — mirrored in the editor preview.
  • Playback: clicking an entry loads it into the player; the player's origin-checked videopress_ended message advances to the next entry, loops back to the first when looping is on, and "Mute by default" rides on every embed URL.
  • Live display metadata: block attributes persist only the video reference and numeric metadata (guid, durationMs, height). Titles and posters are never stored — the editor caches them per session from the videos API and the view script hydrates each entry (and falls back to "Video N" for e.g. private videos), so editor and page always reflect the videos' current metadata. The player renders its own title overlay, so no duplicate now-playing text is rendered.
  • Theme typography integration: the chosen font preset slug is emitted as a CSS custom property pointing at the theme's --wp--preset--font-family--* variable, identically in the editor and the render.

Covered by 62 Jest tests (editor flows, view-script playback/hydration/scroll behavior, formatting utilities, block registration) and 14 PHPUnit tests (registration, rendering, sanitization, escaping, playback flags).

Related product discussion/links

Does this pull request change what data or activity we track or use?

No.

Testing instructions

  • Use a site with VideoPress active (Jetpack with the VideoPress module, or the standalone VideoPress plugin) and a few videos uploaded to VideoPress.
  • Add a "Video Playlist" block: in the centered placeholder or the "Add a video" panel, paste a VideoPress URL or GUID (the Add button enables once you type) — the entry appears with title, thumbnail, duration and resolution read from the video data. Also add several at once via Media Library.
  • Paste a non-VideoPress URL — a "No video found at that link" error appears and nothing is added. Add the same video twice — "Add anyway" / "Cancel" is offered.
  • In the "Playlist" panel, reorder by dragging and with ↑ / ↓ on a handle; remove with ×; with more than 8 videos, a filter input and position numbers appear. The canvas preview mirrors every change.
  • In the Styles tab, switch between Side rail, Grid and Strip, toggle "Dark player surface", and pick an entry-title font from the Typography panel (theme with theme.json fonts required). In Settings → Playback, try Autoplay next, Mute by default, and Loop playlist (note it locks Autoplay on). In "Show on each entry", flip each display toggle.
  • With many videos in the side rail, confirm the rail stays capped at the player's height, scrolls internally, and shows a bottom fade until you reach the end; in the strip layout, the same fade appears at the trailing edge horizontally.
  • Publish and view the post: entry titles/thumbnails load from live video data; clicking an entry plays it; with autoplay on, the next video starts when one ends (looping back to the first when Loop playlist is on; muted when Mute by default is on).
  • Rename a video in VideoPress, then reload the editor and the published post — both show the new title without re-saving the post.

🤖 Generated with Claude Code

@heydemoura
heydemoura requested a review from a team as a code owner August 17, 2026 19:36
@heydemoura heydemoura added Enhancement Changes to an existing feature — removing, adding, or changing parts of it [Status] Needs Review This PR is ready for review. labels Aug 17, 2026
@heydemoura heydemoura self-assigned this Aug 17, 2026
@github-actions

github-actions Bot commented Aug 17, 2026 •

Copy link
Copy Markdown
Contributor

Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

  • To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate the Jetpack Beta plugin. Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the add/videopress-playlist-block branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack add/videopress-playlist-block

Interested in more tips and information?

  • In your local development environment, use the jetpack rsync command to sync your changes to a WoA dev blog.
  • Read more about our development workflow here: PCYsg-eg0-p2
  • Figure out when your changes will be shipped to customers here: PCYsg-eg5-p2
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for your PR!

When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:

  • ✅ Include a description of your PR changes.
  • ✅ Add a "[Status]" label (In Progress, Needs Review, ...).
  • ✅ Add testing instructions.
  • ✅ Specify whether this PR includes any changes to data or privacy.
  • ✅ Add changelog entries to affected projects

This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖


Follow this PR Review Process:

  1. Ensure all required checks appearing at the bottom of this PR are passing.
  2. Make sure to test your changes on all platforms that it applies to. You're responsible for the quality of the code you ship.
  3. You can use GitHub's Reviewers functionality to request a review.
  4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!

@jp-launch-control

jp-launch-control Bot commented Aug 17, 2026 •

Copy link
Copy Markdown

Code Coverage Summary

Coverage changed in 5 files.

File Coverage Δ% Δ Uncovered
projects/packages/videopress/src/class-initializer.php 374/427 (87.59%) 12.12% 1 ❤️‍🩹
projects/packages/videopress/src/class-status.php 4/6 (66.67%) 16.67% -1 💚
projects/packages/videopress/src/client/lib/url/index.ts 47/63 (74.60%) 1.59% -1 💚
projects/packages/videopress/src/client/block-editor/blocks/video/components/icons/index.jsx 2/2 (100.00%) 100.00% -2 💚
projects/packages/videopress/src/class-jwt-token-bridge.php 23/24 (95.83%) 83.33% -20 💚

6 files are newly checked for coverage. Only the first 5 are listed here.

File Coverage
projects/packages/videopress/src/client/block-editor/blocks/playlist/view.ts 74/77 (96.10%) 💚
projects/packages/videopress/src/client/block-editor/blocks/playlist/utils.ts 32/33 (96.97%) 💚
projects/packages/videopress/src/client/block-editor/blocks/playlist/edit.tsx 203/208 (97.60%) 💚
projects/packages/videopress/src/client/block-editor/blocks/playlist/index.ts 3/3 (100.00%) 💚
projects/packages/videopress/src/client/block-editor/blocks/playlist/use-publish-tracking.ts 13/13 (100.00%) 💚

Full summary · PHP report · JS report

A fresh implementation of the Video Playlist block design: the editor
canvas is a live, non-editable preview (player plus entry list), and all
playlist management lives in the block settings sidebar — add videos by
VideoPress URL/GUID or from the media library with metadata (title,
thumbnail, duration, resolution) read from the video data, drag or
arrow-key reordering, duplicate detection with Add anyway/Cancel, and a
filter plus position numbers on long playlists. Three server-rendered
front-end layouts (side rail, grid, strip) share one markup structure,
with a dark surface option, per-entry display toggles, and a view script
that swaps the player on click and advances on the player's ended event
when Autoplay next is on.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heydemoura
heydemoura force-pushed the add/videopress-playlist-block branch from 6b8b883 to ac82b90 Compare August 17, 2026 21:00
Heyde Moura and others added 2 commits August 17, 2026 18:36
Cover the paths CI's coverage check flagged: block registration (the
editor entry, the default build metadata path, nameless metadata, and
the Jetpack-without-VideoPress-module gate), the editor's media library
selection mapping and error, layout/playback/display option changes,
drag-and-drop reordering, filtering, the busy add state, Enter-to-add,
preview-index adjustments around reorders and removals, hour-long
duration formatting in the PHP render, and the view script's guards for
empty playlists and entries without an embed URL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The module-gating test aliased stdClass as Jetpack, which throws on
PHP < 8.3 and is rejected by Phan; use a user-defined anonymous class
instead. Also align the side-rail header with the design (count and
total duration as one right-aligned monospace meta line), give the
sidebar notices breathing room above, and stop dropping resolution
metadata: media-library picks now store the attachment height and
numeric strings from the API are coerced, so 240p videos get their
badge.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heydemoura

Copy link
Copy Markdown
Contributor Author

Fixed the CI failures and tightened the block against the design spec:

  • PHP tests (7.2–8.2) + Static analysis: both failed on the same line — the module-gating test aliased stdClass as Jetpack, which class_alias() rejects before PHP 8.3 and Phan flags as PhanParamMustBeUserDefinedClassname. It now aliases a user-defined anonymous class, which works on every supported PHP version.
  • Side-rail header: video count and total duration now render as a single right-aligned monospace meta line ("5 videos · 1 hr 13 min"), matching the side-rail design.
  • Sidebar notices: the error/duplicate notices in the block sidebar now have top margin instead of sitting flush against the controls above.
  • 240p resolution badge: media-library picks now store the attachment's video height, and numeric metadata the API returns as strings is coerced — so low-resolution videos show their resolution like the rest.

🤖 Generated with Claude Code

Heyde Moura and others added 6 commits August 18, 2026 06:58
The video count and total duration now sit to the right of the "Up
next" label in the list header instead of in a block-level header row,
which is removed from both the front-end render and the editor preview
so the canvas keeps matching the page exactly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The count · duration line (side rail), the grid runtime, and the strip
progress counter now use the design's ui-monospace stack at its sizes —
12px for the side-rail meta, 11.5px for the grid and strip counters —
in the muted color, shared through a single font-stack variable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The sidebar's library action sat flush against the help paragraph;
give it its own row with breathing room so the two ways of adding a
video read as distinct actions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The mini wireframes had a fixed height with fluid width, so they
stretched as the sidebar resized; a fixed 66.664 / 39 aspect ratio
keeps their proportions on every screen size.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The placeholder's flex column stretched every child, so the Media
Library button spanned the full width; the button now keeps its natural
width and the icon, title, instructions and form column are centered,
matching the empty-state design.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Block attributes now persist only the video reference and the numeric
metadata behind the server-rendered totals (duration, height). Titles
and posters are read fresh from the video data everywhere: the editor
keeps a per-session metadata cache filled from the videos API, and the
front-end view script hydrates each entry's title, poster and the
now-playing line the same way, so both always reflect the videos'
current metadata. The server renders positional fallbacks and ignores
any stored display fields.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heydemoura

Copy link
Copy Markdown
Contributor Author

Reworked metadata handling so display data is always live, per review feedback:

  • Block attributes now store only the video reference (guid) and the numeric metadata behind the server-rendered totals (durationMs, height) — titles and posters are no longer persisted.
  • Editor: a per-session metadata cache is filled from the videos API (once per GUID) and drives every title/poster surface — canvas preview, sidebar rows, duplicate warnings, reorder/remove labels. Media-library picks seed the cache instantly and are refreshed from the API like everything else.
  • Front end: the server renders positional fallbacks ("Video 1", no poster) and the view script hydrates each entry's title, poster and the now-playing line from the public videos API, so the page always shows the videos' current metadata. Anonymous lookups that fail (e.g. private videos) quietly keep the fallback.
  • The PHP render now ignores any stored display fields outright (covered by a test asserting hostile stored titles/posters never reach the markup).

🤖 Generated with Claude Code

Heyde Moura and others added 9 commits August 18, 2026 09:39
An empty URL field previously let the button fire and answer with an
error; the button is now disabled (but still focusable for assistive
technology) until the field has content.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a Typography panel with the block editor's own font-family control
(fed by the theme's theme.json font presets) for the now-playing title
and the entry titles. Attributes store the preset slug; both the editor
preview and the PHP render expose the choice as a CSS custom property
pointing at the theme's font-family preset variable, so the canvas and
the page render identically. Slugs are validated server-side and
anything else is dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The font pickers rendered in the inspector's default group, which lands
in the Settings tab; WordPress surfaces typography for core blocks in
the Styles tab, so the panel now registers there via the styles
inspector group.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bare typography.fontFamilies settings path resolves to the raw
origins object in the editor (it is not an override-merged path), so
the flat-array guard never passed and the panel rendered nowhere.
Query the custom/theme/default origins separately, as core's own
typography hook does, and hand the flat list to the controls; the test
mock now mirrors the real per-origin contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The font controls ship no outer margin, so the two pickers sat flush
against each other; stacked controls now get the standard 16px
separation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Layout is a style setting, so its panel now registers in the styles
inspector group ahead of Typography instead of the Settings tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The control's className lands on the select button inside the
component, not on an outer wrapper, so the sibling spacing rule never
matched; each picker now sits in its own wrapper div that the rule
reliably targets.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rail no longer grows past the player column: the entry list
scrolls internally, and while more entries hide below the scroll
position a bottom fade (white, or the dark surface color) signals
them — driven by a scroll listener in the view script and its mirror
in the editor preview, so the canvas behaves like the page.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extends the scroll hint to the horizontal axis: the strip layout now
shows an inline-end fade (sticky inside the scroller, RTL-aware) while
entries hide past the scroll position, cleared on reaching the end —
in the editor preview and on the front end alike.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Heyde Moura and others added 7 commits August 18, 2026 15:57
The VideoPress player renders its own title overlay, so the stage no
longer repeats the current video's title and meta line in the side-rail
and grid layouts. The stage row now only carries the grid layout's
playlist runtime line (rendered only when a runtime is shown), and the
view script and editor preview shed the code that kept the duplicate in
sync. With no now-playing title element left anywhere, its font picker
and attribute are removed too.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The preview no longer appends the explanatory footnote under the
block; its styles and test assertion go with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The one-liner said nothing about what powers the block or what can be
customized; the description now covers the VideoPress source, the
layout and surface choices, the per-entry display options, and the
always-in-sync titles and thumbnails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Autoplay next moves out of the Layout panel into a new Playback panel
in the Settings tab, joined by Mute by default (playback starts muted
via the embed URLs) and Loop playlist (the view script restarts from
the first entry when the last one ends).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rendered wrapper forces the autoplay-next flag on whenever looping
is enabled, and the editor shows the Autoplay toggle as checked and
locked while Loop playlist is on, so an ended final video always
restarts the list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Publishing a post or page containing the block records a
jetpack_videopress_playlist_block_published Tracks event with the post
type, layout, video count and playlist count. The first playlist block
in the post is the designated reporter, so multi-playlist posts record
exactly one event per publish.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@heydemoura
heydemoura merged commit 40e0af2 into trunk Aug 18, 2026
89 of 90 checks passed
@heydemoura
heydemoura deleted the add/videopress-playlist-block branch August 18, 2026 20:49
@github-actions github-actions Bot added [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. and removed [Status] Needs Review This PR is ready for review. labels Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement Changes to an existing feature — removing, adding, or changing parts of it [Package] VideoPress [Status] UI Changes Add this to PRs that change the UI so documentation can be updated. [Tests] Includes Tests

1 participant