Jump to content

Developer Quickstart

Quick reference for developers working with VidPly. For detailed docs, see individual files.

VidPly TYPO3 Extension Logo

Installation

 
composer require mpc/mpc-vidply
 

Then:

  1. Database Update → Admin Tools → Maintenance → Analyze Database Structure
  2. Include Site Set → Site Management → Sites → Your Site → Sets → Add mpc/mpc-vidply
  3. Clear Caches

Requirements: TYPO3 Core, Fluid, and Extbase only — fluid_styled_content is not required. The extension ships its own lib.mpcVidplyContentElement renderer and Default layout (frame wrapper + tt_content header/subheader).

Optional theming: If your site package or fluid_styled_content ships custom frame/header partials, you can point VidPly at them:

 
lib.mpcVidplyContentElement.layoutRootPaths.10 = {$styles.templates.layoutRootPath}
lib.mpcVidplyContentElement.partialRootPaths.10 = {$styles.templates.partialRootPath}
 

Directory Structure

 
mpc_vidply/
├── Classes/
│   ├── Backend/
│   │   ├── Controller/           # MediaImportAjaxController (URL import)
│   │   ├── Form/Element/           # MediaUrlImportElement
│   │   └── Preview/              # VidPlyPreviewRenderer
│   ├── Form/FormDataProvider/    # MediaUrlImportFormDataProvider
│   ├── Service/                  # MediaFromUrlService, MediaUrlNormalizer
│   └── DataProcessing/           # VidPly, Listview, Detail processors
├── Configuration/
│   ├── Sets/mpc-vidply/          # Site Set (TYPO3 13.4+)
│   ├── TCA/
│   │   ├── tx_mpcvidply_media.php # Media record TCA
│   │   └── Overrides/
│   │       └── tt_content.php    # Content element TCA
│   ├── TypoScript/
│   │   └── Helper/ContentElement.typoscript  # lib.mpcVidplyContentElement
│   ├── ContentSecurityPolicies.php
│   ├── Icons.php
│   ├── JavaScriptModules.php
│   └── Services.yaml
├── Resources/
│   ├── Private/
│   │   ├── Language/             # XLF translations (en, de)
│   │   ├── Partials/VidPly/      # Modular template partials
│   │   ├── Layouts/              # Default content element layout
│   │   └── Templates/            # Main VidPly.html template
│   └── Public/
│       ├── Css/vidply.min.css
│       ├── Icons/
│       └── JavaScript/
│           ├── hls.min.js        # HLS streaming (loaded only when needed)
│           ├── dash.all.min.js   # DASH streaming (loaded only when needed)
│           ├── PlaylistInit.js   # Playlist logic
│           ├── PrivacyLayer.js   # GDPR consent (YouTube/Vimeo/SoundCloud)
│           └── vidply/           # Core player (compiled from TypeScript, ESM, code-split)
└── Documentation/
    ├── README.md                 # Documentation index (start here)
    ├── AssetLoading.md
    ├── Editors-Guide.md
    ├── HLS-Implementation.md
    ├── Integrations.md           # Vue/Swiper, CSP, mp-core JSON-LD
    ├── Listview.md
    ├── Partials.md
    ├── PrivacyLayer.md
    └── SettingsArchitecture.md
 

Site Set

Enable in config/sites/<site>/config.yaml:

 
dependencies:
  - mpc/mpc-vidply
 

Or via Backend: Site Management → Sites → Sets → Add VidPly.

Database Tables

tx_mpcvidply_media

Media library records with types: video, audio, youtube, vimeo, soundcloud

Key columns:

ColumnTypeDescription
media_typestringType discriminator
media_filefileFile references (video/audio, HLS/DASH, YouTube/Vimeo/SoundCloud via FAL online-media helpers)
titlestringDisplay title
artiststringCreator name
posterfileThumbnail image
captionsfileWebVTT / SubRip caption files (SRT auto-converts to VTT on save)
chaptersfileWebVTT / SubRip chapter files (SRT auto-converts to VTT on save)
audio_descriptionfileDescribed video (MP4/WebM) for source swap
audio_description_modeselectauto, swap, or vtt_speech — see Audio Description modes below
audio_description_durationintDescribed version duration in seconds (playlist UI)
sign_languagefileSign language overlay
enable_transcriptboolShow transcript panel

