
Installation
composer require mpc/mpc-vidply
Next:
- Database update → Admin tools → Maintenance → Analyse database structure
- Integrate site set → Site management → Sites → Your site → Sets → Add
mpc/mpc-vidply - Clear caches
Prerequisites: TYPO3 Core, Fluid and Extbase only — fluid_styled_content is not required. The extension has its own lib.mpcVidplyContentElement renderer and a standard layout (frame wrapper + tt_content header/subheader).
Optional customisation: If your site package or fluid_styled_content contains custom frame/header partials, you can link VidPly to 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
Website Pack
Activate in config/sites/<site>/config.yaml:
dependencies:
- mpc/mpc-vidply
Or via the backend: Website Management → Websites → Sets → Add VidPly.
Database tables
tx_mpcvidply_media
Entries in the media library of the following types: video, audio, youtube, vimeo, soundcloud
Key columns:
| Column | Type | Description |
|---|---|---|
media_type | String | Type identifier |
media_file | File | File references (video/audio, HLS/DASH, YouTube/Vimeo/SoundCloud via FAL Online Media Helper) |
title | String | Display title |
artist | String | Author’s name |
poster | File | Thumbnail |
captions | File | WebVTT/SubRip subtitle files (SRT is automatically converted to VTT upon saving) |
chapters | File | WebVTT/SubRip chapter files (SRT is automatically converted to VTT when saved) |
audio_description | File | Video file (MP4/WebM) to be used for the source exchange |
audio_description_mode | Select | auto, swapor vtt_speech — see audio description modes below |
audio_description_duration | int | Duration of the captioned version in seconds (playlist user interface) |
sign_language | File | Sign language overlay |
enable_transcript | bool | Show transcript window |
Audio description – Player option mapping
VidPlyProcessor Assigns media fields to VidPly data-vidply-options:
| Media field | Player option |
|---|---|
audio_description (first file) | audioDescriptionSrc |
audio_description_mode | audioDescriptionMode (auto | swap | vtt_speech) |
audio_description_duration | audioDescriptionDuration |
Subtitles with tx_track_kind = descriptions | <track kind="descriptions"> |
fallback hierarchy if audioDescriptionMode is auto:
- Description of the video switch when
audio_descriptionis set - Otherwise, VTT language, if a VTT track with subtitles is available
- Otherwise, the AD button is hidden (no spoken AD)
The AD button is enabled if either a captioned video or a VTT track with descriptions is available.
SRT → WebVTT conversion
WebVTT is required for VidPly playback and transcripts. The extension accepts SubRip uploads (.srt) for subtitles, chapters and alternative audio description tracks (tx_desc_src_file).
| Triggers | Behaviour |
|---|---|
Save tx_mpcvidply_media in the backend | Linked .srt files are embedded directly in .vtt; editors display a flashing message |
| Upgrade Wizard (without shell) | Admin Tools → Upgrade → Upgrade Assistants → VidPly: Convert SRT subtitle 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 relationship between tt_content and tx_mpcvidply_media.
tx_mpcvidply_privacy_settings
Site-wide configuration of the privacy level for external services (YouTube, Vimeo, SoundCloud).
Key columns:
| Column | Type | Description |
|---|---|---|
youtube_headline | varchar(255) | Optional heading |
youtube_intro_text | text | Introductory text before the link |
youtube_outro_text | text | Closing text after the link |
youtube_policy_link | varchar(255) | URL of the privacy policy |
youtube_link_text | varchar(255) | Link text |
youtube_button_label | varchar(255) | ‘aria-label’ button |
vimeo_* | (same fields) | Vimeo settings |
soundcloud_* | (same fields) | SoundCloud settings |
sys_language_uid | int | Language ID (multilingual) |
Extension configuration
Global UI and security standards: Admin → Settings → Extension configuration → mpc_vidply (ext_conf_template.txt).
| Setting | Purpose |
|---|---|
allowedVideoDomains / allowedAudioDomains | Whitelist for external MP4/WebM URLs |
playIcon / playPosition / allowedPlayIconDomains | Custom icon for privacy/playback overlay |
useCssIcons | CSS-based icons in the control bar |
theme / themeSyncEnabled | Synchronisation of dark/light player and page designs |
Full reference: SettingsArchitecture.md → Extension configuration.
Dynamic content and integrations
Vue, Swiper, CSP, mp-core JSON-LD merging, Theme API: integrations.
Fast API after PlaylistInit.js loading:
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:
| Field | Type | Description |
|---|---|---|
tx_mpcvidply_media_items | Group | Selection of media records (MM) |
tx_mpcvidply_options | Check | Bit mask options |
tx_mpcvidply_volume | decimal | Default volume (0–1) |
tx_mpcvidply_playback_speed | decimal | Default speed (0.25–2.0) |
tx_mpcvidply_language | Selection | Force 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:
- Adaptive resizing is always enabled (no toggle button).
- Transcription is controlled on a per-media-record basis (
tx_mpcvidply_media.enable_transcript).
Enable option in ‘Fluid’:
<f:if condition="{data.tx_mpcvidply_options} % 16 >= 8">
<!-- Controls enabled -->
</f:if>
Data processing
VidPlyProcessor
Processes media elements for front-end display.
TypoScript:
tt_content.mpc_vidply =< lib.mpcVidplyContentElement
tt_content.mpc_vidply {
templateName = VidPly
dataProcessing {
10 = Mpc\MpcVidply\DataProcessing\VidPlyProcessor
}
}
Available in the 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 -->
List view & detail view (CE in media library style)
ListviewProcessor—mpc_vidply_listview; createdlistview.rowswith cards, pagination flags, sort settings, etc. SeeDocumentation/Listview.md(Routing, i18n, editor fields).DetailProcessor—mpc_vidply_detail; createddetail.*and player data for the detail page. Template:Detail.html+ListviewCSS, if shared. The associated shelf is only loaded iftt_content.tx_mpcvidply_show_relatedit is enabled (default1); when deactivated,detail.relatedit is empty.
Templates / Sub-templates: Templates/Listview.html, Partials/Listview/*.
Template structure
Main template
Resources/Private/Templates/VidPly.html
Partials
| Partial | Purpose |
|---|---|
VidPly/Assets.html | Conditional registration of assets |
VidPly/VideoSources.html | <source> Video elements |
VidPly/AudioSources.html | <source> Elements for audio |
VidPly/Tracks.html | Subtitles/Chapters <track> Elements |
VidPly/MetadataScripts.html | JSON-LD accessibility metadata |
VidPly/PrivacyLayer.html | GDPR consent overlay |
Customisation
Override templates in your site package:
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 the JavaScript required for the current media types:
| Scenario | Loaded assets | Size |
|---|---|---|
| YouTube/Vimeo/SoundCloud only | PrivacyLayer.js | ~5 KB |
| Local video/audio files | vidply/vidply.esm.min.js (+ chunks) + PlaylistInit.js | ~180 KB |
| HLS streaming | + hls.min.js | +60 KB |
| DASH streaming | + dash.all.min.js | +200 KB |
| Playlist (2+ items) | + PlaylistInit.js | +5 KB |
Up to 97% reduction for external content only.
Files
| File | Purpose |
|---|---|
PrivacyLayer.js | GDPR consent for external services (YouTube / Vimeo / SoundCloud) |
PlaylistInit.js | User interface and navigation for playlists |
hls.min.js | hls.js 1.6.16 for adaptive HLS streaming (Chrome / Firefox / Edge / Desktop Safari) |
dash.all.min.js | dash.js 5.2.0 (modern UMD) for MPEG-DASH streaming |
vidply/*.js | Core player (compiled TypeScript → ESM, code split, includes SoundCloud renderer + buffer spinner + optional download button) |
Initialisation of the player
// 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 });
Data protection level
Implementation
External services (YouTube, Vimeo, SoundCloud) display a consent overlay:
PrivacySettingsServiceRetrieves settings fromtx_mpcvidply_privacy_settingstable- The template renders the poster and the play button (no iframe) using the settings from the database
PrivacyLayer.js/PlaylistInit.jsprocesses the click- If consent is given: Creates an iframe, loads the service’s player
- No tracking until the user explicitly clicks ‘Play’
Privacy settings service
Classes/Service/PrivacySettingsService.php offers:
$privacySettings = $privacySettingsService->getSettingsForService('youtube', $languageId);
// Returns: ['headline', 'intro_text', 'outro_text', 'policy_link', 'link_text', 'button_label']
The settings use translations from the language file if the database fields are empty.
Customise the privacy policy
Recommended: Configure via the backend (List module → Privacy layer settings)
Or override the 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 and 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, together with progressive fallbacks.
How it works
HLS:
- Detects
.m3u8Source in media file references (by MIME type or file extension) - Binds
hls.min.js(only when required) – the VidPly bundle uses it in Chrome, Firefox, Edge and the desktop version of Safari to ensure full feature parity - In Safari on iOS / iPadOS (where MSE is not available), VidPly automatically falls back to native HLS and integrates the
TextTrackAPI into the VidPly menus for subtitles, transcripts and quality – no separate code path is required - Provides a user interface for changing the quality
- Embedded subtitles from the HLS manifest are used by default; local VTT files take precedence
- The HLS renderer also responds to
Hls.Events.SUBTITLE_FRAG_PROCESSED, ensuring that the interactive transcript remains synchronised when new subtitle fragments are loaded for long or live streams
DASH:
- Detects
.mpdthe source in media file references (by MIME type or file extension) - Retrieves
dash.all.min.js(only when required) - Initialises dash.js for video/audio elements
- Provides support for adaptive quality as well as TTML and WebVTT subtitles
- By default, embedded subtitles from the DASH manifest are used; local VTT files take precedence
Source priority: DASH → HLS → progressive (MP4/WebM/MP3/OGG)
CSP configuration
Configuration/ContentSecurityPolicies.php Adds streaming domains and the schemas they use to the whitelist 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 becausehls.js/dash.jsablob:URL is<video>.srcduring playback.data:is required because some HLS variants embed init segments / subtitles inline as data URIs.
VidPly extension
Add a custom media type
- 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' => '...',
];
Update the DataProcessor to handle the new type
Create a partial for the display
Custom player options
Add to tt_content.php:
$GLOBALS['TCA']['tt_content']['columns']['tx_mpcvidply_options']['config']['items'][] = [
'label' => 'My Option',
'value' => 512, // Next power of 2
];
Debugging
Common problems
| Problem | Solution |
|---|---|
| Media is not displayed | Check tx_mpcvidply_media whether the entry has been hidden |
| MM relationship interrupted | Check tx_mpcvidply_content_media_mm Table |
| JS is not loading | Check the browser console; check TypoScript |
| HLS not working | Check the CORS header in the .m3u8 file; ensure that the source file is included in the media record |
| DASH is not working | Check the CORS headers in the .mpd file; ensure that the source file is included in the media record; ensure that dash.js is loaded |
| Privacy Layer is hanging | Clear 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 display detailed errors:
$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] = true;
$GLOBALS['TYPO3_CONF_VARS']['FE']['debug'] = true;
Structured data (JSON-LD)
VidPly generates schema.org structured data for its media, so that pages are eligible for
Google Rich Results for video/audio. The output is included on the page <head>, not in
the template for the ‘content’ element.
What is output
- Detail views and individual media pages: a
VideoObject(orAudioObjectforaudio/soundcloud). - gallery pages with several different media items: one
ItemListof the embeddedVideoObject/AudioObjectnodes. Inlinempc_vidplyplayers (including playlists) andmpc_vidply_listviewshelves are both taken into account and deduplicated based on their media UID in the default language. - Fields from the media record:
name,description,duration(ISO 8601),thumbnailUrl,uploadDate, as well ascontentUrl/embedUrlresolved depending on the media type.
- Detail views and individual media pages: a
contentUrland adaptive streaming: HLS/DASH manifests are not progressive files; therefore,contentUrloutputted for pure HLS/DASH sources (theembedUrlstill points to the playback page). Progressive MP4/WebM/MP3/OGG sources are used, where available.Resolving the playback URL (in order of priority): the one displayed in a list view (
tx_mpcvidply_detail_page), anmpc_vidply_detailelement 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 separate
<script type="application/ld+json">is rendered,page.headerDatarendered using a CSP nonce. - When
mp_coreloaded: The node is loaded into the mp-core@graphandWebPage.mainEntity, so that only a single JSON-LD script is output. If mp-core does not generate a usable@graph, VidPly falls back to a standalone document.
- Standalone: A separate
Disable: Set the website setting
structuredDataEnabledtofalseto suppress all VidPly JSON-LD for this website (enabled by default). Add this toconfig/sites/<site>/config.yamlif your site set does not define this.With mp-core: When mp-core outputs JSON-LD, VidPly includes it in its
@graph. mp-core may also requireseo.schema.enabled— both switches must be set to ‘true’ for the global structured data path to be used by mp-core. See Integrations.md → Structured Data.
Automated tests
The extension includes a PHPUnit suite that runs typo3/testing-framework:
- unit tests (
Tests/Unit/, configurationphpunit.xml.dist) – fast, without a database. - Functional tests (
Tests/Functional/, configurationBuild/FunctionalTests.xml) – test the database/FAL code (repository, services, processors) using a live database.
Set-up (one-off)
Install the extension’s development-specific dependencies in a standalone .Build/ directory tree (starting from the extension’s root directory – the directory containing the 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 require authorisation to create temporary db_* files. Grant this authorisation once to the user “DDEV” db :
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 also requires the typo3Database* environment variables).
Coverage
Enable a coverage driver (ddev xdebug onor 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 information
Target group-based navigation and the complete list of documents: Overview.
Quick guide
Install
composer require mpc/mpc-vidply
# → Database update → Include Site Set → Clear caches
Important files
| What | Where |
|---|---|
| Media TCA | Configuration/TCA/tx_mpcvidply_media.php |
| Content TCA | Configuration/TCA/Overrides/tt_content.php |
| Data processor | Classes/DataProcessing/VidPlyProcessor.php |
| Data protection service | Classes/Service/PrivacySettingsService.php |
| Main template | Resources/Private/Templates/VidPly.html |
| Data Protection JS | Resources/Public/JavaScript/PrivacyLayer.js |
| Data Protection CSS | Resources/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.17 | TYPO3: 13.4+ / 14.x | PHP: ≥8.2