scalecommerce/videooptimizer-sulu

Integrates ScaleCommerce VideoOptimizer into the Sulu 3.0 admin: pick, upload and manage CDN-delivered videos without a separate VideoOptimizer login.

Maintainers

Package info

github.com/ScaleCommerce/videooptimizer-sulu

Homepage

Issues

Documentation

Type:sulu-bundle

pkg:composer/scalecommerce/videooptimizer-sulu

Transparency log

Statistics

Installs: 15

Dependents: 0

Suggesters: 0

Stars: 0

v1.3.1 2026-07-17 19:46 UTC

This package is auto-updated.

Last update: 2026-07-17 19:47:16 UTC


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.

CI Latest Version PHP Sulu License: MIT

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.

Videos view in the Sulu admin β€” libraries as folder tiles and videos as a thumbnail grid

Browse videos right inside Sulu β€” libraries as folder tiles, videos as a thumbnail grid. (Thumbnails and titles blurred/renamed for the demo.)

✨ Highlights

  • πŸŽ₯ video_optimizer field 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, spotlight and a video 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-fold priority 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 with the encoding ladder as codec and resolution chips

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.json and imports it in assets/admin/app.js (the JS ships pre-compiled, so no webpack.config.js change is needed):

    "videooptimizer-sulu": "file:../../vendor/scalecommerce/videooptimizer-sulu/src/Resources/js"
    import 'videooptimizer-sulu';
  • Settings table β€” creates vo_settings from the VideoOptimizerSettings entity. If your team tracks schema through migrations, run bin/adminconsole doctrine:migrations:diff then :migrate instead.

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. πŸŽ‰

VideoOptimizer settings page with the write-only, encrypted API token field

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.