Audio description — player option mapping

VidPlyProcessor maps media fields to VidPly data-vidply-options:

Media fieldPlayer option
audio_description (first file)audioDescriptionSrc
audio_description_modeaudioDescriptionMode (auto | swap | vtt_speech)
audio_description_durationaudioDescriptionDuration
Captions with tx_track_kind = descriptions<track kind="descriptions">

Fallback hierarchy when audioDescriptionMode is auto:

  1. Described video swap if audio_description is set
  2. Else VTT speech if a descriptions VTT track exists
  3. Else AD button hidden (no spoken AD)

The AD button is enabled when either a described video or a descriptions VTT track is present.

SRT → WebVTT conversion

VidPly playback and transcripts require WebVTT. The extension accepts SubRip (.srt) uploads for captions, chapters, and audio-description alternate tracks (tx_desc_src_file).

TriggerBehavior
Save tx_mpcvidply_media in the backendLinked .srt files are converted in place to .vtt; editors receive a flash message
Upgrade wizard (no shell)Admin Tools → Upgrade → Upgrade Wizards → VidPly: Convert SRT caption files to WebVTT
CLI (optional, DDEV/CI)vendor/bin/typo3 mpc-vidply:convert-srt-to-vtt [--dry-run] [--limit=N]

Implementation: SrtToVttConverter, SrtCaptionMigrationService, SrtCaptionConversionHook, ConvertSrtCaptionsUpgradeWizard.

tx_mpcvidply_content_media_mm

MM relation between tt_content and tx_mpcvidply_media.

tx_mpcvidply_privacy_settings

Site-wide privacy layer configuration for external services (YouTube, Vimeo, SoundCloud).

Key columns:

ColumnTypeDescription
youtube_headlinevarchar(255)Optional headline
youtube_intro_texttextIntro text before link
youtube_outro_texttextOutro text after link
youtube_policy_linkvarchar(255)Privacy policy URL
youtube_link_textvarchar(255)Link text
youtube_button_labelvarchar(255)Button aria-label
vimeo_*(same fields)Vimeo settings
soundcloud_*(same fields)SoundCloud settings
sys_language_uidintLanguage ID (multilingual)

Extension Configuration

Global UI and security defaults: Admin → Settings → Extension Configuration → mpc_vidply (ext_conf_template.txt).

SettingPurpose
allowedVideoDomains / allowedAudioDomainsAllow-list for external MP4/WebM URLs
playIcon / playPosition / allowedPlayIconDomainsCustom privacy/play overlay icon
useCssIconsCSS-based control-bar icons
theme / themeSyncEnabledDark/light player + page theme sync

Full reference: SettingsArchitecture → Extension Configuration.

Dynamic content & integrations

Vue, Swiper, CSP, mp-core JSON-LD merge, theme API: Integrations.

Quick API after PlaylistInit.js loads:

 
window.VidPlyInit.scan(rootElement);
window.VidPlyInit.pauseOutside(activeSlide);
document.dispatchEvent(new CustomEvent('mpc:dynamic-content:ready', { detail: { root } }));
 

Content Element

CType: mpc_vidply

TCA fields:

FieldTypeDescription
tx_mpcvidply_media_itemsgroupMedia record selection (MM)
tx_mpcvidply_layoutselectdefault, episode, episodes
tx_mpcvidply_playlist_positionselectbelow (default) or right — desktop playlist panel placement
tx_mpcvidply_optionscheckBitmask options (autoplay, loop, muted, controls, captions, keyboard, auto-advance)
tx_mpcvidply_resume_playbackcheck (toggle)Resume playback for this content element
tx_mpcvidply_show_track_infocheck (toggle)Show in-player track info for single media
tx_mpcvidply_volumedecimalDefault volume (0-1)
tx_mpcvidply_playback_speeddecimalDefault speed (0.25-2.0)
tx_mpcvidply_languageselectForce UI language

