# Architecture

## Plugin Overview

Dental Education Videos is a WordPress Gutenberg block plugin that embeds HLS-streamed dental patient education videos. It supports both the block editor and `[muvi id="topic"]` shortcodes. Published by Toothority Patient Education.

## File Map

```
muvi-dental-videos/
  plugin.php              Main plugin file: block registration, authorization system, shortcodes, admin menu
  admin-page.php          WP admin page: hero with benefits, subscription status, pricing, video gallery with lightbox
  src/
    block.json            Block metadata (apiVersion 3), attribute definitions, asset handles
    index.js              Block registration: edit component, save markup, deprecated migration
    view.js               Frontend player: HLS init, custom controls, overlays, captions, context menu
    style.css             Shared styles (editor + frontend): player container, controls, overlays, menus
    editor.css            Editor-only styles: preview header, picker modal dark theme, category pills
    icons.js              Block icon SVG component
    data/
      videos.js           Video catalog: categories with slugs, ALL_VIDEOS flat list, lookup helpers
    components/
      video-placeholder.js  Placeholder shown when no video is selected
      video-picker.js       Fullscreen dark modal: category tabs, search, thumbnail grid, subscribe link
      video-preview.js      Editor preview: HLS or sample playback, full custom controls, subscribe button
  build/                  Webpack output (generated by `npm run build`), registered via register_block_type()
  assets/
    toothority-logo.png                 Legacy admin page logo (PNG)
    toothority-logo-long-color.svg      Admin page hero logo (color SVG)
    toothority-logo-long-white-pink.svg Picker modal header logo (white/pink SVG)
```

## Build System

- Uses `@wordpress/scripts` (webpack-based). Run `npm run build` from `muvi-dental-videos/`.
- Build output goes to `build/`. The block is registered via `register_block_type( __DIR__ . '/build' )`.
- `block.json` declares `viewScript`, `editorScript`, `style`, and `editorStyle` — WordPress auto-enqueues them.
- `hls.js` is bundled into both `view.js` (frontend) and `index.js` (editor) via npm import.

## Block Registration

- Block name: `muvi-dental/dental-education-video`
- Single attribute: `topic` (string) — the video slug (e.g. `"dental-implants"`)
- `save()` renders a `<video class="muvi muvi-player" data-topic="...">` inside a wrapper div
- `view.js` hydrates all `.muvi-player` elements on DOMContentLoaded
- Deprecated block migration handles the legacy `title` attribute rename to `topic`

## External Services

| Service | URL Pattern | Purpose |
|---|---|---|
| HLS Streaming | `https://app.toothority.com/stream?l=1&v={topic}` | Full-quality video (authorized sites) |
| HLS + HEVC | `https://app.toothority.com/stream?l=1&c=hevc&v={topic}` | HEVC variant for Safari |
| Sample Videos | `https://assets.toothority.com/samples/{topic}.mp4` | Watermarked preview (unauthorized / editor fallback) |
| Poster Images | `https://assets.toothority.com/img/posters-chairside/{topic}.jpg` | Video thumbnails |
| Captions | `https://assets.toothority.com/vtt/{topic}/en.vtt` | English WebVTT captions |

## Authorization System

The streaming server (`app.toothority.com`) checks the requesting site's domain via Origin/Referer headers. The plugin detects authorization status server-side and propagates it to both frontend and editor JS.

### PHP Side (`plugin.php`)

1. **`muvi_check_authorization()`** — Sends a `HEAD` request to the stream endpoint with the site's `Origin` and `Referer` headers. Caches result as a WordPress transient (`muvi_stream_authorized`) for 5 minutes. Stores `'yes'`/`'no'` strings (not booleans) because `get_transient()` returns `false` for missing keys.

2. **`muvi_localize_auth_status()`** (hooked to `enqueue_block_assets`) — Injects `window.muviAuth = { authorized: true/false }` into both view and editor script handles via `wp_localize_script()`.

3. **`render_block` filter** (`muvi_render_block_auth()`) — Stamps `data-authorized="0"` or `"1"` on the block wrapper div at render time. Does not modify `save()` output (avoids block validation errors).

4. **Shortcode handler** — Adds `data-authorized="0"` to the video element for unauthorized sites.

