scalecommerce / videooptimizer-sulu
Integrates ScaleCommerce VideoOptimizer into the Sulu 3.0 admin: pick, upload and manage CDN-delivered videos without a separate VideoOptimizer login.
Package info
github.com/ScaleCommerce/videooptimizer-sulu
Type:sulu-bundle
pkg:composer/scalecommerce/videooptimizer-sulu
Requires
- php: >=8.2
- ext-sodium: *
- doctrine/dbal: ^3.6 || ^4.0
- doctrine/orm: ^2.15 || ^3.0
- sulu/sulu: ^3.0
- symfony/config: ^6.4 || ^7.0
- symfony/console: ^6.4 || ^7.0
- symfony/dependency-injection: ^6.4 || ^7.0
- symfony/filesystem: ^6.4 || ^7.0
- symfony/http-client: ^6.4 || ^7.0
- symfony/http-foundation: ^6.4 || ^7.0
- symfony/http-kernel: ^6.4 || ^7.0
- twig/twig: ^3.0
Requires (Dev)
- phpspec/prophecy-phpunit: ^2.1
- phpstan/phpstan: ^1.12 || ^2.0
- phpunit/phpunit: ^10.5 || ^11.0
- symfony/cache: ^6.4 || ^7.0
README
π¬ VideoOptimizer for Sulu
Pick, upload and manage CDN-delivered videos β right inside the Sulu admin. No separate VideoOptimizer login. No credentials in the browser. Just video.
Product Β· API & docs Β· Report a bug
Give your editorial team adaptive, CDN-streamed video without ever leaving Sulu. This bundle adds a
video_optimizer content field, a selection & upload dialog, library management, and four
ready-to-use content blocks to the Sulu 3.0 admin β while the organization's API token stays on the
server, encrypted at rest. Editors just pick a video and hit publish.
Built and maintained by ScaleCommerce GmbH, the team behind VideoOptimizer. Part of the
scalecommerce/videooptimizer-<platform>plugin family.
Browse videos right inside Sulu β libraries as folder tiles, videos as a thumbnail grid. (Thumbnails and titles blurred/renamed for the demo.)
β¨ Highlights
- π₯
video_optimizerfield type β drop it into any page, snippet or article template. - ποΈ Media-style admin β browse libraries as folder tiles, videos as a thumbnail grid, with title search and a "ready only" filter.
- β¬οΈ Big-file uploads β presigned multipart upload straight from the browser to storage, with live processing status. Or ingest from a remote URL.
- πΌοΈ Full asset control β pick auto-generated thumbnails, upload a custom poster (from disk or the Sulu media library), edit titles and player options, delete videos.
- π§± Four content blocks β
media split,background hero,spotlightand avideo grid, with facade / lightbox / direct presentation modes. - π Token never touches the browser β stored server-side, encrypted with libsodium; all API calls are proxied.
- β‘ Core-Web-Vitals friendly β lazy poster loading,
IntersectionObserver-gated players,above-the-foldpriority hint, and a single lightweight embed per video. - π Global CDN delivery β adaptive-bitrate HLS, edge-cached worldwide, resilient under traffic spikes.
Why deliver video through VideoOptimizer's CDN?
Compared to serving .mp4 files from your own origin:
- Fast, global playback β cached on edge servers near each viewer, so streams start quickly with minimal buffering, worldwide.
- Adaptive bitrate (HLS) β every upload is transcoded into a resolution ladder; the player serves the right quality for the connection and device.
- Scales under load β the CDN absorbs traffic spikes, so campaigns or viral pages never overload your CMS origin, and you avoid origin bandwidth costs on every view.
- Resilient β multiple edge locations mean high availability; one node or origin outage doesn't break playback.
- Effortless for editors β upload once and posters, thumbnails and renditions are generated automatically; embedding is a single lightweight iframe that keeps heavy media off the page's critical path (better Core Web Vitals & SEO).
Library cockpit: manage the encoding ladder as codec/resolution chips, with paid add-ons clearly flagged.
Requirements
| PHP | β₯ 8.2 with ext-sodium |
| Sulu | ^3.0 |
| Symfony | ^6.4 || ^7.0 |
| A VideoOptimizer account | grab an API token at videooptimizer.eu β Account β API Tokens |
π Quick start
1. Install
composer require scalecommerce/videooptimizer-sulu
2. Register the bundle (skip if Symfony Flex did it) in config/bundles.php:
Scale\VideoOptimizerBundle\ScaleVideoOptimizerBundle::class => ['all' => true],
3. Run the installer. The bundle ships a console command that does the steps a plain
composer require cannot β it imports the admin API routes, wires its (pre-compiled) admin JS into
assets/admin, and creates the settings table:
bin/adminconsole scale:videooptimizer:install
It is idempotent (safe to re-run) and only fills in what's missing; add --dry-run to preview.
What it does β or set it up by hand instead
-
Admin API routes β creates
config/routes/scale_videooptimizer_admin.yaml:scale_videooptimizer_api: resource: "@ScaleVideoOptimizerBundle/Resources/config/routing_admin.yaml" prefix: /admin/api
-
Admin JS wiring β adds the dependency to
assets/admin/package.jsonand imports it inassets/admin/app.js(the JS ships pre-compiled, so nowebpack.config.jschange is needed):"videooptimizer-sulu": "file:../../vendor/scalecommerce/videooptimizer-sulu/src/Resources/js"
import 'videooptimizer-sulu';
-
Settings table β creates
vo_settingsfrom theVideoOptimizerSettingsentity. If your team tracks schema through migrations, runbin/adminconsole doctrine:migrations:diffthen:migrateinstead.
4. Build the admin frontend:
cd assets/admin && npm install && npm run build
After updating the bundle, hard-reload the admin (the build hash changes) so the browser doesn't run the stale bundle.
5. Add your token. In the Sulu admin, open Settings β VideoOptimizer and paste your vp_β¦ API
token. It's stored encrypted and never returned to the browser. Done β editors can now pick videos. π
One organization-wide token, stored encrypted server-side β the write-only field never echoes it back.
6. See the blocks in action (optional). Run bin/console assets:install, then create a page with the
"VideoOptimizer showcase" template (shipped by the bundle, no setup) β it already has all four
content blocks wired up and renders them on a self-contained page. See Content blocks.
Optional: a Symfony Flex recipe is included that can register the bundle (step 2) automatically.
Optional: zero-config install with Symfony Flex
A Symfony Flex recipe is included in the repository
under .recipe/. It is not published to
symfony/recipes-contrib β the steps above are the
supported path. If you want a Flex-enabled project to register the bundle in config/bundles.php and
import the admin routes on composer require, you can submit the recipe yourself; see
.recipe/README.md. The scale:videooptimizer:install command still handles the
admin JS wiring and the settings table.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| The VideoOptimizer navigation appears but clicking does nothing / no view opens | The admin JS was not wired into the build | Run bin/adminconsole scale:videooptimizer:install, then cd assets/admin && npm run build, then hard-reload the admin |
| Views open but show "β¦admin API is not reachable (404)" | The proxy routes are not imported | Run bin/adminconsole scale:videooptimizer:install, then bin/adminconsole cache:clear |
| A view says "No VideoOptimizer token is configured yet" | No API token stored | Open Settings β VideoOptimizer and save your vp_β¦ token |
| Settings shows an error but the form is still usable | Expected on a fresh/misconfigured install β the form never blocks so you can always enter the token | Enter the token and save; fix routes if the error mentions 404 |
Upgrading
composer update scalecommerce/videooptimizer-sulu
Then, because the admin is compiled and its translations are cached, refresh both:
cd assets/admin && npm run build # rebuild so new admin views/labels ship bin/adminconsole cache:clear # pick up new translations
Hard-reload the admin afterwards (the build hash changes). Because this package follows semantic
versioning, ^1.0 receives every 1.x feature and fix automatically.
Configuration (optional)
The API and embed base URLs default to VideoOptimizer's production hosts. Override them (e.g. to point
at a staging API) under the scale_video_optimizer key:
# config/packages/scale_video_optimizer.yaml scale_video_optimizer: api_base_url: 'https://api.videooptimizer.eu/api/v1' embed_base_url: 'https://videooptimizer.eu'
Uninstalling
bin/adminconsole scale:videooptimizer:uninstall # add --force to skip the drop-table confirmation
Removes the route import and admin-JS wiring and drops the vo_settings table (which holds the
encrypted token, so it asks for confirmation first; --dry-run previews). Afterwards remove the bundle
from config/bundles.php and run composer remove scalecommerce/videooptimizer-sulu.
Usage
Add the field to a template (config/templates/pages/*.xml):
<property name="video" type="video_optimizer"> <meta> <title lang="en">Video</title> </meta> </property>
Render the CDN player in Twig:
{% if content.video and content.video.uuid %}
{{ video_optimizer_embed(content.video, content.title) }}
{% endif %}
The stored value is { uuid, libraryId, title, posterUrl }; the embed points at
https://videooptimizer.eu/embed/<uuid>.
π§± Content blocks
Beyond the single field, the bundle ships four ready-to-use Sulu content blocks for richer video-driven pages β each delivered as an XML template fragment plus a matching Twig view, so there's nothing to copy-paste.
| Block type | Purpose | Twig view |
|---|---|---|
vo_media_split |
Video beside text, side left/right |
blocks/vo_media_split.html.twig |
vo_background_hero |
Full-bleed native <video> HLS background |
blocks/vo_background_hero.html.twig |
vo_spotlight |
Poster that opens the video in a lightbox | blocks/vo_spotlight.html.twig |
vo_video_grid |
Repeatable grid of videos, each opening a lightbox | blocks/vo_video_grid.html.twig |
Fastest path: the shipped showcase template
The bundle ships a ready-to-use "VideoOptimizer showcase" page template with all four blocks already
wired in and a self-contained view. It is registered automatically β nothing to copy. After
bin/console assets:install, pick it when creating a page, add blocks, publish, and you're done.
Use this to explore the blocks immediately, or as a reference for wiring them into your own templates (below).
Wiring blocks into your own templates
Prefer your own theme/template? Pull the block fragments in via XInclude:
Show the XInclude setup
The template fragments live under src/Resources/config/templates/blocks/ and are pulled into a host
page/snippet template via XInclude β no copy-paste. The root <template> element needs the
xmlns:xi namespace declaration; each block type is then a one-line include:
<template xmlns="http://schemas.sulu.io/template/template" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/template-1.0.xsd"> ... <block name="blocks" default-type="intro" minOccurs="0"> <types> <type name="intro">...</type> <xi:include href="../../../vendor/scalecommerce/videooptimizer-sulu/src/Resources/config/templates/blocks/media_split.xml"/> <xi:include href="../../../vendor/scalecommerce/videooptimizer-sulu/src/Resources/config/templates/blocks/background_hero.xml"/> <xi:include href="../../../vendor/scalecommerce/videooptimizer-sulu/src/Resources/config/templates/blocks/spotlight.xml"/> <xi:include href="../../../vendor/scalecommerce/videooptimizer-sulu/src/Resources/config/templates/blocks/video_grid.xml"/> </types> </block> </template>
The href paths are relative to the host template file β adjust the ../../../vendor/... prefix if your
template lives at a different depth. Symfony's XML config loader resolves the includes at template-parse
time, so the merged schema is validated as a whole.
Registering assets & Twig dispatch
Publish the bundle's CSS/JS like any other bundle asset:
bin/console assets:install
Reference the published files in your base layout, and wrap the blocks in a container with data-vo-base
(vo-blocks.js reads it to locate the bundled hls.light.min.js, loaded once per page):
<link rel="stylesheet" href="{{ asset('bundles/scalevideooptimizer/css/vo-blocks.css') }}"> <script src="{{ asset('bundles/scalevideooptimizer/js/vo-blocks.js') }}" defer></script> <div class="vo-blocks" data-vo-base="{{ asset('bundles/scalevideooptimizer/') }}"> {% for block in content.blocks %} {% include '@ScaleVideoOptimizer/blocks/' ~ block.type ~ '.html.twig' with { block: block } only %} {% endfor %} </div>
Theming
vo-blocks.css scopes all block styling under .vo-blocks via CSS custom properties, so a host theme
can restyle everything without touching the bundle's CSS:
| Property | Purpose |
|---|---|
--vo-accent |
Accent color (links, controls) |
--vo-text |
Primary text color |
--vo-muted |
Secondary/muted text color |
--vo-bg |
Block background color |
--vo-surface |
Card/surface background color |
--vo-radius |
Corner radius for cards/media |
--vo-gap |
Vertical rhythm between sections |
--vo-max-width |
Max content width |
--vo-overlay |
Gradient overlay on media/hero |
--vo-shadow |
Drop shadow for cards/media |
--vo-font |
Font family (defaults to inherit) |
π How it works
The API token is stored once, organization-wide, encrypted with libsodium's secretbox. Editors
never see or handle it. Every call to VideoOptimizer is proxied through the bundle's admin controllers
so the token stays server-side β the browser only ever receives short-lived presigned URLs for the
direct-to-storage part uploads. List endpoints are cursor-paginated and resolved server-side into a flat
array; rate limits (429) are retried once, honoring Retry-After.
Development
composer install vendor/bin/phpunit
Working on the admin UI
The admin UI source lives under src/Resources/js (React 17 + MobX). It is shipped pre-compiled
to src/Resources/js/dist (via Babel, mirroring Sulu's own config) so consumers don't need to widen
their webpack babel config. After editing the source, rebuild the compiled output and commit it:
cd src/Resources/js && npm install && npm run build # regenerates dist/
CI verifies dist/ is in sync with the source, so a stale build fails the pipeline. State mutations in
admin field/view handlers must be wrapped in MobX @action (the Sulu production build enforces actions).
Contributing
Issues and pull requests are welcome at
github.com/ScaleCommerce/videooptimizer-sulu.
Please run vendor/bin/phpunit before opening a PR.
License
Released under the MIT License, Β© ScaleCommerce GmbH.
Ships hls.js (Apache License 2.0) for HLS playback in the content blocks β see THIRD-PARTY-NOTICES.md.


