Skip to content

Stats: add a single media item route to the stats-app proxy#50412

Draft
dognose24 wants to merge 1 commit into
trunkfrom
add/stats-admin-media-item-route
Draft

Stats: add a single media item route to the stats-app proxy#50412
dognose24 wants to merge 1 commit into
trunkfrom
add/stats-admin-media-item-route

Conversation

@dognose24

Copy link
Copy Markdown
Contributor

Related to Automattic/wp-calypso#112499 (STATS-299 — modernized video details page in Odyssey Stats).

Proposed changes

  • Register GET /jetpack/v4/stats-app/sites/{blog_id}/media/{media_id} on the stats-admin REST controller, gated by the same can_user_view_general_stats_callback permission as the single-post route.
  • Serve the response from the local attachment (like get_single_post does, rather than forwarding to WordPress.com, so it works for users without a WordPress.com account). The response mirrors the fields of /sites/{site}/media/{id} that the Calypso video details page consumes: ID, title, date, URL, mime_type, length (seconds, from core video metadata or the VideoPress duration), and thumbnails (VideoPress poster mapped to fmt_hd, falling back to the featured image as fmt_std).
  • Add a REST controller test covering the 200 response shape and the 404 for unknown media IDs.

Related product discussion/links

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

No.

Testing instructions

  • Set up a Jetpack site with a VideoPress video that has some plays.
  • Build the stats-admin package into the Jetpack plugin and open Odyssey Stats (Jetpack → Stats) in wp-admin.
  • Navigate to Videos → click a video to open the video details page.
  • Without this change, wp-json/jetpack/v4/stats-app/sites/{id}/media/{id} returns 404, the header card is hidden, and Retention rate shows “-”.
  • With this change, the header card shows the video title, upload date, and thumbnail, and the Retention rate card shows a percentage (it needs the video duration from this endpoint).
  • Also verify GET /wp-json/jetpack/v4/stats-app/sites/{id}/media/{id} directly: a video attachment ID returns the fields above; a nonexistent ID returns 404.

🤖 Generated with Claude Code

The Odyssey Stats video details page fetches the video's media item for
its title, upload date, thumbnail, and duration (used to derive the
retention rate), but the stats-app proxy had no media route, so the
request always 404ed in wp-admin. Serve the response from the local
attachment like get_single_post does, including the VideoPress duration
and poster from the attachment metadata.

Co-Authored-By: Claude Fable 5 <[email protected]>
@github-actions

github-actions Bot commented Jul 10, 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 or WordPress.com Site Helper), and enable the add/stats-admin-media-item-route branch.
  • To test on Simple, run the following command on your sandbox:
bin/jetpack-downloader test jetpack add/stats-admin-media-item-route
bin/jetpack-downloader test jetpack-mu-wpcom-plugin add/stats-admin-media-item-route

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!

@github-actions github-actions Bot added the [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. label Jul 10, 2026
@jp-launch-control

Copy link
Copy Markdown

Code Coverage Summary

Coverage changed in 1 file.

File Coverage Δ% Δ Uncovered
projects/packages/stats-admin/src/class-rest-controller.php 516/842 (61.28%) 1.33% 4 💔

Full summary · PHP report · JS report

If appropriate, add one of these labels to override the failing coverage check: Covered by non-unit tests Use to ignore the Code coverage requirement check when E2Es or other non-unit tests cover the code Coverage tests to be added later Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR I don't care about code coverage for this PR Use this label to ignore the check for insufficient code coveage.

@dognose24 dognose24 marked this pull request as ready for review July 10, 2026 15:17
@dognose24 dognose24 marked this pull request as draft July 10, 2026 15:31
dognose24 added a commit to Automattic/wp-calypso that referenced this pull request Jul 10, 2026
The header card and the retention rate metric both required the media
item (title, date, thumbnail, and duration), but the Odyssey stats-app
proxy has no media route, leaving the page degraded in wp-admin until a
Jetpack release ships one. Remove the header card and the retention
card/series so the page works identically everywhere, and keep the
media request only as a best-effort enhancement for the breadcrumb
title (matching the legacy page), falling back to a static label.

Revert this commit to restore both once Automattic/jetpack#50412 (or
the retention series from STATS-312) is available.

