Developer Overview
MediaShield overviewFree + ProGet MediaShield →
This section is for developers extending or integrating MediaShield Pro. For site-owner documentation, see Getting Started.
Key Facts
Section titled “Key Facts”| Item | Value |
|---|---|
| PHP namespace | MediaShieldPro\ |
| REST namespace | mediashield-pro/v1 |
| Text domain | mediashield-pro |
| Free plugin required | MediaShield (any active version) |
| Version documented | 1.3.0 |
Pro’s only dependency check is defined( 'MEDIASHIELD_VERSION' ) - it does not compare version numbers. Free and Pro are released in lockstep, so run matching versions (1.3.0 with 1.3.0).
How Pro Extends Free
Section titled “How Pro Extends Free”MediaShield Pro extends the free plugin through WordPress hooks, filters, and free’s public static helpers. It never:
- Registers routes under
mediashield/v1(free’s REST namespace). - Adds a top-level admin menu of its own – it injects pages into free’s React admin SPA via the
mediashield_admin_routesJS filter.
It does read some free-owned options directly (ms_enabled, ms_show_badge) and calls free classes directly (MediaShield\Access\AccessControl::can_watch(), MediaShield\Access\SessionManager::validate_token()). Free treats those as public API; do not assume the boundary is hook-only.
Boot Sequence
Section titled “Boot Sequence”Pro bootstraps on plugins_loaded at priority 20, after free’s default-priority bootstrap, guarded by the MEDIASHIELD_VERSION constant:
plugins_loaded (priority 10) free plugin initializesplugins_loaded (priority 20) Pro checks defined( 'MEDIASHIELD_VERSION' ) -> Migrator::run(), Plugin::instance() -> registers REST, hooks, subsystems -> fires mediashield_pro_loadedPro does not hook mediashield_loaded. That action exists in free but has no subscribers; load order is carried by the priority gap plus the constant guard. Hook mediashield_pro_loaded if you need to run after Pro is ready.
Subsystems
Section titled “Subsystems”| Subsystem | Purpose |
|---|---|
| Access | Per-video role restriction (Access\RoleAccess) |
| Analytics | Heatmap aggregation, realtime viewers, suspicious-activity alerts, optional VPN lookup, playback-event recording |
| Platform | Bunny, YouTube, Vimeo, and Wistia connections, browsing, and import |
| Upload | Platform upload drivers and the ms_upload_queue job log |
| DRM | ClearKey key storage and license issuance. No packaging - that code was removed in 1.3.0. See below |
| LMS | Auto-completion and enrollment gating for LearnDash, Tutor LMS, LifterLMS |
| Watermark | Advanced watermark configuration (7 fields) |
| Milestones | Webhook, email, and user-meta actions on milestone events |
| Ads | Per-video in-video ad selection on top of free’s ad-manager bridge |
| Export | Streaming CSV and async PDF reports |
| Licensing | Pro license activation and status (updates only, no feature gating) |
| Reports | Weekly engagement digest email |
| Privacy | GDPR exporter and eraser for Pro-owned tables |
DRM caveat: the packaging code was deleted in 1.3.0 because nothing called it. No content key is generated by the plugin, and POST /drm/license returns drm_key_not_found. No Shaka Player build is bundled or enqueued by either plugin, so assets/js/drm-player.js bails with a console warning unless the site loads Shaka itself. Treat the DRM subsystem as unproven plumbing, not a shipping feature.
Access Control Priority Stack
Section titled “Access Control Priority Stack”MediaShield\Access\AccessControl::can_watch() runs free’s own checks first - admin bypass, login gate, per-video role gate (_ms_access_role), domain restriction - and only then applies the mediashield_can_watch filter. Subscribers on that filter:
| Priority | Subscriber | Decision |
|---|---|---|
| 20 | Pro Access\RoleAccess |
Per-video role restriction (_ms_access_role) |
| 25 | Pro LMS adapters | Enrollment gate, per video via _ms_lms_require_enrollment |
Every subscriber returns early when $result['allowed'] is already false, so a denial from an earlier callback stands. Pick a priority outside 20-25 for custom callbacks.