5. **`muvi_handle_recheck()`** (hooked to `admin_init`) — Handles the admin page "Recheck Subscription" form: deletes transient, re-probes, redirects (POST-redirect-GET pattern).

### Frontend (`view.js`)

- Reads `data-authorized` attribute from the block wrapper (primary source)
- Falls back to `window.muviAuth.authorized` global (secondary source, defaults to `true`)
- **Authorized**: Full HLS player with custom controls, captions, context menu
- **Unauthorized**: Poster image + amber warning triangle overlay, no video playback, no controls

### Editor (`video-preview.js`, `video-picker.js`)

- Reads `window.muviAuth?.authorized` (defaults to `false` — shows subscribe UI when unknown)
- **Authorized**: Streams full HLS video in the editor preview (via hls.js)
- **Unauthorized**: Plays sample MP4, shows "Sign Up for $99/mo" button in preview header and picker modal

## Data Flow

```
Page request
  -> plugin.php: muvi_check_authorization()
    -> transient cache hit? return cached value
    -> cache miss? HEAD request to stream endpoint -> cache 5 min
  -> render_block filter: stamps data-authorized="0|1" on block wrapper
  -> wp_localize_script: injects window.muviAuth = { authorized: bool }

Frontend (view.js):
  -> reads data-authorized from wrapper (primary) + window.muviAuth (fallback)
  -> unauthorized? poster + warning overlay, early return
  -> authorized? full HLS player with controls

Editor (video-preview.js):
  -> reads window.muviAuth.authorized
  -> authorized? HLS stream via hls.js
  -> unauthorized? sample MP4 + subscribe button

Editor (video-picker.js):
  -> reads window.muviAuth.authorized
  -> unauthorized? shows subscribe link in modal header
```

## Video Catalog

The video list lives in `src/data/videos.js` as `VIDEO_CATEGORIES` (array of `{ name, slug, videos }` objects). Videos can appear in multiple categories. `ALL_VIDEOS` is a deduplicated flat list.

The admin page (`admin-page.php`) has a separate PHP video catalog array — these must be kept in sync manually.

## Admin Upsell Notice

A dismissible admin notice banner shown on all WP admin pages for unauthorized (unsubscribed) sites. Defined in `plugin.php`.

- **`muvi_admin_upsell_notice()`** (hooked to `admin_notices`) — Only renders for `manage_options` users on unauthorized sites. Branded with magenta border, movie camera icon, CTA button ("Get Toothority — $99/mo"), and inline "Learn more" link to `toothority.com/toothority-web`.
- **Dismissal**: Uses WordPress's built-in `is-dismissible` notice class. On dismiss, an AJAX request stores a 30-day expiry timestamp in `user_meta` (`muvi_notice_dismissed_until`). The notice resurfaces after 30 days.
- **`muvi_dismiss_upsell_notice()`** (hooked to `wp_ajax_muvi_dismiss_upsell_notice`) — AJAX handler that verifies nonce and writes the user meta.

## Admin Page

Located at WP Admin > Dental Videos (`admin-page.php`). Self-contained PHP page with inline CSS and JS (no build step). Uses the standard WordPress `<div class="wrap">` container and `<h1>` page title for proper admin layout alignment. Sections:

1. **Hero** — Logo, description, 6 benefit cards in 3×2 grid (Increase Case Acceptance, Save Chair Time, Keep Visitors on Your Site, Chairside Player Included, Reduce Patient Anxiety, Elevate Your Practice)
2. **Subscription Status + Pricing** — Side-by-side cards. Left: Active/Not Active badge + Recheck button. Right: $99/mo pricing + "Get Toothority" CTA
3. **Free-to-try note** — Callout explaining free preview access and what a subscription unlocks (website streaming + chairside player)
4. **Video Gallery** — Dark-themed (`#141414`) section matching the editor picker visual style. Category pill filters, search input, thumbnail grid with poster images. Clicking a thumbnail opens a lightbox modal.
5. **Lightbox** — Fixed overlay with blur backdrop. Shows sample MP4 video player and video title. Closes via X button, backdrop click, or Esc key.

The video catalog is defined as a PHP array at the top of the file, mirroring `src/data/videos.js`. These must be kept in sync manually. All styles use a `muvi-admin-` class prefix to avoid conflicts with WordPress admin CSS.
