13 — Media and Image Pipeline
PRD Document · Savoy Signature Hotels — Multi-Site Headless Platform
Version: 1.0 · Date: 2026-03-04
Related docs:06_Content_Modeling_Umbraco.md,09_Cache_and_Performance.md
1. Purpose
Section titled “1. Purpose”This document details the complete lifecycle of media assets (images, videos, documents) from editor upload to end-user delivery. It covers Azure Blob Storage integration, Umbraco Media Library, focal points, and Next.js Image Optimization with Cloudflare Polish.
2. Media Pipeline Architecture
Section titled “2. Media Pipeline Architecture”2.1 The Asset Journey
Section titled “2.1 The Asset Journey”3. Storage and Infrastructure
Section titled “3. Storage and Infrastructure”All media is entirely decoupled from the Umbraco web server’s local file system.
3.1 Azure Blob Storage
Section titled “3.1 Azure Blob Storage”- Provider: Azure Blob Storage (Standard tier, Hot access).
- Integration:
Umbraco.StorageProviders.AzureBlobNuGet package. - Path structure:
https://{account}.blob.core.windows.net/media/{id}/{filename} - Security: Blob container is public for read access (managed via Cloudflare rules). Umbraco handles write access via SAS tokens or Managed Identity.
3.2 Benefits of Blob Storage
Section titled “3.2 Benefits of Blob Storage”- Stateless Web App: The Azure Web App hosting Umbraco can scale out or tear down instantly without losing media.
- Lower Costs: Blob storage is significantly cheaper than premium SSDs required by App Services.
- Origin for Cloudflare: Cloudflare Images will pull original assets directly from Blob Storage, bypassing the Umbraco server entirely.
4. Image Processing and Optimization
Section titled “4. Image Processing and Optimization”We do not use Umbraco.ImageSharp for frontend delivery. Image processing is offloaded to Cloudflare Images to serve optimized, properly sized WebP/AVIF formats at the edge.
4.1 Focal Points and Cropping
Section titled “4.1 Focal Points and Cropping”Umbraco’s Image Cropper property editor is still used, but exclusively for defining the Focal Point, not for hardcoded server-side crops.
| Concept | Implementation |
|---|---|
| Focal Point | Editors click a spot on the image (e.g., a person’s face). |
| API Response | Contains focalPoint: { top: 0.3, left: 0.5 }. |
| Frontend Render | Translated to CSS: object-position: 50% 30%. |
| Why not crops? | Hardcoded crops break responsive design boundaries. <picture> and object-fit: cover with focal points is the modern standard. |
4.2 Next.js Image Component
Section titled “4.2 Next.js Image Component”The ResponsiveImage component uses next/image to generate responsive srcset output.
// Resulting HTML output<picture> <source media="(min-width: 1024px)" srcset="/_next/image?url=https%3A%2F%2Fstorage...&w=1080&q=75 1080w, /_next/image?url=https%3A%2F%2Fstorage...&w=1920&q=75 1920w" /> <img src="/_next/image?url=https%3A%2F%2Fstorage...&w=640&q=75" srcset="/_next/image?url=https%3A%2F%2Fstorage...&w=320&q=75 320w, /_next/image?url=https%3A%2F%2Fstorage...&w=640&q=75 640w" alt="Pool view" loading="lazy" decoding="async" style="object-position: 50% 30%; object-fit: cover; width: 100%; height: auto;" /></picture>4.3 Cloudflare Images Integration
Section titled “4.3 Cloudflare Images Integration”To avoid routing image requests through the Next.js Node.js server (which increases bandwidth costs and latency), we configure a custom loader that points directly to Cloudflare Images (using the URL format). Cloudflare Images will fetch the origin Blob Storage URL, transform it, and cache it.
export default function cloudflareLoader({ src, width, quality }) { // src is the Azure Blob Storage original URL // We reformat it to use the Cloudflare Images URL format // https://developers.cloudflare.com/images/url-format/
const params = [ `width=${width}`, `quality=${quality || 75}`, 'format=auto' ].join(',');
return `https://savoysignature.com/cdn-cgi/image/${params}/${src}`;}
// next.config.tsconst nextConfig = { images: { loader: 'custom', loaderFile: './src/lib/cloudflare-image-loader.ts', },};5. Media Library Management
Section titled “5. Media Library Management”As outlined in 06_Content_Modeling_Umbraco.md, the media library enforces strict organization.
5.1 Pre-Organization Rules
Section titled “5.1 Pre-Organization Rules”- Client handover condition: The media folder structure must be created by WYcreative before the client receives access.
- Top-level folders:
_Shared,Savoy Signature,Savoy Palace,Royal Savoy, etc. - Sub-folders:
Homepage,Rooms,Restaurants,Gallery->Desktop/Mobile.
5.2 Upload Restrictions
Section titled “5.2 Upload Restrictions”| File Type | Max Size | Allowed Extensions | Validation |
|---|---|---|---|
| Images | Desktop: 1MB<br>Mobile: 500KB | .jpg, .jpeg, .png, .webp, .svg | Reject larger files. Auto-convert format via CDN. |
| Videos | 10MB | .mp4, .webm | Used ONLY for short, looping, mute backgrounds. |
| Docs | 5MB | .pdf, .docx | Menus, brochures, policies. |
[!CAUTION] Video Policy: Large videos (e.g., 2-minute promotional videos with audio) MUST be hosted on YouTube or Vimeo and embedded via the
videoBlockmodule. Umbraco/Blob storage is solely for background ambient videos (< 10MB).
6. SVG Management
Section titled “6. SVG Management”SVGs are heavily used for logos, icons, and illustrations.
6.1 Inline vs. Referenced
Section titled “6.1 Inline vs. Referenced”| Asset | Usage Method | Rationale |
|---|---|---|
| Theme UI Icons (arrows, close, menu, social) | Inline SCSS / React Icons | Hardcoded in the Next.js ui package. Extremely fast, cacheable, zero HTTP requests, skinnable via CSS currentColor. |
| Hotel Logos | Media Library -> <img src=".svg"> | Uploaded to Umbraco siteRoot. Resolves via ResponsiveImage. |
| Content Illustrations (e.g., map vectors) | Media Library -> <img src=".svg"> | Managed by editors. |
6.2 SVG Security
Section titled “6.2 SVG Security”To prevent XSS (Cross-Site Scripting), SVGs uploaded to Umbraco MUST be sanitized via an event handler before saving to Blob Storage, stripping all <script> tags and on* attributes.
7. Acceptance Criteria
Section titled “7. Acceptance Criteria”- Azure Blob Storage provider configured in Umbraco.
- Media URLs in Content API correctly resolve to the Blob Storage domain.
-
next/imageconfigured with a custom loader pointing to Cloudflare/Blob (bypassing Next.js image optimization API). - Media Library strictly organized by hotel and section prior to Go-Live.
- File size restrictions enforced on upload (Images < 1MB, Videos < 10MB, Docs < 5MB).
- Focal Point editor enabled on all Image properties; crops disabled.
- Frontend translates Focal Point API data into CSS
object-position. -
<picture>tag renders both Desktop and Mobile sources properly based on viewport. - SVGs sanitized on upload to strip malicious scripts.
- No image processed by Umbraco ImageSharp for frontend delivery to prevent backend CPU drain.
Next document: 14_Accessibility_and_Compliance.md