# Video Pages & SEO — Dedicated Watch Pages for Google Video Indexing

**Status**: ready to build
**Created**: 2026-03-24
**Updated**: 2026-03-24

## Problem

Since December 2023, Google requires videos to be the **main content of a page** to appear in the Video tab of search results. Product pages with embedded videos are explicitly excluded — Google classifies them as "product pages", not "video pages".

This means the module's current Schema.org VideoObject microdata on product pages cannot rank in Google Videos. The microdata still helps with regular search snippets, but video-specific search visibility is lost.

**Sources**: Google Video SEO Best Practices (developers.google.com/search/docs/appearance/video), Search Engine Land Dec 2023 coverage.

## Solution

Create dedicated video "watch pages" where each unique video gets its own SEO-optimized URL. The video is the hero/main content, and products using that video are listed below as a grid. Additionally, a Video Gallery index page provides browsable access and internal linking.

### URL Structure
- Video page: `/video/{id_video}/{slug}` (e.g., `/video/dQw4w9WgXcQ/product-demo-video`)
- Gallery page: `/videos/`
- Video sitemap: `/sitemap-videos.xml`

### Key SEO Elements
- Video is the main content of the page (above the fold, full-width player)
- VideoObject JSON-LD with `mainEntityOfPage` property (signals to Google this IS the video's primary page)
- Canonical URL, Open Graph video tags
- Video sitemap in Google Video Sitemap 1.1 format for Search Console
- Stable thumbnail URLs (YouTube i.ytimg.com is stable; Vimeo thumbnails cached in DB)

### YouTube Caveat
Google already indexes YouTube videos on youtube.com. The store's video page for the same YouTube video may not outrank YouTube, but it provides: (1) internal linking value, (2) customer browsing experience, (3) ranking potential for product-specific video queries like "Product Name video review". Most valuable for Vimeo (especially unlisted) and HTML5 self-hosted videos where no competing canonical exists.

## Decisions
- **All video providers** get pages (YouTube, Vimeo, HTML5, custom) — no exclusions
- **Full HTML page cache** — render once, serve static file, invalidate on video add/update/delete. 24h TTL. File-based cache in `{module_dir}/cache/`
- **Video Gallery** included in initial scope — `/videos/` listing all videos with provider filter tabs and pagination
- **Opt-in** — all features disabled by default. Merchant must enable `PV_VIDEO_PAGES_ENABLE`
- **No new DB tables** — reuses `product_videos` + `product_videos_microdata`. Cache is file-based

## Architecture

### Reusable Components (already exist)
| Component | File | What to reuse |
|-----------|------|---------------|
| VideoObject JSON-LD generation | `classes/MicroDataForVideo.php` | `getMicroData()`, `printMicroData()` — full Schema.org with YouTube/Vimeo APIs |
| Thumbnail + embed URLs | `classes/ThumbNail.php` | `generateThumbnail()`, `generateEmbed()` |
| Video ID extraction | `productvideo.php:4778` | `getVideoData()` — YouTube/Vimeo/custom regex parsing |
| Microdata DB cache | `product_videos_microdata` table | `id_video` -> JSON, refreshed every 7 days |
| Front controller pattern | `controllers/front/html5microdata.php` | Class naming: `ProductVideo{Name}ModuleFrontController` |

### New Files (9)
1. `classes/PVVideoPage.php` — static data layer (video queries, product lists, URL generation, access checks)
2. `classes/PVPageCache.php` — file-based HTML cache (get/set/invalidate per video/lang/shop)
3. `classes/PVVideoSitemap.php` — XML sitemap generator (Google Video Sitemap 1.1 format)
4. `controllers/front/videopage.php` — video watch page controller
5. `controllers/front/videogallery.php` — gallery index controller
6. `controllers/front/videositemap.php` — sitemap XML controller
7. `views/templates/front/videopage.tpl` — video page template (PS 1.6 + 1.7+ compatible)
8. `views/templates/front/videogallery.tpl` — gallery template
9. `views/css/front-videopage.css` — shared front-end styles

### Modified Files (2)
1. `productvideo.php` — add `hookModuleRoutes()`, config keys, admin section, cache invalidation triggers
2. `upgrade/upgrade-3.4.0.php` — register hook, set defaults, create cache dir

### Config Keys
| Key | Type | Default | Purpose |
|-----|------|---------|---------|
| `PV_VIDEO_PAGES_ENABLE` | bool | 0 | Master toggle for video pages + gallery + routes |
| `PV_VIDEO_SITEMAP_ENABLE` | bool | 0 | Sitemap generation toggle |
| `PV_VIDEO_PAGE_LINK` | bool | 0 | Show "View video page" link on product page videos |
| `PV_VIDEO_GALLERY_PER_PAGE` | int | 24 | Gallery pagination count |

### Routes (via hookModuleRoutes, gated behind PV_VIDEO_PAGES_ENABLE)
```
video/{id_video}/{rewrite}  -> videopage controller
videos                       -> videogallery controller
sitemap-videos.xml           -> videositemap controller
```

### Video Page Layout
```
+--------------------------------------------------+
| Breadcrumb: Home > Videos > Video Title          |
+--------------------------------------------------+
| H1: Video Title                                  |
+--------------------------------------------------+
|                                                  |
|           [  VIDEO PLAYER - 16:9  ]              |
|           [  Full width, above fold ]            |
|                                                  |
+--------------------------------------------------+
| Description | Upload date | Duration | Provider  |
+--------------------------------------------------+
| H2: Products featuring this video                |
| +--------+  +--------+  +--------+  +--------+  |
| | Image  |  | Image  |  | Image  |  | Image  |  |
| | Name   |  | Name   |  | Name   |  | Name   |  |
| | Price  |  | Price  |  | Price  |  | Price  |  |
| | [View] |  | [View] |  | [View] |  | [View] |  |
| +--------+  +--------+  +--------+  +--------+  |
+--------------------------------------------------+
```

### Gallery Page Layout
```
+--------------------------------------------------+
| H1: Video Gallery                                |
| Filter: All | YouTube | Vimeo | HTML5 | Custom   |
+--------------------------------------------------+
| +----------+  +----------+  +----------+         |
| | Thumb    |  | Thumb    |  | Thumb    |         |
| | Title    |  | Title    |  | Title    |         |
| | Provider |  | Provider |  | Provider |         |
| +----------+  +----------+  +----------+         |
+--------------------------------------------------+
| << 1 2 3 ... >>                                  |
+--------------------------------------------------+
```

### Cache System
- Path: `{module_dir}/cache/videopage/{id_lang}/{id_shop}/{id_video}.html`
- Gallery: `{module_dir}/cache/videogallery/{id_lang}/{id_shop}/page-{n}.html`
- TTL: 24 hours
- Invalidation: on video add/update/delete (via existing hooks `actionProductUpdate`, `actionProductDelete`, and AJAX video endpoints)
- Admin button: "Clear Page Cache" in Video Pages section

### Speculative Items (need verification during implementation)
- [?] PS 1.6 `setTemplate()` path resolution (75/100 confidence)
- [?] YouTube video page ranking vs youtube.com canonical (60/100 confidence)
- [?] Vimeo thumbnail URL stability for sitemap (need to verify cached URLs don't expire)

## Implementation Sequence
1. Data layer class
2. Cache class
3. Routes + config keys
4. Video page controller + template + CSS
5. Test on PS8 Docker
6. Gallery controller + template
7. Sitemap generator + controller
8. Admin config section
9. Upgrade script
10. Cache invalidation in hooks
11. End-to-end testing
