Skip to content

MediaShield Pro owns 7 database tables. All use the {$wpdb->prefix} prefix (typically wp_) and utf8mb4_unicode_ci collation; the engine is whatever dbDelta defaults to on the host (InnoDB on any modern MySQL/MariaDB). Tables are created on activation and dropped on plugin deletion.

An eighth table, ms_email_captures, existed before 1.2.0. The email gate it belonged to was removed and the migrator drops the table on upgrade.

Granular playback event log. Input for heatmap aggregation. Rows are written by Analytics\PlaybackRecorder from free’s session heartbeats (default one every 30 seconds), so this is a coarse sampling of playback, not a per-interaction event stream.

Column Type Notes
id bigint PK
session_id bigint FK to ms_watch_sessions.id (free table). Video and user are resolved through that join - this table has no video_id or user_id column
event_type enum play, pause, seek, buffer, complete, focus_lost, focus_gained. Heartbeat-derived rows only ever use play, pause, focus_lost
position float Seconds from video start
timestamp datetime UTC, defaults to CURRENT_TIMESTAMP
metadata JSON {playing, focused, source} for heartbeat rows

Indexes: idx_session (session_id), idx_position (session_id, position), idx_timestamp (timestamp).

Retention: rows older than 90 days are deleted daily by the ms_playback_event_retention job. The window is filterable via mediashield_pro_playback_event_retention_days and floored at 2 days. Aggregated totals survive in ms_heatmap_cache.

Encrypted API credentials for external platforms.

Column Type Notes
id bigint PK
platform varchar(50) bunny, youtube, vimeo, wistia
api_key text Encrypted (AES-256-CBC, key derived from SECURE_AUTH_SALT)
api_secret text Encrypted the same way. Empty string when the platform needs no secret
extra_config JSON Platform-specific fields: label, channel_id, pull_zone_hostname, cdn_token_key
is_active tinyint(1) Default 1
connected_by bigint User ID that created the connection
connected_at datetime UTC, defaults to CURRENT_TIMESTAMP

Index: idx_platform (platform). There is no uniqueness constraint, so multiple connections per platform are allowed.

Upload job tracking.

Column Type Notes
id bigint PK
video_id bigint CPT post ID, nullable
file_path text Source file
target_platform varchar(50) Driver/target name
status enum pending, uploading, processing, complete, failed
progress tinyint 0-100
error_message text Set on failure
uploaded_by bigint User ID
created_at datetime UTC, defaults to CURRENT_TIMESTAMP
completed_at datetime NULL until the job finishes

Index: idx_status (status).

Suspicious viewing pattern alerts.

Column Type Notes
id bigint PK
user_id bigint 0 for anonymous
video_id bigint 0 for site-wide alerts (devtools alerts always use 0)
alert_type enum multi_ip, devtools, rapid_seek, concurrent_stream, vpn_detected. Nothing in 1.3.0 writes concurrent_stream - the value exists for the UI label and the CLI seeder only
severity varchar(20) Default info. Detectors write warning (multi_ip, devtools, rapid_seek) or info (vpn_detected)
message varchar(500) Human-readable description
details JSON Reserved; the detectors do not populate it
is_dismissed tinyint(1) 0 = active
created_at datetime UTC, defaults to CURRENT_TIMESTAMP

Indexes: idx_user (user_id), idx_severity (severity), idx_created (created_at).

Retention: dismissed alerts older than 90 days are removed daily by the ms_alert_pruning job. Undismissed alerts are kept indefinitely.

DRM license records.

Column Type Notes
id bigint PK
video_id bigint
user_id bigint
license_type enum streaming or persistent. Only streaming can be issued since 1.2.0; persistent remains for legacy rows
license_token varchar(255) UUID plus 16 random bytes
device_id varchar(255) Optional client-supplied identifier, default empty
expires_at datetime UTC
created_at datetime UTC, defaults to CURRENT_TIMESTAMP
revoked_at datetime NULL unless revoked. Any revoked row for a video+user pair blocks future issuance for that pair

Indexes: idx_video_user (video_id, user_id), idx_expires (expires_at).

Pre-aggregated heatmap data per video. Values are cumulative running totals, not a rolling window.

Column Type Notes
id bigint PK
video_id bigint
position_bucket smallint 10-second bucket start position in seconds
view_count int Playback events counted into this bucket
avg_duration float Weighted average session length for the bucket
last_aggregated datetime UTC

Index: UNIQUE uk_video_position (video_id, position_bucket). Updated hourly by the ms_heatmap_aggregation job, which upserts everything recorded since the ms_heatmap_last_aggregated option watermark.

Encrypted content keys for DRM-packaged videos.

Column Type Notes
id bigint PK
video_id bigint UNIQUE - one key per video
key_id varchar(255) Hex key ID
content_key_encrypted text AES-128 content key, encrypted at rest
created_at datetime UTC, defaults to CURRENT_TIMESTAMP

Index: UNIQUE uk_video (video_id). A legacy iv column shipped in DB v1 but was never read; the migrator drops it and fresh installs do not create it.

This table has no writer. KeyServer::generate_key() was its only one, and it was called only from DRM\Packager - both deleted in 1.3.0 because nothing reached them. KeyServer::get_key() still reads the table for WidevineLicense, so it stays, but it will be empty until a packaging path is written.

All Pro jobs run through Action Scheduler in the mediashield-pro group.

Hook Cadence Work
ms_heatmap_aggregation hourly Bucket new playback events into ms_heatmap_cache
ms_playback_event_retention daily Prune ms_playback_events past the retention window
ms_alert_pruning daily Delete dismissed alerts older than 90 days
ms_weekly_digest weekly Send the digest email
ms_vpn_lookup on demand One-off IP lookup queued at session start when VPN detection is on