Co-Authored-By: Claude Fable 5 <[email protected]>
dognose24 added a commit to Automattic/wp-calypso that referenced this pull request Jul 10, 2026
The retention rate needs the video duration, which only exists on the
media item — unavailable in Odyssey Stats where the stats-app proxy has
no media route. Drop the card and its derived series for now; it can
return once the backend provides a retention series (STATS-312) or the
proxy learns a media route (Automattic/jetpack#50412).

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>
pull Bot pushed a commit to MaxMood96/wp-calypso that referenced this pull request Jul 10, 2026
* Stats: split video details into a dedicated page modeled on post details

Move the videodetails module out of the summary switch into its own
stats-video-detail page component, keeping the existing URL. Adds a
video details header card (title, published date, thumbnail linking to
the media library) and the shared stats breadcrumb trail.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: add period navigation to the video details chart

Replace the old single-metric StatsVideoSummary with a VideoSummary
section modeled on stats-post-summary: a date label with previous/next
arrows and a Days/Weeks/Months/Years segmented control driving the
statsVideo query. Daily data is paged 30 bars at a time like the post
chart. Removes the now-unused stats-video-summary component.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: add metric highlight tabs to the video details chart

Fetch all four statsVideo series (views, impressions, watch time,
retention rate) and render them as selectable cards below the chart,
matching the post details layout from the JA designs. Card totals are
computed over the dates visible in the chart; retention rate uses a
views-weighted average. The chart's statType now lives in page state,
seeded from the statType query param so existing links keep working.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: modernize the video details Embedded pages module

Replace the legacy StatModuleVideoDetails list with an Embedded pages
card matching the rest of the redesigned video details page. Pages
still display as URLs since the stats/video endpoint only returns embed
URLs; showing post titles needs the API to return post IDs/titles.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: zero-fill the video details chart series

The stats/video endpoint only returns buckets from the video's first
activity onwards, so a young video yields one or two bars, which the
is-chart-tabs layout (max-width bars + space-between) then scatters
away from the axis labels. Fill gaps between buckets, extend the day
view backwards to a full 30-bar window per the designs, and add the
has-less-than-three-bars fallback class used by the traffic chart.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: align the video details chart with the real stats/video semantics

A backend investigation confirmed stats/video/:id treats period as a
fixed trailing-window selector (day yields only 2 daily buckets, year
yields ~13 monthly buckets) rather than a bucket granularity, and that
its statType only recognizes watch_time and impressions — views falls
back to the plays column and there is no retention series at all.

Rework the summary accordingly: fetch the 30-day daily window for the
Days/Weeks views and the 365-day monthly window for Months/Years, and
aggregate buckets client-side. Views now covers the same 30-day window
and plays metric as the Traffic Videos module and the All videos page.
Retention rate is derived per bucket and in total with the canonical
complete_stats formula, (watch time / plays) / video duration, using
the media item duration — replacing the bogus fourth query whose
response was silently the plays series. Also harden the statsVideo
normalizer against the endpoint's empty-window response shape, and drop
the now-unneeded zero-fill and paging logic.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: trim the video details daily window to the trailing 30 days

The stats/video endpoint's month window spans 31 days inclusive, one
more than the 30-day window the Videos module and All videos page show,
so the Views total was off by the oldest day's plays.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: keep video chart month labels on one line

The chart's x-axis labels have a fixed 42px width, so 'MMM YYYY' wraps
and collides with the metric cards below. Show 'MMM' like the legacy
year view did — the full month and year remain in the bar tooltip and
the date heading — and key bar selection by startDate since bare month
labels can repeat within a 13-month window.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: bucket video weekly stats Monday-based to match the date heading

The date heading (stats-date-label) renders weeks Monday-Sunday, while
the weekly aggregation grouped by the locale's week start (Sunday in
en), so a selected bar's tooltip range was one day off from the
heading. Group by isoWeek, which matches the heading's convention on
every Monday key regardless of locale. Also center capped chart bars in
their slots (space-around) so bars line up with the mathematically
positioned x-axis labels when there are only a few buckets.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: show full month-year video chart labels like post details

Restore the 'MMM YYYY' month labels — the post details chart uses the
same format and its second wrapped line is absorbed by the spacing
below the chart, so match that spacing above the metric cards instead
of shortening the label.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: stretch the video details header and embeds cards to full width

The Card base carries margin: 0 auto; inside the page's flex column the
auto inline margins shrink-to-fit and center the card instead of
letting align-items: stretch apply, so the header and embeds cards
rendered narrower than the chart section between them.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: track the video details screen in the stats navigation history

recordCurrentScreen silently ignores screens missing from
possibleBackLinks, and videodetails was never registered (a gap dating
back to the summary-page implementation), so the breadcrumb trail
treated the previous screen as current and dropped the Videos crumb —
or showed only the Stats root on direct entry. Register videodetails
like postDetails (tracked, never rendered as a crumb) and record it in
a layout effect so it lands in the history before the breadcrumb-trail
hook's passive effect reads it.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: address review feedback on the video details page

Include watch-time bucket keys in the chart bucket union, treat missing
embeds data as loading so the empty state doesn't flash before the
deferred request starts, and cover the statsVideo normalizer's
empty-window response shapes with unit tests.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: degrade the video details page gracefully in Odyssey Stats

The Odyssey stats-app proxy (Jetpack stats-admin REST controller) has
no media or settings routes, so those requests 404 in wp-admin. Gate
the shared stats shell's QuerySiteSettings the same way as
QuerySiteFeatures (the request 404s on every Odyssey stats page today),
and hide the video details header card in Odyssey while its media item
is unavailable instead of showing a loading state forever. The media
request is still made, so the card and the derived retention rate light
up automatically once the proxy learns the media route.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: drop the video details page's hard dependency on the media API