Options Bitmask

 
const AUTOPLAY        = 1;    // Auto-start playback
const LOOP            = 2;    // Loop content
const MUTED           = 4;    // Start muted
const CONTROLS        = 8;    // Show controls
const CAPTIONS_DEFAULT = 16;  // Captions on by default
const KEYBOARD        = 64;   // Enable keyboard shortcuts
const AUTO_ADVANCE    = 256;  // Auto-play next in playlist

// Default: 328 = CONTROLS + KEYBOARD + AUTO_ADVANCE
 

Notes:

  • Responsive sizing is always enabled (no toggle).
  • Transcript is controlled per media record (tx_mpcvidply_media.enable_transcript).

Check option in Fluid:

 
<f:if condition="{data.tx_mpcvidply_options} % 16 >= 8">
    <!-- Controls enabled -->
</f:if>
 

Data Processing

VidPlyProcessor

Processes media items for frontend rendering. The processor orchestrates only; the work lives in Classes/Service/Player/:

ServiceResponsibility
TrackAssemblerMedia records → player tracks, incl. captions, chapters, audio description and sign language
PlayerOptionsBuilderOption bitmask, track-dependent overrides, playlist payload, play icon and theme
EpisodeListBuilderServer-rendered episode metadata, sorting and pagination settings
DownloadResolverDownload target, format label and file size of a track or episode row
MediaFileRegistryThe sys_file_reference lookups of one assembly, prefetched in a single query
InlineSvgProvider / UrlSanitizer / LocaleFormatterSanitized inline SVG, URL guards, localized dates and file sizes

TypoScript:

 
tt_content.mpc_vidply =< lib.mpcVidplyContentElement
tt_content.mpc_vidply {
    templateName = VidPly
    dataProcessing {
        10 = Mpc\MpcVidply\DataProcessing\VidPlyProcessor
    }
}
 

Available in template:

 
{mediaItems}           <!-- Array of processed media -->
{isPlaylist}           <!-- true if 2+ items -->
{hasLocalMedia}        <!-- Has video/audio (including HLS/DASH sources) -->
{hasExternalService}   <!-- Has YouTube/Vimeo/SoundCloud -->
{playerOptions}        <!-- Decoded options array -->
{privacySettings}      <!-- Privacy layer settings per service -->
 

Listview & detail (Mediathek-style CEs)

  • ListviewProcessor — mpc_vidply_listview; builds listview.rows with cards, pagination flags, sort defaults, etc. See Documentation/Listview (routing, i18n, editor fields).
  • DetailProcessor — mpc_vidply_detail; builds detail.* and player data for the detail page. Template: Detail.html + Listview CSS when shared. The related shelf is loaded only if tt_content.tx_mpcvidply_show_related is enabled (default 1); when off, detail.related is empty.

