If you build online courses, member lessons, or protected training libraries, a normal video embed is usually not enough. You do not just want to display a player. You want to know whether the learner actually watched enough of the lesson, whether the next step should unlock, and whether a quiz or download should stay hidden until the video is meaningfully consumed. That is exactly what Video Tracking in BricksMembers is for.
The Video Tracking module turns lesson video and audio into tracked learning steps. You can require a watch percentage, control completion behavior, prompt learners to resume where they stopped, reduce skipping ahead, connect supported video hosts, and render the whole experience with the native BRM Video & Audio element in Bricks Builder.
Why Video Tracking Matters for Online Courses
One of the biggest problems in online learning is that a lesson page can look complete even when the actual learning step was skipped. A learner can open a lesson, scroll a bit, and move on. If your course depends on videos, that creates a weak learning flow and makes progress tracking less meaningful.
BricksMembers Video Tracking solves that problem by connecting the video itself to the rest of your course experience. Instead of treating the video as passive content, you can make it part of your completion logic, your lesson progression, and your frontend design in Bricks.
What You Can Do With the Video Tracking Module
- Require a watch threshold so learners need to watch a percentage of the video before the lesson is treated as ready
- Control completion behavior by keeping completion manual, unlocking completion after the threshold, or auto-completing the lesson at the threshold
- Gate BRM next navigation so BricksMembers does not return the next lesson until enough of the video has been watched
- Unlock quizzes and assessments or downloads based on actual watch progress instead of only page access
- Improve the playback experience with resume prompts, auto-resume, and optional skipping restrictions
- Connect hosted video platforms and browse videos from the picker instead of pasting everything manually
- Design tracked lesson video and audio in Bricks using the native BRM Video & Audio element
How Video Tracking Works in BricksMembers
The workflow is simple:
- Enable the Video Tracking module
- Choose your site-wide watch rules in BricksMembers → Video Tracking
- Connect your video hosts if you want to use the built-in picker
- Add a video source to the lesson or content post
- Render the lesson with the BRM Video & Audio element in Bricks
From there, BricksMembers tracks the learner’s watch state and lets the rest of your lesson UI react to it.
Step 1: Enable Video Tracking
- Go to BricksMembers → Modules
- Enable Video Tracking
- Confirm the change
- Open BricksMembers → Video Tracking
If the module is turned off, the settings page stays informational and the watch-state features do not run on the frontend.
Step 2: Choose Your Default Video Rules
The Video Tracking settings page controls the default behavior for tracked lesson videos across the site. This is where you decide what “enough watched” means for your project.
- Required playback percent decides how much of the video must be watched before the threshold is considered reached
- Gate BRM next navigation until threshold controls next destinations returned by BricksMembers; manually authored links need a video-threshold Bricks condition
- Completion mode lets you keep completion manual, unlock it after the threshold, or auto-complete the lesson
- Quiz unlock mode and Downloads unlock mode let you tie follow-up content to video progress
- Resume mode and Restrict skipping ahead help you shape the playback experience for training-style lessons
- Store thumbnails in WordPress helps when you want placeholder or poster images managed in the media library. This is especially useful for Bunny libraries where direct CDN thumbnail URLs may be blocked by library or CDN security settings.
These are defaults, not permanent locks. If a specific lesson needs different rules, you can override key settings per post.
Step 3: Connect Your Video Hosts
If you want a cleaner workflow than pasting URLs manually, connect your video providers in the Video Hosts tab of the same admin area. The connected picker supports Mux, Wistia, Gumlet, Bunny, YouTube, Vimeo, and Cloudflare Stream. Livid is also supported for playback and tracking, but it is a manual-URL provider rather than a connected picker provider.
- Enable the provider you want to use
- Enter the required credentials or tokens for that provider
- Save the settings
- Use the built-in connection test if you want to verify the setup before editing lessons
Once connected, those providers become available in the BRM video picker inside the editor.
Be concrete about the connection requirements. Mux needs a Token ID and Token Secret. Wistia needs an API Token. Gumlet needs an API Key and can also store a Workspace ID. Bunny needs an Access Key and Library ID. YouTube uses a manual token setup: save the Client ID, Client Secret, Access Token, and Refresh Token, then use the exact Authorized Redirect URI shown in the provider’s Setup instructions block while generating the Google tokens. Vimeo uses a manually pasted Access Token with the public and private scopes. Cloudflare Stream needs a Cloudflare account ID and API token with Stream read access; customer code and signed URL TTL are optional. BRM does not open a Google, Vimeo, or Cloudflare connect flow from this page.
Step 4: Add a Video or Audio Source to the Lesson
Edit the lesson or content post and open the BricksMembers meta box. This is where you connect the actual video or audio source that the frontend element will use. The BRM Video & Audio field stores the authored URL, provider value, or embed source. BRM stores the resolved provider separately so existing content stays compatible while the player can still read a normalized source.
For Bunny Stream, you can use a full Bunny embed URL, library_id/video_id, or a bare Bunny video ID when the Bunny provider is selected and your Bunny Library ID is saved in the Video Hosts tab. If Bunny is the only enabled and connected provider, BRM can also resolve a bare Bunny video ID automatically. Bare IDs otherwise resolve only through the explicit Bunny provider so they do not conflict with other providers that also use opaque IDs.
For Cloudflare Stream, use a Stream UID or a supported videodelivery.net / cloudflarestream.com Stream URL. BRM normalizes these sources as cloudflare_stream.
Since version 1.4.0, Livid is supported as a manual-URL provider. Choose Enter URL manually and paste a public https://livid.com/watch/..., /video/..., or /embed/... URL. BricksMembers converts it to the canonical Livid embed and supports watch tracking, resume prompts, completion thresholds, and seek restrictions through Livid’s documented Player.js receiver. A valid Livid URL that you saved before updating to version 1.4.0 works immediately after the update; you do not need to open and re-save the post. There are no Livid credentials or Browse button in BricksMembers because Livid does not currently expose a documented third-party account-library API. In other words: Livid playback is supported, but BricksMembers cannot list the videos in your Livid account.
The video must still be embeddable on your course domain. Review Livid’s embedding guide and domain access guide before publishing. If you use BRM’s Hide timeline seek policy, BricksMembers requests controls=0; Livid documents that control hiding as a Pro feature. On plans where Livid keeps its controls visible, BRM still uses Player.js to snap an unauthorized forward seek back to the watched position.
- BRM Video & Audio lets you paste a direct URL, provider-specific value, or embed source
- Browse lets you choose a video from a connected provider through the picker
- Video Duration lets you store duration metadata for the lesson
- Video Image gives you an optional poster or cover image
- Video Title, Video Chapters, and Video Text Tracks JSON store optional learning metadata for dynamic tags, timeline markers, native tracks, and transcript cue access. Enter chapters as lines like
0:00 Intro, or JSON like[{"time_seconds":0,"title":"Intro"}].
When Video Tracking is active, the same area also exposes per-post overrides for the required percentage, completion mode, and next-navigation gating. That is useful when one lesson should be more strict or more relaxed than your default site-wide rules.
The saved Video Duration field can also be included in course or module learning-time totals. In Bricks, use a scoped reading-time tag such as {brm_post:reading_time:toplevel:minutes:with_video} when you want a raw numeric total, or {brm_post:reading_time:toplevel:human:with_video} when you want compact output such as 29mins or 1hr 30min.
Audio and Protected Media Since Version 1.4.0
Since version 1.4.0, the player is called BRM Video & Audio. You can use direct HTML5 audio URLs for MP3, M4A, AAC, WAV, OGG/OGA, FLAC, and Opus files. A direct WordPress Media Library URL or another public/external URL remains public and is visible to the browser as the media source. Adding that URL to the player does not make the file private.
Audio players use their natural control height instead of a video aspect ratio. Since version 1.4.0, the frontend runtime keeps the audio wrapper at aspect-ratio:auto, even when a Bricks class or template applies 16 / 9 to video wrappers. No custom CSS spacer or fixed player height is required.
When the audio or video needs BricksMembers access checks, create it as a Protected Download instead. Enable both Protected Downloads and Video Tracking. Then enter brm-download:123 in the post’s BRM Video & Audio field, replacing 123 with the Protected Download ID. The Enrollments module is needed only when that download is assigned directly to specific users. It is not required when the download uses the normal level-based access mode.
You can also build a playlist or library from a Bricks query loop. Query the brm_download post type, place BRM Video & Audio in the repeated item, and choose Current query loop item under Video source context. In JSON this is videoSourceContext=query_loop. BricksMembers automatically uses the current Protected Download as the source, so you do not need to create a second custom field or dynamic tag.
For local Protected Downloads, the player receives a short-lived BricksMembers gateway URL tied to the logged-in WordPress user and current session. The private filesystem path is not placed in the HTML. BricksMembers checks the source, content access, and download access again on every media request, and supports the HTTP Range requests that browsers use for seeking. Because the URL is viewer-specific, BricksMembers marks the page and stream responses private and non-cacheable. If a cache or CDN ignores WordPress no-cache signals, exclude that player or account page manually.
Remote storage uses the same BricksMembers gateway. BricksMembers uses the exact provider saved on that Protected Download and fails closed if it is missing or unavailable; it never silently changes to whichever provider is currently active. A remote HEAD request is answered locally. For GET, every signed provider URL stays on the server. BricksMembers requests validated 4 MiB byte-range chunks from the provider, stores each chunk temporarily outside the web root, and pipes the bytes to the authorized viewer. The provider URL, object key, and temporary file path therefore do not appear in page source or a browser redirect.
The remote proxy accepts files up to 500 MiB by default. Administrators can change the base limit with Remote Proxy Maximum File Size (MB) under Protected Downloads → Download Settings; developers can apply a final override with the brm_protected_media_proxy_max_file_bytes filter. A custom storage provider must support one byte range per request and return 206 Partial Content with exact Content-Range and Content-Length headers. It must also return a stable strong ETag or Last-Modified value so BricksMembers can verify that all chunks belong to the same file. BricksMembers sets inline for playback and attachment for an explicit download on its own response, so no provider-side Force Download or Content-Disposition rule is required.
The provider origin must itself be private. For S3 or Google Cloud Storage, deny unsigned public object reads. For Bunny or another CDN, use its private/token-authenticated origin setup. BricksMembers hides the signed origin URL and authorizes its own gateway, but it cannot make an object private when the original bucket or CDN URL is already publicly reachable.
Local and remote protected delivery both occupy a PHP worker while bytes are transferred. BricksMembers permits three simultaneous body-bearing protected-media transfers per WordPress user by default, across that user’s sessions and across local or remote files. Developers can tune the bounded limit with brm_protected_media_transfer_concurrency_limit and the recovery lease with brm_protected_media_transfer_lease_seconds. This design is suitable for a modest library and low-volume playback, but not for high-concurrency streaming without additional infrastructure planning. It is access-controlled delivery, not DRM. The raw local path, remote provider URL, provider key, and private temporary path are not exposed in the page source, but the browser must still receive media bytes to play them. BricksMembers therefore cannot prevent screen or audio recording or an authorized user from retaining media their device receives. Direct WordPress Media Library URLs and arbitrary public external URLs also remain public; only a Protected Download uses this gateway.
The Protected Download element is still a download/link or download-list element; it is not a player. Use BRM Video & Audio for playback. If you also want to offer the file as a download, add a native Bricks Button or Text Link. Set BricksMembers Action to Protected Media , Download protected media. Choose Current post or query-loop item for the current protected source, or choose Specific protected download and enter its ID.
A Protected Download is stored as the brm_download custom post type, but its WordPress single-post route and the public WordPress core REST collection are disabled. Do not use a post permalink as a member “view” action. Build the authorized list with the Protected Download element or an access-filtered Bricks query loop, use BRM Video & Audio to play media, and use the secure Button/Text Link action when the member should download the file.
For generated Bricks JSON, the same controls are brmButtonAction=protected_media:secure_download, brmProtectedMediaTarget=current_media or specific_download, and brmProtectedMediaDownloadId for the explicit ID. Legacy brmProtectedMediaAction content still works but should not be authored in new layouts. BricksMembers renders a short-lived fallback URL so the link is never saved as authored Bricks content. On a normal click, the frontend requests a fresh viewer-specific URL immediately before starting the download. Access is checked while issuing that URL and again when the gateway receives it, so a long-open account page does not rely only on its original render. Do not add another manual link URL to that Button or Text Link.
Local and remote playback or download bytes pass through PHP. That is appropriate for modest libraries and smaller audiences, and it gives BricksMembers per-request session checks plus Range support without exposing the provider URL. Plan separate delivery infrastructure before using this feature for very large files or high concurrent playback.
Step 5: Use the BRM Video & Audio Element in Bricks
Add the BRM Video & Audio element to your Bricks template or lesson layout. This is the frontend layer that displays the tracked media and reacts to the learner’s watch progress.
- Direct embed shows the player immediately
- Click to load shows a placeholder first and loads the player on interaction
- Lightbox opens the video in a lightbox flow
The element also includes placeholder controls for preview images, text, icons, overlays, and resume prompt styling, so the tracked player can fit naturally into your lesson design.
Since version 1.3.0, the element also includes playback speed, native Picture-in-Picture, focus-scoped keyboard shortcuts, off-screen/floating behavior, lazy loading, timed actions, threshold redirects, chapter timeline markers, and advanced watermark styling. Playback speed is per render and is not saved server-side. Keyboard and chapter-marker seeks respect the configured seek policy.
Use the advanced chapter and viewer-watermark controls only when the lesson design needs them. For chapters, choose Timeline markers only when the saved video has canonical chapter metadata and uses a native HTML5/HLS source. For watermarks, choose personalized/custom text or an authored image, then turn on Customize watermark styling only when you need custom typography, colors, opacity, spacing, border, shadow, rounded corners, or image width.
The element also has a Video source context control. Leave it on Current post for normal lesson templates. Choose Current query loop item when the BRM Video & Audio element is inside a Bricks query loop and each card should play and track the loop item’s own BRM media, such as related lesson video or audio cards.
When using Current query loop item for ordinary lessons or content, the loop item must be the actual post with its own BRM media source. Structure depth does not change this lookup, so a Course → Module → Lesson → Video structure is supported when the loop returns the Video post. Current post still reads the outer lesson in that setup. For a brm_download loop, BricksMembers instead resolves the current Protected Download automatically and always checks that download’s access before producing a playable URL. Users without download access do not receive the protected player source.
Since version 1.3.0, logged-in users get tracked watch progress when the BRM Video & Audio element renders playable media for them. Logged-out visitors can watch visible public/direct sources, but BricksMembers does not save guest watch state. Protected Download sources are different: they require a logged-in viewer with current download access even when Playback Access is set to Always allow playback. For public/direct sources, you can use the element’s Playback Access group or protect the surrounding lesson with your normal Bricks conditions, redirects, query filters, or layout rules.
The Playback Access group can block playback for logged-out users, selected BRM gates, or all BRM access gates. Selected gates include selected user levels, post required levels, enrollment, prerequisites, and drip. Selected user levels works like the Bricks condition for assigned levels: you choose one or more levels in the element, and the viewer must have at least one of them. Post required levels reads the required-level setting from the video post itself. When playback is blocked, the element can show an overlay message or render nothing. You can also configure After playback blocked to update matched wrappers by AJAX, which is useful when you want custom messages elsewhere on the page to refresh immediately.
What Happens When a Learner Reaches the Watch Threshold
This is where Video Tracking becomes especially useful. The BRM Video & Audio element includes an After Video Threshold Reached control, so the page can react immediately when the learner has watched enough. It is important to understand that the threshold being reached and the page visibly changing are two separate things. The watch state is always stored, but the visible UI change only happens if you configure an action here or if the page naturally re-renders later.
- Do nothing means the watch state is still saved, but nothing on the page changes right away. Use this when you only need the stored watch data for later checks, not an instant visual reaction
- Update matched wrappers (AJAX) refreshes the wrappers you target so quiz areas, progress blocks, or condition-based sections can redraw without a full page reload. It only updates the wrappers you tell it to refresh; it does not automatically guess which part of the layout should change
- Reload page with position triggers a full page reload, then restores scroll position or jumps to the selector you configured. This is the safest option when multiple parts of the page depend on updated watch state and you do not want partial-refresh edge cases
- Redirect sends the learner to a configured URL or post after optional completion confirmation and delay. Use it for flows like going to a quiz after 90% watched or after completion
This makes it possible to reveal the next lesson button, unlock a quiz, refresh a progress bar, or show a protected download right after the required watch percentage is reached.
Gate BRM Next Navigation applies to next destinations resolved by BricksMembers, including the BRM Post Navigation element, BRM next-item dynamic tags, and BRM navigation actions. It does not automatically change a manually authored Bricks Button, Text Link, or custom URL. For a custom next button, use a BRM: Video Watch → Video threshold reached Bricks condition and refresh an unconditional parent wrapper after the threshold, or use the BRM navigation element.
Require threshold for manual completion is different: it guards BRM completion writes for the current post on the server. The current post must have the tracked BRM video source that the completion control refers to. The BRM Progress Checkbox renders a locked state automatically while this completion mode is active. Use After Video Threshold Reached to refresh that already-rendered checkbox when the threshold is reached.
A Good Default Setup for Most Course Creators
If you are setting this up for the first time, a practical starting point is:
- Require a meaningful watch percentage instead of expecting 100% on every lesson
- Use manual completion unlock if you still want learners to confirm they are done
- Enable next-navigation gating only on lessons where sequence actually matters
- Use resume prompts or auto-resume for longer lessons and trainings
- Test one lesson end to end before rolling the setup across the whole site
That approach gives you a stricter and more useful learning flow without making every lesson feel heavy-handed.
Video Watch Dynamic Tags You Can Use in Bricks
BricksMembers also gives you dynamic tags for watch progress, so you can display progress-aware helper text directly inside the lesson design. These tags are meant for logged-in learner contexts on tracked posts. They read the effective watch state for the current post context, which is normally the current post and can be the active loop item in Bricks post query loops.
Since version 1.1.0, read-only video-watch tags and the BRM Progress Bar video mode can also resolve a target user inside Bricks user query loops, including loops filtered by Users: Group Membership, and BricksMembers profile pages. This only affects display. Video watch writes still belong to the current viewer, and cross-user display requires the target user’s privacy settings and group-role permissions to allow video progress visibility.
The BRM Progress Bar in Video mode defaults to the stored watched-coverage state from BricksMembers, and the video-watch dynamic tags read that same stored state. If you want the bar to follow the temporary player position instead, set the Progress Bar’s Video Progress Value control to Live playback position. During playback, stored-progress displays update after successful watch-state sync responses.
When the current lesson/post has its own BRM Video source, watched coverage is read from that current post. BricksMembers does not guess relationship fields for video watch state. If you need an overview of videos below a course, module, lesson, or playlist shell, use the explicit summary modifier described below.
{brm_video_watch:percent}returns the watched percentage as a numeric string, for example67.5or100. This is not rounded to a whole number by default{brm_video_watch:required_percent}returns the required threshold as an integer string, for example80{brm_video_watch:remaining_percent}returns the remaining percentage needed to hit the threshold, rounded to two decimals and never below0. If the learner already passed the threshold, this tag returns0{brm_video_watch:threshold_reached}returns1when the required watch threshold has been reached and0when it has not{brm_video_watch:completed}returns1only when the runtime marks the video as fully watched. This is not the same as threshold reached. A learner can havethreshold_reached = 1whilecompleted = 0{brm_video_watch:any_completed}returns1when the video is fully watched or the same post is completed through Progress Tracking. Use this for lesson/sidebar completion indicators that should accept either source{brm_video_watch:last_position}returns the actual last saved playback position in seconds, for example325for 5 minutes and 25 seconds- Since version 1.3.0,
{brm_video_watch:resume_position}returns the resume-safe playback position in seconds. This can be higher thanlast_positionwhen BRM has a saved max watched position - Since version 1.3.0,
{brm_video_watch:last_position_formatted}and{brm_video_watch:resume_position_formatted}return formatted watch times like05min 25secor01h 02min 03sec {brm_video_watch:resume_available}returns1only when there is a meaningful resume point. In the current runtime, that means the stored last position is at least 5 seconds in and not within the last 5 seconds of the video. Otherwise it returns0- Since version 1.3.2,
{brm_video_watch:summary:percent}returns the average watched percentage across video posts below the current structure item - Since version 1.3.2,
{brm_video_watch:summary:completed_count}and{brm_video_watch:summary:total_count}return the completed and total video-post counts below the current structure item - Since version 1.3.2,
{brm_video_watch:summary:completed_percent}returns the percentage of counted video posts that are fully watched - Since version 1.3.2,
{brm_video_watch:summary:threshold_reached_count}returns how many counted video posts have reached their watch threshold - Since version 1.3.2,
{brm_video_watch:summary:remaining_count}returns how many video posts below the current structure item are not fully watched - Since version 1.3.2,
{brm_video_watch:summary:all_completed},{brm_video_watch:summary:any_completed},{brm_video_watch:summary:all_threshold_reached}, and{brm_video_watch:summary:any_threshold_reached}return1or0for aggregate status checks
Since version 1.3.2, summary tags default to descendants below the current post. Add :children for direct child posts only, :current for the current post, or anchor modifiers such as :parentlevel, :toplevel, and :level_N. For example, {brm_video_watch:summary:completed_count:toplevel} counts completed videos below the current top-level course item.
A practical example is a helper message like Watch {brm_video_watch:required_percent}% to unlock the quiz. You have watched {brm_video_watch:percent}% so far. If the learner watched 67.5% of a lesson with an 80% threshold, that sentence will render as Watch 80% to unlock the quiz. You have watched 67.5% so far.
Also note the difference between the watch tags: percent is the current numeric progress, threshold_reached is the yes/no unlock state, and completed is the fully watched flag. Those are related, but they are not interchangeable.
If your video lesson also uses Progress Tracking, you can link dashboards or course catalog cards to the exact lesson the learner most recently completed with {brm_progress:completed_item:last:toplevel:url} and {brm_progress:completed_item:last:toplevel:title}. Use {brm_progress:continue:toplevel:url} for the next uncompleted lesson instead.
Common Use Cases
- Course lessons where a quiz should only appear after the learner has watched enough of the lesson video
- Training libraries where the next lesson should stay blocked until the current lesson is actually consumed
- Member dashboards that show real watch progress instead of only generic completion states
- Protected resources where a download should unlock after the learner reaches the watch threshold
- Long video lessons where resume prompts make the learning experience much smoother
Troubleshooting
Video learning controls (since version 1.4.0)
The BRM Video element can now add opt-in A–B looping, playback-speed buttons or a compact select, HLS.js quality choices, YouTube-style chapter markers, explicit WebVTT storyboard previews, and text or image viewer watermarks. The builder enables A–B looping per video, then each viewer sets temporary A and B points from the playhead; those points are not saved. Viewer watermarking is a traceability deterrent, not cryptographic DRM; Viewer Code is the cache-safe default, while display name and email must be selected explicitly.
Add the separate BRM Video Transcript element when a lesson needs a searchable transcript. Match its Player Binding Key to the video when multiple players use the same post. For sharing, use a native Bricks Button and choose BricksMembers Action → Lesson , Share lesson page; the shared URL is the current page, bound lesson-post permalink, or a safe custom URL, never the media source. A player binding is needed only for an optional current-time fragment.
In the post editor, WebVTT tracks use explicit Transcript, Chapters, Timeline Preview, or Generic purposes. Previewing chapters from a saved VTT track updates the editor for review only. Save the post normally to persist the previewed chapter list.
- Tracking is not starting , Confirm the Video Tracking module is enabled and the post actually has a video source
- The Browse button is missing , Connect and enable at least one supported video provider first
- The threshold never unlocks anything , Recheck the required percentage, post-level overrides, and the After Video Threshold Reached action on the BRM Video & Audio element
- Playback progress moves but watched coverage stays at 0% , Check the browser Network tab for
brm_get_video_watch_stateandbrm_sync_video_watch_state. Logged-out visitors do not send tracking requests. Logged-in playable renders send awatch_token; if the request is missing that token or returnsInvalid video tracking session, the page is not using the current BRM Video & Audio render config. Analytics messages such asUser excluded from trackingusually come from AnalyticsWP pageview tracking and are separate from BRM video watch tracking. - Bunny provider thumbnails show as broken images , Make sure the Bunny Access Key and Library ID are saved in the Video Hosts tab, then enable Store thumbnails in WordPress so BRM can fetch the provider thumbnail server-side and use the local media-library copy. BRM does not guess Bunny CDN thumbnail hosts because restricted Bunny libraries can block those guessed URLs. If you use Custom image with an ACF or Bricks dynamic tag, the tag resolves against the video post being rendered, including query-loop video items.
- Next lesson navigation stays blocked , Review your global and per-post navigation gating settings
- Resume behavior feels wrong , Revisit the global resume mode and skip restriction settings in BricksMembers → Video Tracking