Developer Overview
MediaShield Pro proUpdated
Pro feature. Available in MediaShield Pro.
This section is for developers extending or integrating MediaShield Pro. For site-owner documentation, see Getting Started.
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#
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#
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 initializes
plugins_loaded (priority 20)
Pro checks defined( 'MEDIASHIELD_VERSION' )
-> Migrator::run(), Plugin::instance()
-> registers REST, hooks, subsystems
-> fires mediashield_pro_loaded
Pro 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#
| 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#
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.