The header card and the retention rate metric both required the media
item (title, date, thumbnail, and duration), but the Odyssey stats-app
proxy has no media route, leaving the page degraded in wp-admin until a
Jetpack release ships one. Remove the header card and the retention
card/series so the page works identically everywhere, and keep the
media request only as a best-effort enhancement for the breadcrumb
title (matching the legacy page), falling back to a static label.

Revert this commit to restore both once Automattic/jetpack#50412 (or
the retention series from STATS-312) is available.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: render video details media-derived UI only when the data exists

Restore the header card and the Retention rate card, but field-adaptive
instead of hard-required: the header card renders once the media item
loads (never in Odyssey until its proxy has a media route), the date
and thumbnail only when present, and the Retention rate card and series
only when the video duration is available — the metric row divides
evenly whether three or four cards render. The breadcrumb keeps the
media title with a static fallback.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: drop the thumbnail from the video details header card

The VideoPress poster comes in arbitrary aspect ratios and sizes, which
the card layout handled poorly; keep the card to the title and
published date.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: source the video title and upload date from the statsVideo post

The stats/video/:id response includes the video's attachment post, so
the breadcrumb and header card can take the title and published date
from data the page already fetches — and since the Odyssey stats-app
proxy forwards stats routes, both now work in wp-admin too. The media
item is only consulted as a fallback and for the video duration
(retention rate), which remains adaptive.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: polish the video details layout

Pin the period switcher to the right edge when the period header wraps
on narrow widths, add breathing room between the header and the chart,
and stretch the metric cards to the row height so they stay uniform
when a label or value wraps inside one of them.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: remove the video details retention rate card

The retention rate needs the video duration, which only exists on the
media item — unavailable in Odyssey Stats where the stats-app proxy has
no media route. Drop the card and its derived series for now; it can
return once the backend provides a retention series (STATS-312) or the
proxy learns a media route (Automattic/jetpack#50412).

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: avoid flashing the video chart empty state when switching windows

QuerySiteStats defers its initial request, so on the first render after
switching to the Months/Years window the requesting flag is still false
while the data is missing, and the chart briefly rendered its empty
state — resizing the layout — before the fetch started. Treat a
missing selected series as loading, matching the embeds card.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: stop the video chart and metric cards from shifting layout

Nothing hides the chart while StatsModulePlaceholder shows inside
.is-summary-chart (the .is-chart-tabs.is-loading rule targets the outer
wrapper), so during loading the placeholder and the empty chart stacked
to double height and the page jumped when data arrived — pin the
section height and hide the chart while loading. Also lay the metric
cards out with a fixed-column grid: the 50%-minus-gap flex basis could
round past 100% and wrap cards one per row.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: stack video metric cards one per row on small screens

The single-column rule still targeted the old flex layout
(flex-basis has no effect on grid items), so narrow viewports kept two
columns.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: pad the video metric cards off the viewport edges on narrow screens

The page container only adds side padding above the medium breakpoint,
so the cards sat flush against the browser edge below it.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: truncate long breadcrumb labels with an ellipsis

The ellipsis rules on the current-crumb label never applied: each crumb
wraps in an unclassed span that is the actual flex item, so nothing let
the label shrink, and the label also lacked white-space: nowrap. Give
the wrapper a class with min-width: 0 and complete the truncation
rules — long video/post titles now ellipsize instead of overflowing.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: keep the header Jetpack logo from shrinking with long titles

The logo sits in JetpackTitle's HStack as a flex item with the default
flex-shrink: 1, so it compressed alongside a truncating breadcrumb
title; the previous pin targeted a logo inside the breadcrumbs nav,
where none renders.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: show exact zero and exact one hours watched on the video cards

'value > 1' rendered both 0 and exactly 1 as '< 1.0' — zero hours
reading as 'less than one' is misleading.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: zero out the video details heading margin for wp-admin

wp-admin core styles give h4 a 1.33em vertical margin in Odyssey Stats.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: restore the video details card loading placeholder

The card lost its shimmer when the title source moved to the statsVideo
response and started rendering nothing until data arrived, pushing the
chart down when it appeared. Reserve the card with shimmering
title/date lines while neither source has responded, and keep hiding it
only when the response confirms there is no title.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

* Stats: pin the embeds card heading margin for wp-admin

Same as the details card heading: wp-admin core styles give h4 a
1.33em vertical margin in Odyssey Stats.

Part of STATS-299.

Co-Authored-By: Claude Fable 5 <[email protected]>

---------

Co-authored-by: Claude Fable 5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Package] Stats Admin [Status] In Progress [Status] Needs Author Reply We need more details from you. This label will be auto-added until the PR meets all requirements. [Tests] Includes Tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant