Hooks and Filters Reference
MediaShield overviewFree + ProGet MediaShield →
Hooks Pro adds to its own surfaces use the mediashield_pro_* prefix. A few Pro-fired hooks predate that convention and still use the plain mediashield_* prefix (mediashield_lms_lesson_completed, mediashield_lms_adapters_loaded). They are fired as-is in 1.3.0 - there is no do_action_deprecated() shim in the code today. The two DRM packaging actions that used to be on this list were removed in 1.3.0.
Actions Fired by Pro
Section titled “Actions Fired by Pro”mediashield_pro_loaded
Section titled “mediashield_pro_loaded”Fired when Pro is fully initialized (end of Core\Plugin::__construct(), on plugins_loaded priority 20). No arguments.
add_action( 'mediashield_pro_loaded', function() { // Pro is ready} );mediashield_pro_privacy_before_erase
Section titled “mediashield_pro_privacy_before_erase”Fires before Pro’s GDPR eraser runs. Mutate $counters by reference to include your own removals.
add_action( 'mediashield_pro_privacy_before_erase', function( $email, $user, $page, $counters ) { if ( $user ) { $counters->items_removed += my_addon_delete( $user->ID ); }}, 10, 4 );$user is a WP_User or false for a guest email. $counters is a plain object with items_removed and items_retained int properties.
mediashield_lms_lesson_completed
Section titled “mediashield_lms_lesson_completed”Fires after an LMS lesson is auto-completed from a video milestone. Arguments: ( int $user_id, int $video_id, int $lesson_id, string $lms_slug ).
mediashield_lms_adapters_loaded
Section titled “mediashield_lms_adapters_loaded”Fires after adapters are registered. Read-only: an action cannot mutate the adapter array. Use the mediashield_lms_adapters filter to register an adapter.
Action Scheduler work hooks
Section titled “Action Scheduler work hooks”These are queued jobs rather than extension points, but they are real hooks you can observe or enqueue.
| Hook | Enqueued by | Arguments |
|---|---|---|
mediashield_fire_webhook |
Milestones\AdvancedActions, async, on a webhook milestone action |
( string $url, array $payload ) |
mediashield_generate_pdf |
REST\ExportController, async, on POST /export/pdf/report |
( int $user_id, array $filters, string $report_id ) |
ms_vpn_lookup |
Analytics\VpnDetection, single action at session start |
( string $ip, int $user_id, int $video_id ) |
All three run in the mediashield-pro Action Scheduler group. mediashield_fire_webhook falls back to a synchronous call when Action Scheduler is unavailable; the PDF route returns a 500 export_no_scheduler error instead.
Filters Provided by Pro
Section titled “Filters Provided by Pro”mediashield_pro_privacy_erase_result
Section titled “mediashield_pro_privacy_erase_result”Filter the final GDPR erase result array before it is returned to WordPress.
add_filter( 'mediashield_pro_privacy_erase_result', function( $result, $email, $user, $page ) { $result['messages'][] = 'Synced with CRM.'; return $result;}, 10, 4 );mediashield_pro_privacy_export_result
Section titled “mediashield_pro_privacy_export_result”Same shape for the exporter: ( array $result, string $email, WP_User|false $user, int $page ). $result carries data (WP exporter item array) and done (bool).
mediashield_pro_render_linked_lesson_video
Section titled “mediashield_pro_render_linked_lesson_video”Control whether Pro auto-appends the linked video to an LMS lesson’s content. Return false to suppress when your theme handles rendering.
add_filter( 'mediashield_pro_render_linked_lesson_video', function( $render, $video_id, $post_id ) { if ( 'my-theme' === get_template() ) { return false; } return $render;}, 10, 3 );mediashield_pro_license_valid
Section titled “mediashield_pro_license_valid”Override the license check result. Use in local development to bypass the outbound license check. (define( 'MEDIASHIELD_PRO_LICENSE_BYPASS', true ) in wp-config.php short-circuits the same check earlier.)
add_filter( 'mediashield_pro_license_valid', '__return_true' );mediashield_lms_adapters
Section titled “mediashield_lms_adapters”Register a custom LMS adapter. The filter passes a map of slug => adapter instance; entries must be objects implementing MediaShieldPro\LMS\LMSAdapterInterface. Class-name strings are rejected with a _doing_it_wrong() notice.
add_filter( 'mediashield_lms_adapters', function( $adapters ) { $adapters['my_lms'] = new MyPlugin\LMS\MyLMSAdapter(); return $adapters;} );The filter runs on plugins_loaded priority 30, so register your callback before that.
mediashield_pro_playback_event_retention_days
Section titled “mediashield_pro_playback_event_retention_days”Days of raw ms_playback_events rows to keep before the daily prune. Default 90, floored at 2.
add_filter( 'mediashield_pro_playback_event_retention_days', fn() => 30 );mediashield_vpn_lookup_url
Section titled “mediashield_vpn_lookup_url”The VPN/proxy lookup endpoint template. The bundled default is ip-api.com’s HTTPS endpoint, which requires the owner’s paid key. %1$s receives the URL-encoded IP and %2$s the key; a template using only %s receives the IP. Pointing this at your own resolver also removes the key requirement, because the check for “is detection usable” passes as soon as the endpoint differs from the bundled default.
Bunny playback filters
Section titled “Bunny playback filters”| Filter | Value |
|---|---|
mediashield_pro_bunny_stream_url |
( string $url, string $platform_video_id, int $video_id ) - the derived Bunny playback URL |
mediashield_pro_bunny_direct_play |
bool, default from ms_bunny_direct_play - serve a real <video> instead of Bunny’s iframe |
mediashield_pro_bunny_prefer_mp4 |
bool, default from ms_bunny_prefer_mp4 - MP4 rendition instead of HLS |
mediashield_pro_bunny_token_ttl |
int seconds, default 6 hours - CDN token lifetime |
mediashield_pro_bunny_token_key |
string - the Bunny CDN token key used for signing |
Free Hooks That Pro Consumes
Section titled “Free Hooks That Pro Consumes”| Free hook | Pro subscriber | Priority | Purpose |
|---|---|---|---|
mediashield_can_watch |
RoleAccess | 20 | Role restriction |
mediashield_can_watch |
LMS adapters | 25 | Enrollment gates |
mediashield_settings_response |
ProSettings, DRMSettings, AdvancedConfig | 10 | Inject Pro settings into GET /settings |
mediashield_settings_update |
ProSettings, DRMSettings, AdvancedConfig | 10 | Save Pro settings on PUT /settings |
mediashield_protection_levels |
Core\Plugin | 10 | Adds the experimental drm protection level, only while ms_drm_method is not none |
mediashield_player_type |
Core\Plugin | 10 | Override to 'drm' when _ms_protection_level is drm and ms_drm_method is not none |
mediashield_upload_drivers |
Core\Plugin | 10 | Register Bunny, Vimeo, YouTube, Wistia drivers |
mediashield_watermark_config |
Watermark\AdvancedConfig | 10 | Extend with 7 configurable fields |
mediashield_milestone_reached |
AdvancedActions | 10 | Fire webhook, email, or user-meta tag on milestone |
mediashield_milestone_reached |
LMS adapters | 10 | Auto-complete linked lesson |
mediashield_session_started |
SuspiciousActivity | 10 | Multi-IP detection |
mediashield_session_started |
VpnDetection | 20 | Queue VPN/proxy lookup (only when enabled and keyed) |
mediashield_devtools_detected |
SuspiciousActivity | 10 | Log devtools detection |
the_content |
LMS\LMSManager | 20 | Append the linked video to an LMS lesson or topic |
rest_request_after_callbacks |
Analytics\PlaybackRecorder | 10 | Record a playback event from each free session heartbeat |
GDPR: Pro registers its own exporter and eraser with WordPress core (wp_privacy_personal_data_exporters / wp_privacy_personal_data_erasers). It does not subscribe to free’s mediashield_privacy_before_erase.