Templates / partials: Templates/Listview.html, Partials/Listview/*.

Template Structure

Main Template

Resources/Private/Templates/VidPly.html

Partials

PartialPurpose
VidPly/Assets.htmlConditional asset registration
VidPly/MediaPlayer.htmlThe <video> / <audio> element (mediaKind)
VidPly/VideoSources.html<source> elements for video
VidPly/AudioSources.html<source> elements for audio
VidPly/Tracks.htmlCaption/chapter <track> elements
VidPly/MetadataScripts.htmlJSON-LD accessibility metadata
VidPly/PrivacyLayer.htmlGDPR consent overlay

Customization

Override templates in your sitepackage:

 
tt_content.mpc_vidply {
    templateRootPaths.20 = EXT:my_sitepackage/Resources/Private/Templates/
    partialRootPaths.20 = EXT:my_sitepackage/Resources/Private/Partials/
}
 

JavaScript Architecture

Conditional Loading

VidPly only loads JavaScript needed for current media types:

ScenarioAssets LoadedSize
YouTube/Vimeo/SoundCloud onlyPrivacyLayer.js~5KB
Local video/audiovidply/vidply.esm.min.js (+ chunks) + PlaylistInit.js~180KB
HLS streaming+ hls.min.js+74KB
DASH streaming+ dash.all.min.js+200KB
Playlist (2+ items)+ PlaylistInit.js+5KB

Up to 97% reduction for external-only content.

Files

FilePurpose
PrivacyLayer.jsGDPR consent for external services (YouTube / Vimeo / SoundCloud)
PlaylistInit.jsPlaylist UI and navigation
hls.min.jshls.js 1.7.3 for adaptive HLS streaming (Chrome / Firefox / Edge / desktop Safari)
dash.all.min.jsdash.js 5.2.1 (modern UMD) for MPEG-DASH streaming
vidply/*.jsCore player v1.2.9 (compiled TypeScript → ESM, code-split, includes SoundCloud renderer + buffering spinner + optional download button)

Player Initialization

 
// This is roughly what `PlaylistInit.js` does internally:
import { Player, PlaylistManager } from './vidply/vidply.esm.min.js';

const player = new Player('#my-player', {
    controls: true,
    keyboard: true,
    responsive: true,
});

// Optional: attach a playlist
const playlist = new PlaylistManager(player, { autoAdvance: true });
 

Privacy Layer

Implementation

External services (YouTube, Vimeo, SoundCloud) show consent overlay:

  1. PrivacySettingsService fetches settings from tx_mpcvidply_privacy_settings table
  2. Template renders poster + play button (no iframe) with database settings
  3. PrivacyLayer.js / PlaylistInit.js handles click
  4. On consent: Creates iframe, loads service player
  5. No tracking until user explicitly clicks play

Privacy Settings Service

Classes/Service/PrivacySettingsService.php provides:

 
$privacySettings = $privacySettingsService->getSettingsForService('youtube', $languageId);
// Returns: ['headline', 'intro_text', 'outro_text', 'policy_link', 'link_text', 'button_label']
 

Settings fall back to language file translations if database fields are empty.

Customize Privacy Notice

Recommended: Configure via backend (List Module → Privacy Layer Settings)

Or override template: Partials/VidPly/PrivacyLayer.html

 
<div class="vidply-privacy-layer" 
     data-service="{service}" 
     data-embed-url="{embedUrl}">
    <f:if condition="{privacySettings.headline}">
        <h3>{privacySettings.headline}</h3>
    </f:if>
    <img src="{poster}" alt="{title}" />
    <button class="vidply-privacy-play">
        {privacySettings.button_label}
    </button>
    <p class="vidply-privacy-notice">
        {privacySettings.intro_text}
        <a href="{privacySettings.policy_link}">{privacySettings.link_text}</a>
        {privacySettings.outro_text}
    </p>
</div>
 

HLS & DASH Streaming

HLS and DASH are not separate media types. Streaming sources (.m3u8 / .mpd) are added as files within the Video or Audio media type alongside progressive fallbacks.

How It Works

HLS:

  1. Detects .m3u8 source in media file references (by MIME type or extension)
  2. Includes hls.min.js (only when needed) — the VidPly bundle uses it on Chrome / Firefox / Edge / desktop Safari for full feature parity
  3. On iOS / iPadOS Safari (where MSE is unavailable), VidPly automatically falls back to native HLS and bridges the browser's TextTrack API into the VidPly captions, transcript and quality menus — no separate code path required
  4. Provides quality switching UI
  5. Embedded captions from HLS manifest used by default; local VTT files override
  6. The HLS renderer also reacts to Hls.Events.SUBTITLE_FRAG_PROCESSED, so the interactive transcript stays in sync as new subtitle fragments are loaded for long / live streams

DASH:

  1. Detects .mpd source in media file references (by MIME type or extension)
  2. Includes dash.all.min.js (only when needed)
  3. Initializes dash.js on video/audio element
  4. Provides adaptive quality, TTML and WebVTT subtitle support
  5. Embedded subtitles from DASH manifest used by default; local VTT files override

Source priority: DASH → HLS → progressive (MP4/WebM/MP3/OGG)

CSP Configuration

Configuration/ContentSecurityPolicies.php whitelists streaming domains and the schemes used by hls.js / dash.js.

Add custom domains:

 
return [
    'default-src' => ["'self'"],
    'media-src' => ["'self'", 'blob:', 'data:', 'https://your-cdn.com'],
    'connect-src' => ["'self'", 'https://your-cdn.com'],
];
 

blob: is required because hls.js / dash.js set a blob: URL on <video>.src while playing. data: is required because some HLS variants embed init segments / subtitles inline as data URIs.

 

Extending VidPly

Add Custom Media Type

  1. Extend TCA (Configuration/TCA/Overrides/tx_mpcvidply_media.php):
$GLOBALS['TCA']['tx_mpcvidply_media']['columns']['media_type']['config']['items'][] = [
    'label' => 'My Service',
    'value' => 'myservice',
];

$GLOBALS['TCA']['tx_mpcvidply_media']['types']['myservice'] = [
    'showitem' => '...',
];
  1. Update DataProcessor to handle new type

  2. Create Partial for rendering

Custom Player Options

Prefer dedicated checkboxToggle fields on tt_content for new on/off player settings. TYPO3 validates type=check bitmasks with (2 ** count(items)) - 1, so adding more bitmask bits can silently fail on save once the field is full.

If a bitmask item is still appropriate, keep the total item count within the limit:

 
$GLOBALS['TCA']['tt_content']['columns']['tx_mpcvidply_options']['config']['items'][] = [
    'label' => 'My Option',
    'value' => 32,  // Must fit `(2 ** count(items)) - 1` (348 for seven items)
];
 

Debugging

Common Issues

IssueSolution
Media not showingCheck tx_mpcvidply_media record not hidden
MM relation brokenCheck tx_mpcvidply_content_media_mm table
JS not loadingCheck browser console; verify TypoScript
HLS not workingCheck CORS headers on .m3u8; verify source file is in media record
DASH not workingCheck CORS headers on .mpd; verify source file is in media record; verify dash.js loads
Privacy layer stuckClear caches; check PrivacyLayer.js

Useful Queries

 
-- Check media records
SELECT uid, title, media_type, hidden FROM tx_mpcvidply_media;

-- Check MM relations
SELECT * FROM tx_mpcvidply_content_media_mm WHERE uid_local = [tt_content.uid];

-- Check file references
SELECT * FROM sys_file_reference WHERE tablenames = 'tx_mpcvidply_media';
 

Debug Mode

Enable TYPO3 debug mode to see detailed errors:

 
$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] = true;
$GLOBALS['TYPO3_CONF_VARS']['FE']['debug'] = true;
 

Structured data (JSON-LD)

VidPly emits schema.org structured data for its media so pages are eligible for Google video/audio rich results. The output is built in the page <head>, not in the content element template.

  • What is emitted

    • Detail views and single-media pages: one VideoObject (or AudioObject for audio/soundcloud).
    • Gallery pages with several distinct media: an ItemList of embedded VideoObject/AudioObject nodes. Inline mpc_vidply players (including playlists) and mpc_vidply_listview shelves are both included and deduplicated by their default-language media UID.
    • Fields map from the media record: name, description, duration (ISO 8601), thumbnailUrl, uploadDate, plus contentUrl/embedUrl resolved per media type.
  • contentUrl and adaptive streaming: HLS/DASH manifests are not progressive files, so no contentUrl is emitted for HLS/DASH-only sources (the embedUrl still points at the watch page). Progressive MP4/WebM/MP3/OGG sources are used when available.

  • Watch URL resolution (in priority order): the detail page configured on a listview (tx_mpcvidply_detail_page), a mpc_vidply_detail element on the same page, then the site-wide first detail page; otherwise a #media-<uid> fragment on the current page.

  • With and without mp-core

    • Standalone: a dedicated <script type="application/ld+json"> is rendered from page.headerData using a CSP nonce.
    • With mp_core loaded: the node is merged into mp-core's @graph and linked via WebPage.mainEntity, so only a single JSON-LD script is output. If mp-core produces no usable @graph, VidPly falls back to a standalone document.
  • Disabling: set the site setting structuredDataEnabled to false to suppress all VidPly JSON-LD for that site (defaults to enabled). Add to config/sites/<site>/config.yaml if your Site Set does not define it.

  • With mp-core: when mp-core emits JSON-LD, VidPly merges into its @graph. mp-core may also require seo.schema.enabled — both toggles must be true for mp-core's global structured-data path. See Integrations → Structured data.

Automated Tests

The extension ships a PHPUnit suite built on typo3/testing-framework:

  • Unit tests (Tests/Unit/, config phpunit.xml.dist) — fast, no database.
  • Functional tests (Tests/Functional/, config Build/FunctionalTests.xml) — exercise the database/FAL code (repository, services, processors) against a real database.

Setup (once)

Install the extension's own dev dependencies into a self-contained .Build/ tree (from the extension root — the directory containing this package's composer.json):

 
composer install --working-dir=vendor/mpc/mpc-vidply
# or, in a path-repo checkout:
composer install --working-dir=mpcore/packages/mpc-vidply
 

Functional tests need permission to create throwaway db_* databases. Grant it once to the DDEV db user:

 
ddev exec 'mysql -uroot -proot -e "GRANT ALL ON \`db_%\`.* TO \`db\`@\`%\`; FLUSH PRIVILEGES;"'
 

Run

 
# Unit tests (replace EXT_ROOT with your checkout path)
ddev exec 'cd mpcore/packages/mpc-vidply && .Build/bin/phpunit -c phpunit.xml.dist'

# Functional tests (point typo3Database* at the DDEV `db` service)
ddev exec 'cd mpcore/packages/mpc-vidply && \
  typo3DatabaseDriver=mysqli typo3DatabaseHost=db typo3DatabaseName=db \
  typo3DatabaseUsername=db typo3DatabasePassword=db \
  .Build/bin/phpunit -c Build/FunctionalTests.xml'
 

Composer shortcuts are also defined: composer test:unit and composer test:functional (the functional one still needs the typo3Database* env vars in front of it).

Coverage

Enable a coverage driver (ddev xdebug on, or install pcov) and add a coverage flag:

 
ddev exec 'cd mpcore/packages/mpc-vidply && XDEBUG_MODE=coverage .Build/bin/phpunit -c phpunit.xml.dist --coverage-text --coverage-html .Build/coverage'
 

Further reading

Audience-based navigation and the full document list: Documentation/README.

Quick Reference

Install

 
composer require mpc/mpc-vidply
# → Database update → Include Site Set → Clear caches
 

Key Files

WhatWhere
Media TCAConfiguration/TCA/tx_mpcvidply_media.php
Content TCAConfiguration/TCA/Overrides/tt_content.php
DataProcessorClasses/DataProcessing/VidPlyProcessor.php
Player servicesClasses/Service/Player/
Privacy ServiceClasses/Service/PrivacySettingsService.php
Main TemplateResources/Private/Templates/VidPly.html
Privacy JSResources/Public/JavaScript/PrivacyLayer.js
Privacy CSSResources/Public/Css/privacy-layer.css

Database

 
-- Tables
tx_mpcvidply_media          -- Media library
tx_mpcvidply_content_media_mm -- Content-to-media relation
tx_mpcvidply_privacy_settings -- Privacy layer configuration
 

Version: 1.2.27 | TYPO3: 13.4+ / 14.x | PHP: ≥8.2 | Bundled player: VidPly v1.2.9

Share page