VideoPress Studio: integration branch (flag-gated dashboard expansion)#50168
VideoPress Studio: integration branch (flag-gated dashboard expansion)#50168vianasw wants to merge 29 commits into
Conversation
…g, initial state) Adds the jetpack_videopress_studio filter (default off) to Admin_UI, strips the gated dashboard routes from the generated wp-build route registry when the flag is off, exposes the gate in the dashboard initial state as features.studio, types it in global.d.ts, and adds an isStudioEnabled() client helper plus PHPUnit coverage for the flag and route stripping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a flag-gated Playlists tab to the dashboard strip (prop-driven via DashboardLayout), a per-video Details/Analytics/Editor sub-nav (VideoNav) plus a shared per-video chrome (VideoLayout), and four stub routes — /playlists, /playlists/$id, /video/$id/analytics, /video/$id/editor — matching the paths Admin_UI::STUDIO_ROUTE_PATHS strips when the flag is off. Each stub renders a NotFound fallback when the flag is disabled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Defer Studio route stripping to the wp-build page init actions (priority 5, ahead of the build/routes.php readers at 10) so the Studio filter is evaluated at the same request stage Initial_State mirrors it to the client, keeping late-registered filters (init, admin_init) consistent between server routes and the client flag. - Make maybe_strip_studio_routes() public for the hook callback and drop the reflection helper from Admin_UI_Studio_Test, which also removes the PHP < 8.1 setAccessible() hazard. - Update the DashboardTabs docblock summary to mention the conditional Playlists tab. - Add Jest coverage for the isStudioEnabled() initial-state edge cases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
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:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
Phan enforces @internal cross-namespace: the test class lived in Automattic\Jetpack while the methods it exercises are @internal to Automattic\Jetpack\VideoPress. Align the test namespace with the package (a convention already used by sibling tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Code Coverage SummaryCoverage changed in 13 files. Only the first 5 are listed here.
65 files are newly checked for coverage. Only the first 5 are listed here.
Full summary · PHP report · JS report Coverage check overridden by
I don't care about code coverage for this PR
|
Register the vps_playlist attachment taxonomy and its term meta when the
jetpack_videopress_studio filter is on (default off):
- Taxonomy is REST-only (public/show_ui false, show_in_rest true) with
rest_base 'videopress-playlists', all capabilities mapped to
upload_files, and _update_generic_term_count so counts work for
unattached inherit-status attachments.
- Term meta: vps_playlist_artwork_id (integer), vps_playlist_type
(enum collection|series|course|season, invalid values coerce to
collection), vps_playlist_order (array of positive ints, deduped).
Membership truth stays in term relationships; the order meta is
presentation-only and written atomically per reorder.
- delete_attachment prunes the deleted ID from every playlist's order
meta before the term relationships vanish.
- No Jetpack Sync handling for the term meta (known limitation).
Tests live in a new sqlite-backed suite (tests/php-sqlite) because the
default WorDBless dbless engine has no term-table backing; the suite
bootstrap heals the WorDBless sqlite install (missing db_version
disables term meta entirely, and default roles are absent). The suite
verifies the core REST media endpoint accepts
{ "videopress-playlists": [term_id] } on POST /wp/v2/media/{id} and
filters GET /wp/v2/media?videopress-playlists={term_id}.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…reorder Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r entry Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Gate playlist REST reads on upload_files via a terms-controller subclass; core allows anonymous view-context reads of any show_in_rest taxonomy. - Invalidate the library cache (and swallow the presentation-only order-prune failure) in useRemoveFromPlaylist so stale playlistIds can't resurrect a removed membership via the add modal. - Re-read each attachment's playlist terms right before the replace-set write in useSetPlaylists to narrow the cross-client last-write-wins race. - Wire Playlists::init() unconditionally; register() re-checks the Studio flag at init time so late-added filters are honored. - Replace the isMutating settle guard in useReorderPlaylist with a module counter; overlapping reorders settling together could both skip the invalidation. - Clear vps_playlist_artwork_id when the artwork attachment is deleted; document the MEDIA_TRASH count limitation. - Report per-playlist results from useDeletePlaylist and surface partial failures in the delete modal. - Use _n() for the add-modal partial notice and the bulk delete confirmation; artwork-specific media-frame labels; stable empty playlists identity; declare @wordpress/url in routes/playlists. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds GET /jetpack/v4/videopress/stats/video/{post_id} as a blog-signed
proxy to the WPCOM v1.1 sites/{blog_id}/stats/video/{post_id} endpoint,
returning the upstream body as a tolerant passthrough for the upcoming
Studio per-video analytics screen. Admin-gated like the sibling
video-plays route, with validated period/num/end_date params.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move KpiCard, ViewsTrendsCard, and DateRangeSelector from components/overview/ to a new components/stats/ directory, and move the KPI / skeleton / chart-frame / chart-tooltip SCSS they consume from routes/overview/style.scss into a component-level stats/style.scss imported by the components themselves, so the upcoming per-video analytics route can reuse them without pulling in the Overview page stylesheet. Compiled selectors and rendered output are unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Trim/cut reducer with clamped invariants (integer ms, sorted merged cuts, 1s minimum output), gesture-coalescing undo history, canonical operations serialization, timeline px/ms + snapping + ruler math, and the playback skip engine — all dependency-free TypeScript with exhaustive jest coverage. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oint Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…r entry Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Zoomable ruler + filmstrip-placeholder strip with playhead scrubbing, pointer-captured trim handles and cut segments dispatching TRANSIENT/COMMIT into the session history, a New cut toolbar with an editable timecode box and log-scale zoom control, and document-level keyboard shortcuts. No new dependencies: absolutely-positioned divs and native Pointer Events throughout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Surface stats query errors instead of silent zeroed KPIs (error card replaces the KPI/chart region on both the Overview and per-video screens, and the channel-comparison table gets an explicit failure state), validate YYYY-MM-DD date args in the stats proxies (core skips format: date), deduplicate the two WPCOM proxy callbacks into shared helpers, drop the unconsumed dailyPlays surface from useVideoPages, correct the REST controller docblock about which endpoint feeds the plays chart, and document why the pages card ignores the date range. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…HP mock
The contract for the wpcom ffmpeg pipeline: GET/POST/DELETE
wpcom/v2/videopress/{guid}/edits plus a storyboard stub, served locally by
a single deletable mock class (lazy job promotion, deterministic failure
path, state in one site option) gated on the Studio flag. Frontend gets
mirrored transport types and react-query hooks: useVideoEdits (2s polling
while processing), useSaveVideoEdits (typed 409 conflict error), and
useRestoreOriginal.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Playlist detail artwork no longer flashes the 'No artwork' placeholder while the members fetch is in flight: videosLoading is threaded into PlaylistDetailArtwork so usePlaylistArtwork falls back to its own order[0] lookup (usually cached from the list) during the load. - The details screen's 'Add to playlist' button now gates on the same idle predicate as the library bulk action, so uploading/processing videos deep-linked into the details route can't be added to playlists. - The thumbnail card's action-button row wraps on narrow viewports instead of overflowing the card when both buttons render. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lows Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…xtraction fallback Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ntry Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g, undo history, and navigation guards Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fixes # N/A
Proposed changes
Long-lived integration branch for the VideoPress Studio project — a feature-flagged expansion of the modernized VideoPress dashboard (playlists + library management, per-video analytics, a trim & cut video editor, and YouTube metadata import). Work lands on this branch in phases; this PR stays draft and exists primarily so CI runs on every push. Everything is gated behind
jetpack_videopress_studio(default off) — with the flag off, no Studio route modules are registered and the dashboard is unchanged.Currently included (Phase 0 — skeleton):
Admin_UI::is_studio_enabled()/jetpack_videopress_studiofilter, default false.features.studioin the dashboard initial state +isStudioEnabled()client helper.video-nav/video-layoutcomponents./playlists,/playlists/$id,/video/$id/analytics,/video/$id/editor(placeholder stages, not-found fallback when the flag is off).Related product discussion/links
Does this pull request change what data or activity we track or use?
No.
Testing instructions
jetpack build packages/videopress --deps.admin.php?page=jetpack-videopress&p=%2Fplaylistsdoes not render a Playlists screen.add_filter( 'jetpack_videopress_studio', '__return_true' );p=/playlists.jetpack test php packages/videopressandpnpm testfromprojects/packages/videopress.