# Blue Billywig WordPress Plugin - User Guide

## Table of Contents

1. [Getting Started](#getting-started)
2. [Connecting to Blue Billywig](#connecting-to-blue-billywig)
3. [Uploading Videos](#uploading-videos)
4. [Browsing the Video Library](#browsing-the-video-library)
5. [Embedding Videos in Posts](#embedding-videos-in-posts)
6. [Embedding Channels](#embedding-channels)
7. [Using oEmbed (Paste to Embed)](#using-oembed)
8. [Using the Shortcode](#using-the-shortcode)
9. [Video Transcripts](#video-transcripts)
10. [Video Chapters](#video-chapters)
11. [Video SEO](#video-seo)
12. [Content Protection](#content-protection)
13. [Folders](#folders)
14. [Analytics Dashboard](#analytics-dashboard)
15. [Settings Reference](#settings-reference)
16. [Troubleshooting](#troubleshooting)

---

## Getting Started

The Blue Billywig plugin lets you manage, upload, and embed video content from your Blue Billywig Online Video Platform (OVP) directly within WordPress. Videos are hosted on Blue Billywig's infrastructure and streamed via CDN -- no video files are stored on your WordPress server.

### Requirements

- WordPress 5.9 or higher (tested up to 6.9)
- PHP 8.1 or higher
- A Blue Billywig publication with API credentials

### Installation

1. Install the plugin from the WordPress plugin directory, or upload the `blue-billywig` folder to `/wp-content/plugins/`
2. Activate the plugin via **Plugins > Installed Plugins**
3. Go to **Blue Billywig > Settings** to enter your API credentials

---

## Connecting to Blue Billywig

Navigate to **Blue Billywig > Settings** in the WordPress admin sidebar.

Enter your credentials:

- **Publication** -- Your publication name (e.g., `mycompany`). This is the subdomain part of `mycompany.bbvms.com`.
- **API ID** -- Your API token ID from the Blue Billywig OVP.
- **API Secret** -- Your API token secret.

Click **Test Connection** to verify the credentials work before saving. If the test succeeds, click **Save Settings**.

Additional settings:

- **Default Playout** -- The player skin used for video embeds. Select from available playouts in your publication.
- **Embed Type** -- How videos are embedded: JavaScript (recommended), iframe, URL, AMP, or oEmbed.
- **Videos Per Page** -- Number of videos shown per page in the library (default: 15).

---

## Uploading Videos

Navigate to **Blue Billywig > Upload** in the admin sidebar.

### Setting Metadata Before Upload

Before uploading, you can fill in:

- **Title** -- Leave empty to use the filename as the title.
- **Description** -- Optional video description.
- **Tags** -- Comma-separated tags for categorization.
- **Status** -- Published (immediately available) or Draft.

### Uploading

Drag and drop video files onto the upload area, or click **browse files** to select them from your computer.

Supported formats include MP4, MOV, AVI, WebM, MKV, MXF, MPEG, FLV, WMV, and common audio formats (MP3, WAV, AAC, FLAC, M4A).

Files can be up to 20 GB. Large files are automatically split into chunks and uploaded in parallel. If your connection drops during upload, you can retry -- the upload resumes where it left off.

After upload completes, the video appears in the Blue Billywig library. Transcoding starts automatically and typically takes a few minutes depending on file size and format.

---

## Browsing the Video Library

Navigate to **Blue Billywig > Library** in the admin sidebar.

The library shows all videos in your Blue Billywig publication with thumbnail previews. Use the controls at the top to:

- **Search** -- Type in the search box to filter by title
- **Published date** -- Filter by creation date
- **Media type** -- Filter by video, audio, image, or document

### Folder Navigation

If your publication uses folders, a folder tree appears on the left sidebar. Click a folder to show only videos in that folder. Click **All videos** to return to the unfiltered view. Folders are nested -- click through the hierarchy to drill down.

### Video Details

Click a video to see its details: creation date, update date, status, and current playout. From the detail view you can:

- **Change the playout** for this specific video
- **Copy the embed code** for pasting into other contexts
- **Edit** the video metadata
- **Delete** the video

---

## Embedding Videos in Posts

### Using the Gutenberg Block (Recommended)

1. In the block editor, click **+** to add a new block
2. Search for **Blue Billywig Embed**
3. In the block settings sidebar (right panel), search for a video by typing at least 3 characters
4. Click a video from the search results to select it
5. The video preview appears in the editor

Block settings (in the sidebar):

- **Choose Playout** -- Override the default playout for this embed
- **Show Transcript** -- Display the AI-generated transcript below the video (default: on)
- **Show Chapters** -- Display clickable chapter navigation below the video (default: on)

### Using the Classic Editor

1. Click the **Blue Billywig media** button above the editor
2. Browse or search for a video in the modal
3. Click **Copy embed code** on the video you want
4. Paste the embed code into the editor

---

## Embedding Channels

Channels are branded video portals with swim lanes, search, and detail pages.

### Using the Gutenberg Block

1. Add a new block and search for **Blue Billywig Channel**
2. In the block settings sidebar, select a channel from the dropdown
3. Optionally set the height (default: 600px, supports CSS values like `80vh`)

The channel renders on the published page as a full interactive video portal.

---

## Using oEmbed

The simplest way to embed a video: just paste its URL on its own line in the editor.

Supported URL formats:

```
https://yourpub.bbvms.com/p/default/c/12345.html
https://yourpub.bbvms.com/view/default/12345.html
https://yourpub.bbvms.com/ch/199.html
```

WordPress automatically converts these into embedded video players or channel portals. This works in both the block editor and classic editor.

---

## Using the Shortcode

For themes or page builders that don't support blocks, use the shortcode:

```
[blue-billywig-embed videoid="12345"]
```

Replace `12345` with the clip ID from your Blue Billywig publication. The embed uses your configured default playout and embed type.

---

## Video Transcripts

When a video has an AI-generated transcript (created by Blue Billywig's transcription service), it appears below the player in a collapsible "Transcript" section.

Transcripts are:

- **Open by default** -- visitors see the text immediately
- **Collapsible** -- click the "Transcript" header to collapse/expand
- **Scrollable** -- long transcripts have a scrollable container
- **SEO-indexed** -- search engines can read and index the transcript text, making your video content discoverable through text search

If no transcript is available but the video has subtitles, the subtitle text is displayed as a fallback.

To enable transcripts for a video, configure the transcription service in your Blue Billywig OVP.

Toggle transcripts on/off per embed in the Gutenberg block settings sidebar.

---

## Video Chapters

When a video has chapter markers defined in the Blue Billywig OVP, a "Chapters" section appears below the player.

Each chapter shows:

- **Timestamp** (e.g., 0:00, 2:15, 5:30)
- **Chapter title**

Chapters help viewers navigate long videos and improve engagement.

Toggle chapters on/off per embed in the Gutenberg block settings sidebar.

---

## Video SEO

The plugin automatically generates structured data for search engines:

### JSON-LD VideoObject Schema

When using iframe, oEmbed, URL, or AMP embed types, the plugin outputs a JSON-LD `VideoObject` schema block containing:

- Video title and description
- Thumbnail URL
- Upload date
- Duration
- Transcript text (if available)

This helps search engines understand and display your video content in search results, potentially showing video rich snippets.

Note: When using JavaScript embeds, the Blue Billywig player handles schema output via its built-in tagging engine, so the plugin skips JSON-LD to avoid duplicates.

### Video XML Sitemap

Videos embedded in posts (via blocks or shortcodes) are automatically included in the WordPress XML sitemap. Each video entry contains title, description, thumbnail, player URL, duration, and publication date.

---

## Content Protection

If your Blue Billywig publication uses Content Protection Policies (CPP), you can gate video access based on WordPress user roles.

### How It Works

1. A video with a CPP assigned requires authentication to play
2. When a logged-in WordPress user with an authorized role views the page, the plugin signs a JWT token with the CPP shared secret
3. The JWT is appended to the video embed URL
4. The Blue Billywig platform validates the JWT and serves the content
5. Unauthorized or logged-out visitors see a gate message with a login link

### Setup walkthrough

Setting up content protection takes three parts: **(A)** create a CPP in your Blue Billywig OVP, **(B)** save its settings to WordPress, **(C)** assign the CPP to the clips you want to protect.

#### Part A — Create the CPP and get its shared secret (in the OVP)

1. Log in to your Blue Billywig OVP at `https://<your-publication>.bbvms.com/ovp/`.
2. Open **Content** (left sidebar) → **Content protection policies** (or navigate to `/ovp/#/contentProtection`).
3. Click **+ New policy**. Give it a descriptive name (e.g. "Premium members").
4. Under **Rules**, add a rule of type **Shared Secret / API key token**. This is the mechanism the plugin uses — the CPP's shared secret is used by the platform gatekeeper to validate JWTs signed by WordPress.
5. The OVP will either auto-generate a secret or let you paste one. **Copy this secret value now — you will need it in step B.** A typical secret looks like `557751965e2acdf48d54b96350896e3d` (32 hex characters).
6. Save the policy. Note the policy's **ID** (visible in the URL or listing) — you'll need it in Part C.

> Keep the secret private. Anyone with the secret can sign JWTs that grant access to every clip protected by this policy. If it leaks, rotate it (edit the policy, regenerate the secret, save, then update WordPress in Part B again).

#### Part B — Save the secret and enable protection in WordPress

There is currently no admin UI for these options (planned for a future release). You have three equivalent ways to set them, pick whichever fits your workflow.

**Option 1 — WP-CLI (recommended if you have SSH access)**

```bash
wp option update blue-billywig-cp-enabled 1
wp option update blue-billywig-cp-secret '557751965e2acdf48d54b96350896e3d'
wp option update blue-billywig-cp-expiry 3600
wp option update blue-billywig-cp-roles '["administrator","editor","subscriber"]' --format=json
```

Replace the secret value with your own from step A.5. Adjust the role list to match the WordPress roles allowed to view protected content.

**Option 2 — SQL (direct database access)**

```sql
INSERT INTO wp_options (option_name, option_value, autoload) VALUES
  ('blue-billywig-cp-enabled', '1', 'yes'),
  ('blue-billywig-cp-secret',  '557751965e2acdf48d54b96350896e3d', 'yes'),
  ('blue-billywig-cp-expiry',  '3600', 'yes'),
  ('blue-billywig-cp-roles',   'a:3:{i:0;s:13:"administrator";i:1;s:6:"editor";i:2;s:10:"subscriber";}', 'yes')
ON DUPLICATE KEY UPDATE option_value = VALUES(option_value);
```

Replace the secret value. The `cp-roles` value is a PHP-serialized array — use the exact format above, changing only the role slugs and the leading count (`a:N:`).

**Option 3 — PHP snippet (in a mu-plugin or custom helper)**

```php
update_option( 'blue-billywig-cp-enabled', 1 );
update_option( 'blue-billywig-cp-secret', '557751965e2acdf48d54b96350896e3d' );
update_option( 'blue-billywig-cp-expiry', 3600 );
update_option( 'blue-billywig-cp-roles', array( 'administrator', 'editor', 'subscriber' ) );
```

Run this once (e.g. via a one-off WP-CLI `eval` command) and then remove the snippet — you don't want it running on every page load.

#### Part C — Assign the CPP to your clips (in the OVP)

Protection only activates on clips that have the CPP assigned. You can do this per clip:

1. Open a clip in the OVP
2. Go to its **Metadata** (or **Protection**) tab
3. Select the CPP you created in Part A from the **Content Protection Policy** dropdown
4. Save

You can also bulk-assign in the OVP via multi-select in the media library, or via API: `PUT /sapi/mediaclip/{id}` with body `{ "cpp": "<cpp-id>" }`.

#### Part D — Test

1. Log out of WordPress (or open a private window) and visit a post with a protected clip embedded. You should see the gate message.
2. Log in as a user with an allowed role. The clip should play.
3. Log in as a user with a disallowed role. You should see the gate message.

### Configuration reference

| Option | Description | Default |
|--------|-------------|---------|
| `blue-billywig-cp-enabled` | Enable/disable content protection | `0` (disabled) |
| `blue-billywig-cp-secret` | Shared secret from your CPP (set in Part A) | (empty) |
| `blue-billywig-cp-roles` | Array of WordPress role slugs that get access | `administrator, editor, author, subscriber` |
| `blue-billywig-cp-expiry` | JWT token validity in seconds (lower = more rotation, higher = less load) | `3600` (1 hour) |
| `blue-billywig-cp-message` | Custom HTML gate message (sanitized with `wp_kses_post`) | Default message with login link |

### Behavior

- Videos **without** a CPP assigned always play normally, regardless of these settings
- Videos **with** a CPP assigned are checked: if the current user has an authorized role, the JWT is injected; otherwise the gate message appears
- If the API is unreachable or the shared secret is not configured, content is blocked (fail-closed) to prevent accidental exposure

### Integration with Membership Plugins

Because content protection uses standard WordPress roles, it works with every major membership / paid access plugin without any custom code. The membership plugin's job is to assign the right role (or create custom roles per tier) when a user subscribes, upgrades, downgrades, or cancels. The Blue Billywig plugin's job is to check whether the current user's role is in the allowed list.

The common pattern for all plugins below is:

1. Create one or more WordPress roles that represent paid tiers (e.g. `bb_basic`, `bb_premium`, `bb_enterprise`)
2. Configure the membership plugin to assign these roles to active members
3. Set `blue-billywig-cp-roles` to include the tiers that should see protected content

#### MemberPress

MemberPress assigns a WordPress role per membership level. In **MemberPress > Memberships > Edit Membership > Advanced**, set "WordPress Role" to a custom role (create via the User Role Editor plugin or programmatically).

For gated videos:

```php
// Grant access to "Premium" and "VIP" membership tiers only.
update_option( 'blue-billywig-cp-roles', array( 'mepr_premium', 'mepr_vip' ) );
```

MemberPress's built-in Rules engine can also be used to restrict entire posts/pages that contain BB embeds, giving you two layers of protection (page-level block from MemberPress, plus video-level gate from BB).

#### Restrict Content Pro (RCP)

RCP stores membership status separately from WP roles by default, but a role can be assigned per subscription level in **RCP > Membership Levels > Edit Level > Role**.

```php
update_option( 'blue-billywig-cp-roles', array( 'rcp_silver', 'rcp_gold' ) );
```

For finer control, use RCP's `rcp_member_can_access` filter in combination with BB — e.g. if a user can access the post, inject a custom role for the duration of the page render that BB's role check sees.

#### Paid Memberships Pro (PMP)

PMP assigns users to "membership levels" that can map to WP roles via the **PMPro Add On - Roles for Series**. In PMP admin, each level can have a corresponding role.

```php
update_option( 'blue-billywig-cp-roles', array( 'pmpro_level_2', 'pmpro_level_3' ) );
```

PMP also exposes a `pmpro_has_membership_access_filter` that can be used to dynamically extend BB's role check with PMP's per-post access rules.

#### WooCommerce Memberships

WooCommerce Memberships uses "Membership Plans", which can be tied to WooCommerce products. Each plan can be mapped to a role through the **User Role Editor** or via this snippet:

```php
add_action( 'wc_memberships_user_membership_status_changed', function( $user_membership ) {
    $user = $user_membership->get_user();
    $plan_slug = $user_membership->get_plan()->get_slug();
    if ( $user_membership->get_status() === 'active' ) {
        $user->add_role( 'bb_' . $plan_slug );
    } else {
        $user->remove_role( 'bb_' . $plan_slug );
    }
});
```

Then `blue-billywig-cp-roles` lists `bb_basic`, `bb_premium`, etc.

#### LearnDash / LifterLMS (LMS scenarios)

LearnDash assigns a `group_leader` role automatically and lets you create course-specific groups. For video courses, create a WordPress role per course (or per group) and allow only that role:

```php
update_option( 'blue-billywig-cp-roles', array( 'ld_course_101', 'ld_course_202' ) );
```

LearnDash's course completion hooks can add/remove roles as students progress.

### Multiple Tiers with Different Access Levels

The built-in `blue-billywig-cp-roles` setting treats all allowed roles equally — if you're in one of them, you get access to every CPP-protected video. For per-tier access (tier A sees some videos, tier B sees others), use one of these approaches:

**Option 1 — One CPP per tier.** Create multiple CPPs in the Blue Billywig OVP, each with its own shared secret. Use a small custom plugin to select the right CPP secret based on the current user's highest tier. This requires extending the plugin's `SapiClient::signJwt()` call with dynamic secret lookup.

**Option 2 — Role-based clip assignment.** Tag each clip in BB with metadata (e.g. a custom field `required_tier`). Add a `render_block_bluebillywig/embed` filter in your theme/plugin that checks whether the current user's role matches the clip's tier before allowing the BB plugin's gate check to proceed.

**Option 3 — Page-level gating.** Rely on the membership plugin to restrict entire pages, and keep BB's gate as a second layer. Simple and reliable for most cases.

### Scale Considerations

The BB content gating itself is fast — an in-memory role check and an HMAC-SHA256 JWT sign per page view. WordPress can support hundreds of thousands of subscriber accounts with proper DB tuning (persistent object cache like Redis, indexed `user_meta` lookups).

Limits to watch for at larger scale:

- **Login friction.** Every viewer needs a WP account. SSO/OAuth plugins (WP SAML Auth, Nextend Social Login, miniOrange SSO) reduce this friction and integrate with the same role system.
- **JWT expiry.** Default is 1 hour. A viewer who caches a page or shares the URL could inadvertently grant access to others for up to that period. Lower the `blue-billywig-cp-expiry` option for more frequent rotation at the cost of more gate renders.
- **Secret rotation.** If the CPP shared secret leaks, rotate it in the OVP and update `blue-billywig-cp-secret` in WordPress. All outstanding JWTs become invalid instantly.
- **Enterprise identity.** For tens of millions of viewers or enterprise SSO requirements, treat WordPress as the editorial CMS and put a dedicated identity provider (Auth0, Okta, Azure AD) in front. Have that system mint JWTs directly using the CPP secret, bypassing WordPress role-based gating.

---

## Folders

If your Blue Billywig publication uses folders to organize content, the WordPress plugin shows a navigable folder tree in the video library.

### In the Library Page

The folder tree appears as a sidebar on the left of **Blue Billywig > Library**. Click a folder to filter the video grid to that folder's contents. Click **All videos** to show everything.

Folders can be nested. The tree shows the full hierarchy from your publication.

### In the Media Library Tab

When inserting videos via the classic editor's "Blue Billywig media" button, the same folder tree is available in the sidebar of the media picker.

### Managing Folders

Folders are created and managed in the Blue Billywig OVP, not in WordPress. The WordPress plugin is read-only for folder structure -- it mirrors whatever is configured in your publication.

---

## Content Security Policy (CSP) — nonce support

If your site enforces a strict CSP that disallows `unsafe-inline` for scripts (a common security hardening), you need a way to allow Blue Billywig's embed script tags. The plugin supports the standard `nonce` mechanism: any `<script>` tag the plugin generates (block embeds, channel embeds, oEmbed fallbacks, shortcodes, JSON-LD) will receive a `nonce="..."` attribute when one is provided via the `bluebillywig_csp_nonce` filter.

The Blue Billywig player extracts this nonce from its embed script tag and propagates it to all dynamically created scripts and styles, so a single nonce on the BB embed unlocks the entire player runtime.

### Wiring it up

In your theme's `functions.php`, a security plugin, or a small mu-plugin, hook the filter to return the nonce your CSP layer is using:

```php
add_filter( 'bluebillywig_csp_nonce', function () {
    // Return the per-request CSP nonce from your CSP plugin / framework.
    // Example: a CSP plugin might expose it as a global or via its own filter.
    if ( function_exists( 'my_csp_get_nonce' ) ) {
        return my_csp_get_nonce();
    }
    return defined( 'CSP_NONCE' ) ? CSP_NONCE : '';
} );
```

The nonce must be the SAME value that appears in the `Content-Security-Policy` HTTP header your site sends, e.g. `Content-Security-Policy: script-src 'self' 'nonce-abc123' https://*.bbvms.com;`.

### What gets the nonce

- BB embed `<script>` tags rendered by the Gutenberg embed block
- BB channel `<script>` tags rendered by the Gutenberg channel block
- oEmbed fallback `<script>` tags (when pasting BB URLs)
- Shortcode `<script>` tags (`[blue-billywig-embed]`)
- JSON-LD `<script type="application/ld+json">` blocks for SEO
- All `<script>` tags inside SAPI-fetched embed code (added via post-processing)

### What does NOT need the nonce

- BB media URLs in `src=` attributes — those are URLs, not inline scripts; covered by `script-src https://*.bbvms.com`
- Player network requests at runtime — same as above
- The plugin's own admin assets (`bb_main.js`, `bb_upload.js`, etc.) — WordPress core handles these via `wp_enqueue_script`, which respects CSP plugin filters automatically

### Without CSP

If you don't return a value from the filter (or never add it), the plugin behaves identically to before — no `nonce=""` attribute is added. Sites without strict CSP need no configuration.

---

## Analytics Dashboard

A dashboard widget appears on the WordPress admin home screen (**Dashboard**) showing video analytics for the last 7 days.

The widget displays:

- **Views** -- Total video views
- **Player Loads** -- Total times a player was loaded on a page
- **Play Rate** -- Percentage of player loads that resulted in a view
- **Top 5 Videos** -- Most-viewed videos with view counts
- **Device Breakdown** -- Bar chart showing views by device type (desktop, mobile, tablet, etc.)

Analytics data is cached for 15 minutes to reduce API calls.

---

## Settings Reference

Navigate to **Blue Billywig > Settings**.

| Setting | Description |
|---------|-------------|
| **Publication** | Your Blue Billywig publication name (subdomain of bbvms.com) |
| **API ID** | API token ID from the OVP |
| **API Secret** | API token secret from the OVP |
| **Default Playout** | Player skin for embeds (dropdown populated from your publication) |
| **Embed Type** | JavaScript (default), iframe, URL, AMP, or oEmbed |
| **Videos Per Page** | Number of videos per page in the library |

### Platform Link

**Blue Billywig > Platform** provides a direct link to your Blue Billywig OVP admin interface.

---

## Troubleshooting

### "Blue Billywig API not configured"
Go to **Settings** and enter valid API credentials. Click **Test Connection** to verify.

### Videos don't appear in the library
- Check that your API credentials are correct (Test Connection)
- Verify your publication has published videos
- Check the browser console for JavaScript errors

### Upload fails or stalls
- Check your browser console for error messages
- Verify your API credentials allow upload operations
- Try a smaller file first to rule out network issues
- Uploads go directly to Blue Billywig's storage -- WordPress server limits don't apply

### Video doesn't play on the page
- Check if the video has a Content Protection Policy -- if so, ensure content protection is configured in the plugin
- Verify the video status is "published" in the OVP
- Check the browser console for network errors

### Gutenberg block shows "Loading configuration..."
- Verify API credentials are set and working
- Check that the REST API is accessible (some security plugins block REST endpoints)

### Folder tree doesn't appear
- Folders must be created in the Blue Billywig OVP first
- If your publication has no folders, the sidebar is hidden automatically
- Check that your API credentials have permission to read folders

### Content protection shows gate message to admins
- Verify `blue-billywig-cp-secret` matches the shared secret from your CPP
- Check that your WordPress role is listed in `blue-billywig-cp-roles`
- Ensure `blue-billywig-cp-enabled` is set to `1`
