MediaBlaster Player
The MediaBlaster player embeds video on WordPress pages and posts. Video.js is the default playback engine with a safe native HTML5 fallback, multi-source HLS/DASH/MP4 support, caption/chapter/thumbnail tracks, subscription-aware locked states, and JavaScript events for integrations.
All embed methods (shortcode, Gutenberg block, Elementor widget) call the same PHP renderer: Mediablaster_Player::render(). Locked or premium content never exposes playable source URLs in HTML when access is denied.
Using the MediaBlaster Player shortcode
[mediablaster_player]
Legacy shortcode (still supported)
[tv-video-player]
Both shortcodes use the same renderer. New sites should prefer [mediablaster_player].
Example with skin:
[mediablaster_player id="123" skin="streaming"]
Using the MediaBlaster Player Gutenberg block
- In the block editor, click + and search for MediaBlaster Player (category: Media).
- Insert the block and open the sidebar panels: Video Source, Player Display, Playback Options, Tracks, Advanced.
- Choose a Video Post ID (search or numeric ID). The block imports HLS/MP4, poster, and track URLs from that post’s MediaBlaster meta into the sidebar (editable). You can also enter direct URLs manually.
- Publish — the frontend renders via PHP (server-side dynamic block).
Imported HLS/MP4 URLs in the sidebar are for reference only when a Video Post ID is set. The frontend always loads fresh URLs from that video post on each page view (same as the shortcode). This matters for Vimeo and other signed CDN URLs that expire.
Use URL fields instead of post meta (Advanced) applies only when no video post is selected (manual URL mode). The toggle is disabled when a post ID is set.
If URL fields stay empty after selecting a post, reload the editor or re-select the video — playback still works from post meta when only Video Post ID is set.
The editor shows a server-side preview when a source is configured. Subscription and source resolution run on the server, same as the shortcode.
Using the MediaBlaster Player Elementor widget
Requires Elementor (Elementor Pro not required).
- Edit a page with Elementor.
- Search the widget panel for MediaBlaster Player (category MediaBlaster).
- Configure Source mode:
- Current Post — uses the post ID of the page being viewed. On Theme Builder templates (e.g. single
movies), set the sample post under the template Preview Settings (gear → Preview Settings → choose a movie); the editor canvas uses that preview post, not theelementor_librarytemplate post itself. - Select Post — pick a video from the dropdown or enter a Post ID.
- Direct URLs — HLS/DASH/MP4 fields only (optional Post ID for poster/meta fallback).
- Current Post — uses the post ID of the page being viewed. On Theme Builder templates (e.g. single
- Adjust display, playback, and track settings in the remaining sections.
Editor preview: The Elementor canvas shows the same live player as the frontend (PHP render + Video.js), not a text-only placeholder. If no source is configured yet, a dashed hint box explains what to set. Subscription-locked videos show the locked state in the editor too.
Example: drag MediaBlaster Player onto the page, set Current Post on a single movie template, or Select Post with a specific video ID.
Choosing a source
| Method | Best for |
|---|---|
Post ID (shortcode id, block Video Post ID, Elementor Select Post) | Videos with MediaBlaster meta (rovidx_smarttv_URL, format, captions, BIF) |
| Direct URLs | External streams, landing pages without a video CPT post |
| Current post context | [mediablaster_player] with no id on a movie/episode singular template |
URL priority when overrides are set: hls → dash → mp4_1080–360 → url → mp4 → post meta. See Source resolution below.
Enabled video post types depend on MediaBlaster → General Settings (movies, short videos, episodes). The block editor searches these types via AJAX; Elementor lists up to 50 recent published videos per type.
Selecting a player skin
| Skin | Use case |
|---|---|
default | General site embeds |
streaming | TV-style layouts |
creator | Creator-focused pages |
Set via shortcode skin="streaming", block Player Display → Skin, or Elementor Player Display → Skin.
Adding captions
- From post meta: use default behavior (
captions="true"on shortcode, or leave caption URL empty in block/widget). - Override with WebVTT:
caption="https://…/en.vtt"pluscaption_labelandcaption_lang. - Subtitles track:
subtitlesURL withsubtitles_label/subtitles_lang.
Adding chapters
- Boolean (default):
chapters="true"loads chapter behavior from post meta when available. - WebVTT URL:
chapters="https://…/chapters.vtt"with optionalchapters_labelandchapters_lang.
Configure in the block/widget Tracks panel or shortcode attributes.
Adding thumbnail/storyboard tracks
- BIF from meta:
thumbnails="true"(default) loadsrovidx_smarttv_bifwhen present. - WebVTT storyboard:
thumbnails="https://…/thumbnails.vtt".
BIF scrubber UI on the progress bar is not implemented yet; metadata is stored for future use.
Autoplay and muted behavior
Most browsers block autoplay with sound. For reliable autoplay, enable Muted as well as Autoplay.
The Gutenberg block shows a warning in the sidebar when autoplay is on and muted is off. Elementor includes the same guidance in control help text.
Native fallback mode
Force native HTML5 (skip Video.js):
[mediablaster_player player_engine="native"]
Or set Player engine → Native HTML5 in the block or Elementor widget. If Video.js is unavailable, the player falls back automatically.
Basic examples
Current post context:
[mediablaster_player]
Specific video post:
[mediablaster_player id="123"]
Direct MP4 URL:
[mediablaster_player mp4="https://example.com/video.mp4"]
HLS stream:
[mediablaster_player hls="https://example.com/master.m3u8"]
DASH stream:
[mediablaster_player dash="https://example.com/manifest.mpd"]
Multiple MP4 qualities:
[mediablaster_player mp4_1080="https://example.com/video-1080.mp4" mp4_720="https://example.com/video-720.mp4" mp4_480="https://example.com/video-480.mp4"]
Poster override:
[mediablaster_player id="123" poster="https://example.com/poster.jpg"]
Autoplay (requires muted for most browsers):
[mediablaster_player id="123" autoplay="true" muted="true"]
Vertical video:
[mediablaster_player id="123" aspect_ratio="9:16"]
Captions (WebVTT):
[mediablaster_player caption="https://example.com/captions.vtt" caption_label="English" caption_lang="en"]
Subtitles:
[mediablaster_player subtitles="https://example.com/es.vtt" subtitles_label="Spanish" subtitles_lang="es"]
Chapters (WebVTT):
[mediablaster_player chapters="https://example.com/chapters.vtt" chapters_label="Chapters" chapters_lang="en"]
Thumbnail/storyboard VTT:
[mediablaster_player thumbnails="https://example.com/thumbnails.vtt"]
Legacy BIF trickplay URL (stored for future scrubber UI):
[tv-video-player bif="https://example.com/storyboard.bif"]
Player skins:
[mediablaster_player skin="streaming"]
[mediablaster_player skin="creator"]
Native HTML5 fallback (skip Video.js):
[mediablaster_player player_engine="native"]
Player engine
Default engine: videojs.
- Video.js loads only when a player is rendered on the page (not globally).
- If bundled Video.js files are missing, or another plugin already registered Video.js, the player adapts automatically.
- If Video.js is unavailable, playback falls back to native HTML5 without breaking the page.
Bundled assets live at public/vendor/videojs/ inside the plugin. See that folder’s README for upgrade instructions.
Supported attributes
| Attribute | Default | Description |
|---|---|---|
id | current post | Video post ID |
player_engine | videojs | videojs or native |
hls | — | HLS master URL (.m3u8) |
dash | — | DASH manifest URL (.mpd) |
mp4_1080 | — | 1080p MP4 URL |
mp4_720 | — | 720p MP4 URL |
mp4_480 | — | 480p MP4 URL |
mp4_360 | — | 360p MP4 URL |
url | — | Generic video URL override |
mp4 | — | MP4 URL override |
type | auto | Force format: hls, mp4, dash, etc. |
poster | featured image | Poster image URL |
max_width | 1920px | CSS max-width |
aspect_ratio | 16:9 | 16:9, 4:3, 1:1, 9:16, 21:9 |
skin | default | default, streaming, creator |
controls | true | Show player controls |
preload | metadata | none, metadata, auto |
autoplay | false | Autoplay when allowed |
loop | false | Loop playback |
muted | false | Mute audio |
playsinline | true | Mobile inline playback |
start_time | 0 | Seek to seconds on load |
class | — | Extra CSS classes |
bif | — | Legacy BIF URL or VTT storyboard alias |
captions | true | Load caption tracks from post meta |
caption | — | Single captions VTT URL |
caption_label | English | Captions track label |
caption_lang | en | Captions track language |
subtitles | — | Single subtitles VTT URL |
subtitles_label | English | Subtitles track label |
subtitles_lang | en | Subtitles track language |
captions_json | — | JSON array of multiple caption/subtitle tracks |
chapters | true | Boolean flag, or chapters VTT URL |
chapters_label | Chapters | Chapters track label |
chapters_lang | en | Chapters track language |
thumbnails | true | Boolean flag for BIF meta, or thumbnails VTT URL |
thumbnail_label | Thumbnails | Thumbnail metadata track label |
resume | false | Reserved for future resume watching |
autoplay_next | false | Reserved for future playlist autoplay |
locked_behavior | overlay | overlay or message when access denied |
Dual-mode attributes
thumbnails:true/falsecontrols BIF meta loading; a URL value sets a WebVTT metadata track.chapters:true/falsesets the chapters feature flag; a URL value adds a WebVTT chapters track.captions:falsedisables all caption/subtitle output including post meta.
Source resolution
URL priority:
- Shortcode
hls - Shortcode
dash - Shortcode
mp4_1080,mp4_720,mp4_480,mp4_360 - Shortcode
url - Shortcode
mp4 - Post meta
rovidx_smarttv_URL - Fire Creator add-on MP4 meta (when active)
Format detection uses explicit args, post meta rovidx_smarttv_format, or URL extension (.m3u8, .mpd, .mp4).
For HLS (.m3u8), the player follows master-playlist redirects server-side before rendering (common for Vimeo and other CDNs). This ensures variant playlists and segments load from the CDN origin with proper CORS instead of failing with a perpetual spinner.
Captions load from post meta rovidx_smarttv_cc when captions="true".
BIF trickplay loads from post meta rovidx_smarttv_bif when thumbnails="true" (boolean mode).
Subscription access
When subscriptions are enabled, the player checks entitlements before outputting playable sources.
- Show locked — Branded overlay with poster; no video URL in markup
- Hide completely — Empty output (no player rendered)
- Use
locked_behavior="message"for legacy plain-text locked message
See Subscription Access Metabox.
Headless configuration
For apps or custom PHP integrations, use:
$config = Mediablaster_Player::get_config( $post_id, $args );
$html = Mediablaster_Player::render( $post_id, Mediablaster_Player::normalize_args( $args ) );
get_config() returns a sanitized array (sources, tracks, poster, engine, locked state, etc.) without HTML. Premium sources are omitted when access is denied.
normalize_args() maps block/Elementor keys (postId, className) to player args and applies the same sanitization as shortcodes.
Developer hooks
apply_filters( 'mediablaster_player_args', $args, $post_id );
apply_filters( 'mediablaster_player_engine', $engine, $post_id, $args );
apply_filters( 'mediablaster_player_use_videojs', $use_videojs, $post_id, $args );
apply_filters( 'mediablaster_player_source_args', $source_args, $post_id, $args );
apply_filters( 'mediablaster_player_hls_url', $url );
apply_filters( 'mediablaster_player_resolved_hls_url', $resolved, $original_url );
apply_filters( 'mediablaster_player_sources', $sources, $post_id, $args );
apply_filters( 'mediablaster_player_poster', $poster, $post_id, $args );
apply_filters( 'mediablaster_player_caption_tracks', $tracks, $post_id, $args );
apply_filters( 'mediablaster_player_tracks', $tracks, $post_id, $args );
apply_filters( 'mediablaster_player_thumbnail_track', $track, $post_id, $args );
apply_filters( 'mediablaster_player_resolved', $resolved, $post_id, $args );
apply_filters( 'mediablaster_player_config', $config, $post_id, $args );
apply_filters( 'mediablaster_player_markup', $html, $resolved, $args );
apply_filters( 'mediablaster_player_locked_title', $title, $post_id, $args );
apply_filters( 'mediablaster_player_locked_message', $message, $post_id, $args );
apply_filters( 'mediablaster_player_locked_button_url', $url, $post_id, $args );
apply_filters( 'mediablaster_player_locked_button_text', $text, $post_id, $args );
apply_filters( 'rovidx_wpstv_player', $html, $args ); // legacy
apply_filters( 'rovidx_wpstv_video_url', $url, $post_id ); // legacy
Actions:
do_action( 'mediablaster_player_rendered', $resolved, $args );
do_action( 'mediablaster_player_enqueue_assets' );
JavaScript events
The player dispatches custom events on the wrapper element:
mediablaster:player-readymediablaster:playmediablaster:pausemediablaster:endedmediablaster:errormediablaster:timeupdatemediablaster:loadedmetadata
Event detail:
{
postId,
engine, // "videojs" or "native"
wrapper,
video,
player, // Video.js instance or wrapper
currentTime,
duration
}Troubleshooting
Block shows poster but no play button or controls (shortcode works)
Typical signs: HLS or segments may load in the Network tab; no JavaScript errors; Video.js poster visible; no big play icon or control bar.
Cause: Video.js can end up in vjs-controls-disabled when ingesting a <video class="video-js"> element without a native controls attribute, even when data-controls="true" is on the wrapper.
Check in browser DevTools:
const w = document.querySelector('[data-mediablaster-player]');
const vjs = w?.querySelector('.video-js');
console.log({
dataControls: w?.getAttribute('data-controls'),
controlsDisabled: vjs?.classList.contains('vjs-controls-disabled'),
bigPlayDisplay: getComputedStyle(w.querySelector('.vjs-big-play-button')).display
});Expect when healthy (paused): dataControls is "true", controlsDisabled is false, bigPlayDisplay is "block".
Fix in plugin: public/js/mediablaster-player.js must call player.controls(true) after Video.js ready when controls are enabled (applyVideoJsControls). Do not remove this when refactoring init.
Also verify: hard-refresh the page so mediablaster-player.js is not cached; block has a valid Video Post ID; compare with [mediablaster_player id="SAME_ID"] on a test page.
Block does not play but Network shows HLS requests
- Confirm Video Post ID points to a published video with
rovidx_smarttv_URL(or overrides in manual URL mode). - Do not rely on stale HLS URLs saved in block JSON for post-backed videos — playback uses post meta, not expired sidebar URLs.
- See Video.js controls above if the stream loads but the UI is missing.
Player assets missing (no init at all)
- Dynamic block renders after
wp_head; assets enqueue viahas_block(),view_script_handles, andrender_block→Mediablaster_Player_Assets::enqueue(). - Scripts: bundled
video.min.jsthenmediablaster-player.js(dependency order matters). - Init retries until
window.videojsexists and runs again onwindow.loadfor deferred block scripts.
Editor: Controls toggle looks off for new blocks
The block defaults Controls to on. If the sidebar showed Controls off before saving, older posts may have controls: false in block JSON — re-enable Controls under Playback Options and update the post.
Subscription locked
No playable <source> in HTML when access is denied — expected. See Subscription access.
Implementation notes (developers)
| Topic | Rule |
|---|---|
| Single renderer | Block/Elementor must use Mediablaster_Player::render() — no parallel player logic in blocks/player/index.js on the frontend |
| Post-backed URLs | Mediablaster_Player_Integration::args_from_block_attributes() strips playback URL overrides when postId > 0 |
| Block booleans | Only pass controls, autoplay, etc. when set in block attributes; never null placeholders |
parse_bool | Empty string is false; use defaults in sanitize_args() for null/missing bool keys |
| Video.js UI | Always applyVideoJsControls() after init/ready when data-controls="true" |
Cursor skill for agents: wp-smart-tv-player (.cursor/skills/wp-smart-tv-player/SKILL.md).
Not implemented yet
Do not expect these yet:
- BIF scrubber UI on the progress bar
- Resume watching / watch progress storage
- Continue Watching shelf
- Analytics dashboard
- Autoplay next episode logic
- Chromecast / AirPlay / VAST ads
- Full REST player config endpoint (use
get_config()in PHP for now)
Related guides
- Shortcodes
- Podcast Audio Player — separate player for
podcast_episodeaudio (not Video.js) - Video Data metabox
- Closed Captions & Trickplay