Repository navigation
feat(media): model image, video, external video, and 3D variants - #690
Conversation
ebfd04b to
cfbf898
Compare
|
@amithanda thanks, great feedback! Addressed, and with benefit of fresh eyes, caught a few additional issues. PTAL. |
|
Thanks for the quick turnaround, the updates address all three of my threads (verified locally: One thing to reconcile before merge: the PR description still reflects the first revision. It describes externally hosted video as On classification: I agree the practical impact is probably near zero, and the security rationale is sound. But since this tightens validation on an existing field, should we either mark it |
The existing Media shape is image-oriented: one URL and one pair of dimensions cannot describe alternate video resolutions and codecs, adaptive streaming manifests, device-specific 3D formats, poster images, or an externally hosted player's embed URL. Model Media as an open type discriminator with image, video, and model_3d variants. Keep `url` as the primary resource, add `sources` as the complete set of negotiable renditions, and represent externally hosted playback as a video with `embed_url` rather than a separate media type. Optional `preview` and `name` fields provide a poster and human-readable label; source metadata covers media type, format, dimensions, and byte size. The shape follows the schema.org MediaObject family while using scalar seconds for duration and bytes for file size. REST and MCP examples demonstrate self-hosted video, 3D models, and embedded video without vendor-specific hosts. This is additive and preserves generated-source compatibility: the base keeps `required: [type, url]` plus `width` and `height`, shared constraints are defined once on the base, and unknown media types remain valid but MUST be ignored by Platforms. Follow-up: teach the Schema Reference macro to merge inherited base constraints when rendering description-only variant refinements.
Fresh eyes review & amendments to staged update:
- `url` was overloaded. Folding external video into `video` made
`video.url` mean "a rendition, or the watch page when `embed_url` is
present", with `embed_url` presence as an implicit discriminator.
Restore `external_video` as its own well-known type whose `url` is the
third-party player; `video.url` is always a playable rendition; drop
`embed_url`. `required: [type, url]` is untouched.
- `width`/`height` had been redefined for video as layout size, with two
SHOULDs patching the resulting confusion. Restore intrinsic pixels,
drop both rules, and mark them not applicable to `model_3d`.
- "MUST ignore an unrecognized type" contradicted how `link`, `policy`,
and `fulfillment_method` treat open vocabularies. Use the house rule:
MUST NOT reject; MAY present from `preview`/`name`, or omit.
Review-driven changes:
- Define every field once on `media.json`. `sources` moves up so images
can carry AVIF/WebP/JPEG and width variants, and `duration` moves up so
generated types are complete: JSON Schema code generators do not
project `if`/`then`, so a field defined only in a per-type file was
invisible to them. The per-type files now hold description overrides
only.
- Require the `https` scheme on `url`, `preview.url`, and
`sources[].url` via `pattern`, paired with a consumer rule: a Platform
MUST NOT fetch, embed, or navigate otherwise. This deliberately
tightens the released `url`; no example or fixture has ever carried
`http://`, and secure-context Platforms already refuse mixed content.
- A Platform embedding an `external_video` player MUST isolate it so it
cannot navigate the top-level document or act as the Platform. The
mechanism is illustrated, not mandated: a sandboxed, credentialless
iframe granting only what the player needs.
- Product/Variant prose says "media item", not "image"; the first item a
Platform presents is featured.
main.py: render per-type variants with fields completed from the base.
A variant property carrying only a `description` refines a field the
base defines, so it has no `type` or `required` of its own; schema_fields
rendered such rows as `any` / `Optional` directly under a base table
saying `string` / `Required`. Find the composing base by scanning
siblings for an `allOf[].then.$ref` to the file being rendered, copy each
description-only property's definition from the base while keeping the
variant's description, and pass the base's `required` through as
parent_required_list. No call-site parameter: the relationship is read
from the schema that declares it. Only the four media variants use
description-only refinement; fulfillment_destination's variants declare
their own properties and render unchanged. The idiom is documented in
schema-authoring.md.
The previous commit added `pattern: ^https://` to `url`, `preview.url`, and `sources[].url`, with a Business MUST to match. On `url` that tightens a released field: an `http://` media URL that validates against 2026-08-25 fails, which makes the whole PR breaking and therefore not backportable. It also makes media the one place UCP scheme-checks a content URL, while `link.url`, `product.url`, `variant.url`, `image_url`, `tracking_url`, `continue_url`, and the other `format: uri` content fields carry no constraint at all. Remove the patterns and the producer MUST so the PR is additive again. In their place, a Platform MAY refuse to load or embed a media URL that is not `https`: refusing is conformant, and a Business knows an `http://` asset may not render. The isolation requirement for `external_video` players is unchanged; a player executing in the Platform's origin is the player acting as the Platform, which that requirement already forbids. The scheme requirement can land later as a breaking change. When it does, it should be a sweep of every content URL UCP hands a Platform for rendering, linking, or embedding â deciding which fields must be `https` and which, if any, legitimately carry another scheme â rather than one field family at a time.
cfbf898 to
ebfd925
Compare
|
@amithanda updated PR description. You're right on the ! as it was specced, but I think we should walk that back: added a ~ |
|
Thanks, Ilya. Thanks for putting this together. I left just one comment for you to consider. |
amithanda
left a comment
There was a problem hiding this comment.
Approving! Please take a look at the open comment from @maximenajim before merging.
State the property in terms of what the player must not receive â credentials, Platform APIs and agent tools, protocol messaging, and navigation of the host application â and add the check that makes the example sound: before embedding, decline the item unless `url` is an absolute `https` URL without userinfo that is not same-site with the Platform. This is the rule Payment Authentication already applies to a URL loaded into a browsing context. The example is unchanged; the inputs that defeat it are excluded before the iframe exists.
âĶversal-Commerce-Protocol#690) Backports Universal-Commerce-Protocol#690 (commit 4a3aa10) to the release/2026-04-08 branch.
The released
Mediashape is image-oriented: one URL and one pair of dimensions cannot describe alternate encodings and sizes of an image, alternate renditions or adaptive-streaming manifests of a video, device-specific 3D formats, a poster still, or a video that exists only inside a third-party player. This PR extends the openMediatype â one schema, discriminated bytypeâ following the schema.orgMediaObjectfamily while preserving the released wire contract.sourceson every media item: alternate renditions of the resource aturlâ image encodings and sizes, video renditions and HLS/DASH manifests, 3D file formats â each withmime_type,format, dimensions, andfilesize.preview, a poster or thumbnail still, andname, a title distinct from accessibility-orientedalt_text.durationin seconds for time-based media.external_videoas a well-knowntypefor a video presented through a third-party player. Itsurlis the player;video.urlis always a playable rendition.media_image,media_video,media_external_video,media_model_3d) selected by openif/thencomposition. Every field is defined once onmedia.json; the per-type files refine descriptions only, so generated types are complete.httpsscheme.external_videoplayer MUST isolate it so that it cannot navigate the top-level document or otherwise act as the Platform; the web mechanism (a sandboxed, credentialless iframe) is illustrated, not mandated.typeopen. A Platform MUST NOT reject a product or response for an unrecognized mediatype; it MAY present the item frompreviewandname, or omit it. Platforms SHOULD treat the first media item they present as featured.Image with alternate encodings
{ "type": "image", "url": "https://cdn.example.com/products/runner-pro-blue.jpg", "alt_text": "Runner Pro in Blue", "width": 1600, "height": 1600, "sources": [ { "url": "https://cdn.example.com/products/runner-pro-blue.avif", "mime_type": "image/avif", "width": 1600, "height": 1600 }, { "url": "https://cdn.example.com/products/runner-pro-blue.jpg", "mime_type": "image/jpeg", "width": 1600, "height": 1600 } ] }Video
urlis a playable rendition and appears insources; a manifest lets the Platform stream adaptively.{ "type": "video", "url": "https://cdn.example.com/video-720.mp4", "name": "Product demonstration", "duration": 24, "width": 1280, "height": 720, "preview": { "url": "https://cdn.example.com/video-poster.jpg", "width": 1280, "height": 720 }, "sources": [ { "url": "https://cdn.example.com/video-720.mp4", "mime_type": "video/mp4", "width": 1280, "height": 720 }, { "url": "https://cdn.example.com/video.m3u8", "mime_type": "application/vnd.apple.mpegurl" } ] }External video
The video exists only inside a third-party player, so
urlis the player and there are no renditions. A Platform that does not embed third-party players presentspreviewor omits the item.{ "type": "external_video", "url": "https://videos.example.com/embed/123", "name": "Runner Pro trail review", "duration": 212, "preview": { "url": "https://cdn.example.com/review-poster.jpg", "width": 1280, "height": 720 } }3D model
{ "type": "model_3d", "url": "https://cdn.example.com/product.glb", "preview": { "url": "https://cdn.example.com/product-poster.jpg" }, "sources": [ { "url": "https://cdn.example.com/product.glb", "mime_type": "model/gltf-binary" }, { "url": "https://cdn.example.com/product.usdz", "mime_type": "model/vnd.usdz+zip" } ] }Backward compatibility
Additive for conforming uses of the released
Mediacontract:typeandurl; no field is removed, renamed, or retyped; all new fields are optional; unknowntypevalues remain valid.urlkeeps its released meaning â the media resource the Platform renders, now stated per type â andwidth/heightkeep their released meaning as intrinsic pixel dimensions.httpsmedia. Requiringhttpson content URLs is a breaking change and is proposed separately as a protocol-wide sweep of every URL a Platform renders, links, or embeds.Also in this PR:
schema_fields(docs macro) renders a description-only per-type refinement with the type and requirement of the base field it refines, instead ofany/Optional; the idiom is documented inschema-authoring.mdunder Per-Value Field Semantics.Checklist