Video CDN
@laioutr-app/cms and @laioutr-core/frontend-coreA product page wants a 20-second demo clip. A hero wants a muted loop behind the headline. A campaign page wants a vertical feed of short clips that plays as the shopper scrolls. All three are the same job for the platform: an editor picks a video in Studio, and the storefront plays it fast enough that nobody waits.
The Laioutr CDN handles that job on the same storage, hostname, and cache path it uses for images. This page describes what that path does, where it stops, and which seams exist for video that needs more than it offers.
The delivery path
Uploads go from the browser straight to the project's bucket through a short-lived presigned URL. Nothing is written to the index until the object has landed, so an abandoned upload leaves nothing behind. Delivery runs through the project's own hostname with the edge cache in front, and the origin is only touched on a miss.
There is no transcoding queue and no processing state to poll. A video is playable as soon as it is finalized, because every derived variant is produced on request by the transform layer, not ahead of time.
The upload envelope
Video is accepted only inside a fixed envelope. The limits come from the transform layer, and the CDN rejects an out-of-envelope file at the moment upload targets are requested rather than accepting it and degrading to an untransformable raw file.
| Constraint | Value |
|---|---|
| Container and codecs | MP4 with H.264 video and AAC or MP3 audio |
| File size | 100 MB |
| Duration | 10 minutes |
| Output | Progressive MP4 |
A rejected file comes back as a per-file too_large or unsupported_type outcome, so one bad file in a batch does not sink the rest. The limits are enforced server-side; a client-side hint in the picker is a convenience, not the boundary.
Delivery URLs and caching
A finalized video is served from the project's delivery host at a key minted once for that upload:
https://{projectId}.cdn.laioutr.cloud/{key}.mp4
Two properties of that URL matter when you build on it.
A key is never reused. Each upload mints a fresh key, and the bytes behind a key never change. Replacing a video writes a new key and repoints the reference; it does not overwrite the old object.
Responses are cached for a year, immutably. A successful delivery carries cache-control: public, max-age=31536000, immutable. That is what makes a repeat view free, and it is also why keys are immutable: a browser cache cannot be purged, so a swapped object would keep serving the old bytes to returning visitors for as long as the entry lives. Error responses are excluded from that header.
A branded delivery hostname (cdn.example.com) can replace the default. It changes the host in the URL and nothing else about this section.
Transformations
Derived variants are produced at the edge from the stored MP4 by prefixing the key with a transform path:
https://{projectId}.cdn.laioutr.cloud/cdn-cgi/media/{options}/{key}.mp4
| Mode | Produces | Typical use |
|---|---|---|
mode=video | Re-encoded progressive MP4 at a requested width, height, and fit | Serving a smaller rendition to a small element |
mode=frame | A single JPEG frame at a given time | Poster images and picker thumbnails |
mode=spritesheet | A grid of frames | Scrub previews on a timeline |
Each distinct transform URL is cached like any other object, so a variant is generated once and then served from cache.
Posters are wired up for you. A video picked from the Laioutr CDN carries a poster whose sources use the laioutrCmsPoster Nuxt Image provider, which rebuilds a mode=frame URL at whatever width the layout asks for. The poster therefore gets responsive widths in the same way an image does.
mode=video width ladder is available on the delivery host but is not yet applied automatically by the built-in player, so upload video at the size you intend to serve.Rendering video in the storefront
An asset picked from the CDN is a canonical MediaVideo value, so it renders through the same component as any other video:
<template>
<Media :media="section.clip" playback="background" />
</template>
The parts of that already have their own pages:
Mediadispatches onmedia.type, plays video with a built-in native player, and holds video back from fetching until it is within a screen of the viewport.playback="interactive"gives a native player with controls;playback="background"gives the muted autoplay loop, with autoplay suppressed under reduced motion.MediaSourceVideocarries the source URL, dimensions, optional focal point, and thestreamingdiscriminator described below.- Media and Media Library covers the picker, the connector contract, and staged uploads.
What the CDN meters
Delivered bytes are counted per project, off the request path, and rolled up daily and monthly. Image and video traffic share one line; there is no separate video-minutes meter on this path.
Metering is record-only. Passing the volume included in your plan raises a notification and an upgrade conversation; it does not throttle delivery and it does not break media on a live storefront. Enforcement is reserved for abuse, not for ordinary overage.
Beyond the envelope
Long-form video, adaptive bitrate for weak networks, DRM, and per-video analytics are outside what the progressive path does. The platform does not pretend otherwise, and it does not require a migration to get them: the contract and the render path were built with the seams already open, so an adaptive provider is a per-asset choice rather than a platform switch.
The source already declares its streaming format
MediaSourceVideo.streaming is part of the canonical type, not an extension:
interface MediaSourceVideo {
provider: string;
src: string;
width: number;
height: number;
streaming?: 'progressive' | 'hls' | 'dash';
// ...
}
A source produced by the Laioutr CDN is progressive. A source produced by an adaptive backend points src at an HLS manifest and sets streaming: 'hls'. Both are valid MediaVideo values, both are stored the same way, and both can sit side by side in one project. See Streaming formats for what each value tells a renderer.
The player is a registration, not a fork
The built-in <video> element plays progressive sources. HLS and DASH need a JavaScript player, and <Media> takes one through provideMediaRenderers. A renderer registered for video replaces the built-in for every video in the app, with no change at any call site:
import { provideMediaRenderers } from '#ui-kit/components/Media/MediaRenderersProvider';
import StreamingVideo from '~/components/StreamingVideo.vue';
export default defineNuxtPlugin((nuxtApp) => {
provideMediaRenderers(nuxtApp.vueApp, {
video: StreamingVideo,
});
});
The renderer receives the narrowed media object and the playback props, reads media.sources and media.streaming, and decides whether a source plays natively or needs the player's engine. That is where a Mux, Bunny, Cloudflare Stream, or Vidstack player goes. See Overriding with a custom renderer for the full contract and a worked example.
The library is a connector
An external video service can appear in Studio as its own picker alongside the Laioutr CDN. That is the media-library connector interface: declare capabilities, answer list, and optionally handle uploads. Your list maps the provider's assets onto MediaVideo values with streaming: 'hls' and a poster URL, and every downstream consumer keeps working, because the shape is canonical.
The full contract, including staged uploads and the transient processing state a transcoding backend needs, is in Media and Media Library.
What changes when video leaves the Laioutr CDN
Adaptive providers buy capability at a cost that is worth naming before you commit:
- Delivery hostname. Managed streaming services usually serve from a shared, account-wide playback domain. A branded per-project delivery host is a property of the Laioutr CDN path and does not carry over.
- Isolation. On the Laioutr CDN, one project's assets are in one bucket bound to one hostname. On a shared streaming account, separation depends on tagging and identifier secrecy instead.
- Metering. Streaming services bill stored and delivered minutes, and that traffic never touches the Laioutr CDN, so it does not appear in the delivered-bytes rollup. It is a separate line on a separate bill.
- Upload shape. Presigned single-PUT uploads are what the Laioutr CDN does. Resumable and multipart protocols used by streaming services are a different upload path, and the connector's finalize step waits for a transcode instead of confirming an object.
A useful middle path keeps the file in the project's bucket as the source of truth and lets the streaming provider pull from it, so storage and delivery stay separable.
Not on this path today
Stated plainly, so nothing is inferred from silence. The Laioutr CDN video path does not provide:
- Adaptive bitrate, HLS, or DASH from Laioutr's own delivery, which is what Beyond the envelope is for
- Signed or expiring delivery URLs, hotlink restrictions, or DRM; delivery is public by design, as storefront media is public content
- Watermarking, automatic content tagging, or a hosted player skin
- Transcode webhooks or a processing-status API, which the synchronous transform path has no need for
- Asset deletion from the picker
CDN
The Laioutr CDN stores and delivers a project's images and video from its own bucket and its own delivery hostname, with Cloudflare transformations and edge caching in front of it.
Checkout
Overview of Laioutr’s composable checkout library and how it connects to your ecommerce backend and payment providers.