- Implemented configurable public URL rewriting.

What changed:
Added pretty URL helpers/default setting in 
[includes/db.php](/Users/tyemeclifford/Documents/GH/mediaplayer/includes/db.php).
Added root rewrite rules in 
[.htaccess](/Users/tyemeclifford/Documents/GH/mediaplayer/.htaccess):/v/<slug>
/all
/search/<query>

Added Admin → Settings “Public URL Style” toggle in 
[admin/settings.php](/Users/tyemeclifford/Documents/GH/mediaplayer/admin/settings.php), 
defaulting to pretty URLs.
Updated public/player/catalogue/embed/admin preview links to use the 
configured URL style.
Added redirects from legacy ?v= and ?q= URLs to pretty routes when 
rewriting is enabled.
Documented deployment and fallback behavior in 
[README.md](/Users/tyemeclifford/Documents/GH/mediaplayer/README.md).
This commit is contained in:
Ty Clifford
2026-06-24 13:16:55 -04:00
parent 0f262cab3f
commit 1abe5218ca
13 changed files with 187 additions and 30 deletions
+10
View File
@@ -0,0 +1,10 @@
DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^v/([^/]+)/?$ index.php?v=$1 [B,L,QSA]
RewriteRule ^all/?$ catalogue.php [L,QSA]
RewriteRule ^search/?$ catalogue.php [L,QSA]
RewriteRule ^search/(.+)/?$ catalogue.php?q=$1 [B,L,QSA]
</IfModule>
+13 -1
View File
@@ -8,13 +8,14 @@ Styled to match the existing `gate.php` / `index.php` dark theme exactly.
## Requirements ## Requirements
- PHP 8.0+ with `pdo_sqlite`, `fileinfo` extensions - PHP 8.0+ with `pdo_sqlite`, `fileinfo` extensions
- Apache `mod_rewrite` for default pretty URLs, or switch to query URLs in Admin → Settings
- Write permissions on the `data/` and `media/` directories - Write permissions on the `data/` and `media/` directories
--- ---
## Installation ## Installation
1. **Upload** all files to your web server (e.g. `/live/` or wherever `/index.php` lives). 1. **Upload** all files to your web server. To use domain-level pretty URLs such as `/v/my-video`, deploy the app at the web root.
2. **Set permissions** so PHP can write to these directories: 2. **Set permissions** so PHP can write to these directories:
```bash ```bash
@@ -36,6 +37,7 @@ Styled to match the existing `gate.php` / `index.php` dark theme exactly.
/ ← web root (deploy here) / ← web root (deploy here)
├── index.php ← Main player + catalogue (home page) ├── index.php ← Main player + catalogue (home page)
├── catalogue.php ← Full video catalogue browse page ├── catalogue.php ← Full video catalogue browse page
├── .htaccess ← Pretty URL rewrite rules
├── includes/ ├── includes/
│ ├── db.php ← SQLite bootstrap + helper functions │ ├── db.php ← SQLite bootstrap + helper functions
│ └── layout.php ← Shared HTML head, topbar, footer, pagination │ └── layout.php ← Shared HTML head, topbar, footer, pagination
@@ -84,6 +86,16 @@ Use **Admin → YouTube Importer** to paste a YouTube channel URL, `@handle`, or
The full catalogue page includes a public search for published videos only. Search matches video title, description, and slug; draft videos remain hidden. The full catalogue page includes a public search for published videos only. Search matches video title, description, and slug; draft videos remain hidden.
### Public URLs
Pretty URLs are enabled by default:
- `/v/<slug>` plays a video
- `/all` lists all public videos
- `/search/<query>` searches public videos
You can switch to PHP query URLs in **Admin → Settings → Public URL Style** if your host does not support `.htaccess` rewrites.
### Catalogue Per-Page ### Catalogue Per-Page
Configurable in **Admin → Settings → Videos per page** (150). Configurable in **Admin → Settings → Videos per page** (150).
+1 -1
View File
@@ -170,7 +170,7 @@ render_head('Add Video — Admin', '
</div> </div>
</div> </div>
<nav class="topbar-social"> <nav class="topbar-social">
<a class="social-pill" href="../index.php" target="_blank"> <a class="social-pill" href="<?= h(public_absolute_url(public_home_url())) ?>" target="_blank">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
View Site View Site
</a> </a>
+1 -1
View File
@@ -238,7 +238,7 @@ render_head('Edit Video — Admin', '
</div> </div>
</div> </div>
<nav class="topbar-social"> <nav class="topbar-social">
<a class="social-pill" href="../index.php?v=<?= urlencode($video['slug']) ?>" target="_blank"> <a class="social-pill" href="<?= h(public_absolute_url(public_video_url($video['slug']))) ?>" target="_blank">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
Preview Preview
</a> </a>
+1 -1
View File
@@ -141,7 +141,7 @@ render_head('Admin — TyClifford.com', '
</div> </div>
</div> </div>
<nav class="topbar-social"> <nav class="topbar-social">
<a class="social-pill" href="../index.php" target="_blank"> <a class="social-pill" href="<?= h(public_absolute_url(public_home_url())) ?>" target="_blank">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
View Site View Site
</a> </a>
+1 -1
View File
@@ -47,7 +47,7 @@ render_head('Admin Login — TyClifford.com', '
<div class="brand-sub">admin dashboard</div> <div class="brand-sub">admin dashboard</div>
</div> </div>
</div> </div>
<a href="../index.php" class="btn btn-secondary btn-sm"> Back to site</a> <a href="<?= h(public_absolute_url(public_home_url())) ?>" class="btn btn-secondary btn-sm"> Back to site</a>
</header> </header>
<div class="login-wrap"> <div class="login-wrap">
+13 -1
View File
@@ -12,10 +12,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$per_page = max(1, min(50, (int)($_POST['catalogue_per_page'] ?? 10))); $per_page = max(1, min(50, (int)($_POST['catalogue_per_page'] ?? 10)));
$site_title = trim($_POST['site_title'] ?? 'Ty Clifford'); $site_title = trim($_POST['site_title'] ?? 'Ty Clifford');
$site_sub = trim($_POST['site_sub'] ?? 'tyclifford.com / media'); $site_sub = trim($_POST['site_sub'] ?? 'tyclifford.com / media');
$url_mode = $_POST['url_rewrite_mode'] ?? 'pretty';
if (!in_array($url_mode, ['pretty', 'query'], true)) $url_mode = 'pretty';
set_setting('catalogue_per_page', (string)$per_page); set_setting('catalogue_per_page', (string)$per_page);
set_setting('site_title', $site_title); set_setting('site_title', $site_title);
set_setting('site_sub', $site_sub); set_setting('site_sub', $site_sub);
set_setting('url_rewrite_mode', $url_mode);
$msg = 'Settings saved.'; $msg = 'Settings saved.';
} }
@@ -40,6 +43,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$per_page = setting('catalogue_per_page', '10'); $per_page = setting('catalogue_per_page', '10');
$site_title = setting('site_title', 'Ty Clifford'); $site_title = setting('site_title', 'Ty Clifford');
$site_sub = setting('site_sub', 'tyclifford.com / media'); $site_sub = setting('site_sub', 'tyclifford.com / media');
$url_rewrite_mode = url_rewrite_mode();
// DB stats // DB stats
$db = get_db(); $db = get_db();
@@ -83,7 +87,7 @@ render_head('Settings — Admin', '
</div> </div>
</div> </div>
<nav class="topbar-social"> <nav class="topbar-social">
<a class="social-pill" href="../index.php" target="_blank"> <a class="social-pill" href="<?= h(public_absolute_url(public_home_url())) ?>" target="_blank">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15,3 21,3 21,9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
View Site View Site
</a> </a>
@@ -164,6 +168,14 @@ render_head('Settings — Admin', '
name="catalogue_per_page" value="<?= h($per_page) ?>" min="1" max="50"> name="catalogue_per_page" value="<?= h($per_page) ?>" min="1" max="50">
<span class="hint">How many thumbnails to show per page (150).</span> <span class="hint">How many thumbnails to show per page (150).</span>
</div> </div>
<div class="form-group">
<label class="form-label" for="url_rewrite_mode">Public URL Style</label>
<select class="form-select" id="url_rewrite_mode" name="url_rewrite_mode">
<option value="pretty" <?= $url_rewrite_mode === 'pretty' ? 'selected' : '' ?>>Pretty rewrite URLs (/v/sample)</option>
<option value="query" <?= $url_rewrite_mode === 'query' ? 'selected' : '' ?>>PHP query URLs (index.php?v=sample)</option>
</select>
<span class="hint">Default routes are /v/&lt;slug&gt;, /all, and /search/&lt;query&gt;. Pretty URLs require Apache mod_rewrite and the included .htaccess.</span>
</div>
</div> </div>
<button class="btn btn-primary btn-sm" type="submit"> <button class="btn btn-primary btn-sm" type="submit">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><polyline points="17,21 17,13 7,13 7,21"/><polyline points="7,3 7,8 15,8"/></svg> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 01-2-2V5a2 2 0 012-2h11l5 5v11a2 2 0 01-2 2z"/><polyline points="17,21 17,13 7,13 7,21"/><polyline points="7,3 7,8 15,8"/></svg>
+1 -1
View File
@@ -587,7 +587,7 @@ render_head('YouTube Importer — Admin', '
</div> </div>
</div> </div>
<nav class="topbar-social"> <nav class="topbar-social">
<a class="social-pill" href="../index.php" target="_blank">View Site</a> <a class="social-pill" href="<?= h(public_absolute_url(public_home_url())) ?>" target="_blank">View Site</a>
<a class="social-pill" href="logout.php">Logout</a> <a class="social-pill" href="logout.php">Logout</a>
</nav> </nav>
</header> </header>
+24 -5
View File
@@ -2,9 +2,28 @@
require_once __DIR__ . '/includes/db.php'; require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/layout.php'; require_once __DIR__ . '/includes/layout.php';
$route_segments = public_route_segments();
$search = public_search_query_from_request();
if (url_rewrite_enabled()) {
$route_head = $route_segments[0] ?? '';
$target = null;
if (isset($_GET['q']) && $route_head !== 'search') {
$target = public_search_url($search);
} elseif (!isset($_GET['q']) && !in_array($route_head, ['all', 'search'], true)) {
$target = public_catalogue_url();
}
if ($target !== null) {
$redirect_page = max(1, (int)($_GET['page'] ?? 1));
if ($redirect_page > 1) $target = media_append_query($target, ['page' => (string)$redirect_page]);
header('Location: ' . $target, true, 302);
exit;
}
}
$per_page = (int)setting('catalogue_per_page', '10'); $per_page = (int)setting('catalogue_per_page', '10');
$page = max(1, (int)($_GET['page'] ?? 1)); $page = max(1, (int)($_GET['page'] ?? 1));
$search = trim($_GET['q'] ?? '');
$paged = get_videos_paginated($page, $per_page, true, $search); $paged = get_videos_paginated($page, $per_page, true, $search);
render_head('Videos — TyClifford.com', ' render_head('Videos — TyClifford.com', '
@@ -76,14 +95,14 @@ render_head('Videos — TyClifford.com', '
<?= $paged['total'] ?> <?= $search ? 'result' : 'video' ?><?= $paged['total'] != 1 ? 's' : '' ?> <?= $paged['total'] ?> <?= $search ? 'result' : 'video' ?><?= $paged['total'] != 1 ? 's' : '' ?>
</span> </span>
</div> </div>
<form class="public-search" method="get" action="catalogue.php" role="search"> <form class="public-search" method="get" action="<?= h(public_catalogue_url()) ?>" role="search">
<input class="form-input" type="search" name="q" value="<?= h($search) ?>" placeholder="Search public videos…"> <input class="form-input" type="search" name="q" value="<?= h($search) ?>" placeholder="Search public videos…">
<button class="btn btn-secondary btn-sm" type="submit"> <button class="btn btn-secondary btn-sm" type="submit">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
Search Search
</button> </button>
<?php if ($search): ?> <?php if ($search): ?>
<a href="catalogue.php" class="btn btn-secondary btn-sm">Clear</a> <a href="<?= h(public_catalogue_url()) ?>" class="btn btn-secondary btn-sm">Clear</a>
<?php endif; ?> <?php endif; ?>
</form> </form>
</div> </div>
@@ -105,7 +124,7 @@ render_head('Videos — TyClifford.com', '
$vdur = (int)$v['duration']; $vdur = (int)$v['duration'];
$created = substr($v['created_at'], 0, 10); $created = substr($v['created_at'], 0, 10);
?> ?>
<a class="cat-thumb" href="https://tyclifford.com/player/?v=<?= urlencode($vslug) ?>" title="<?= h($vtitle) ?>"> <a class="cat-thumb" href="<?= h(public_video_url($vslug)) ?>" title="<?= h($vtitle) ?>">
<div class="cat-thumb-img"> <div class="cat-thumb-img">
<?php if ($vthumb): ?> <?php if ($vthumb): ?>
<img src="<?= h(MEDIA_URL . $vthumb) ?>" alt="<?= h($vtitle) ?>" loading="lazy"> <img src="<?= h(MEDIA_URL . $vthumb) ?>" alt="<?= h($vtitle) ?>" loading="lazy">
@@ -133,7 +152,7 @@ render_head('Videos — TyClifford.com', '
<?php if ($paged['total_pages'] > 1): ?> <?php if ($paged['total_pages'] > 1): ?>
<div class="cat-footer"> <div class="cat-footer">
<span class="cat-count">page <?= $paged['page'] ?> of <?= $paged['total_pages'] ?></span> <span class="cat-count">page <?= $paged['page'] ?> of <?= $paged['total_pages'] ?></span>
<?php render_pagination($paged['page'], $paged['total_pages'], 'catalogue.php?' . ($search ? 'q=' . urlencode($search) . '&' : '') . 'page=%d'); ?> <?php render_pagination($paged['page'], $paged['total_pages'], public_catalogue_page_url_pattern($search)); ?>
</div> </div>
<?php endif; ?> <?php endif; ?>
<?php endif; ?> <?php endif; ?>
+1 -1
View File
@@ -200,7 +200,7 @@ header('Content-Security-Policy: frame-ancestors *');
<!-- Title + brand overlay --> <!-- Title + brand overlay -->
<div class="title-overlay" id="title-overlay"> <div class="title-overlay" id="title-overlay">
<span class="title-text"><?= htmlspecialchars($video['title'], ENT_QUOTES) ?></span> <span class="title-text"><?= htmlspecialchars($video['title'], ENT_QUOTES) ?></span>
<a class="brand-link" href="<?= $base_url ?>index.php?v=<?= urlencode($video['slug']) ?>" <a class="brand-link" href="<?= h(public_absolute_url(public_video_url($video['slug']))) ?>"
target="_blank" rel="noopener">tyclifford.com</a> target="_blank" rel="noopener">tyclifford.com</a>
</div> </div>
+104
View File
@@ -67,6 +67,7 @@ function get_db(): PDO {
'admin_password' => password_hash('admin', PASSWORD_DEFAULT), 'admin_password' => password_hash('admin', PASSWORD_DEFAULT),
'site_title' => 'Ty Clifford', 'site_title' => 'Ty Clifford',
'site_sub' => 'tyclifford.com / media', 'site_sub' => 'tyclifford.com / media',
'url_rewrite_mode' => 'pretty',
]; ];
$ins = $pdo->prepare("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)"); $ins = $pdo->prepare("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)");
foreach ($defaults as $k => $v) $ins->execute([$k, $v]); foreach ($defaults as $k => $v) $ins->execute([$k, $v]);
@@ -100,6 +101,109 @@ function set_setting(string $key, string $value): void {
->execute([$key, $value]); ->execute([$key, $value]);
} }
function url_rewrite_mode(): string {
$mode = strtolower(setting('url_rewrite_mode', 'pretty'));
return in_array($mode, ['pretty', 'query'], true) ? $mode : 'pretty';
}
function url_rewrite_enabled(): bool {
return url_rewrite_mode() === 'pretty';
}
function public_base_path(): string {
$script = str_replace('\\', '/', $_SERVER['SCRIPT_NAME'] ?? '');
$dir = rtrim(dirname($script), '/');
if ($dir === '/' || $dir === '.') $dir = '';
if (str_ends_with($dir, '/admin')) $dir = substr($dir, 0, -6);
return $dir;
}
function public_url_path(string $path): string {
$base = public_base_path();
$path = '/' . ltrim($path, '/');
if ($path === '/') return $base === '' ? '/' : $base . '/';
return $base . $path;
}
function public_home_url(): string {
return url_rewrite_enabled() ? public_url_path('/') : public_url_path('/index.php');
}
function public_catalogue_url(): string {
return url_rewrite_enabled() ? public_url_path('/all') : public_url_path('/catalogue.php');
}
function public_video_url(string $slug): string {
$slug = trim($slug);
if (url_rewrite_enabled()) return public_url_path('/v/' . rawurlencode($slug));
return public_url_path('/index.php') . '?v=' . rawurlencode($slug);
}
function public_search_url(string $query): string {
$query = trim($query);
if ($query === '') return public_catalogue_url();
if (url_rewrite_enabled()) return public_url_path('/search/' . rawurlencode($query));
return public_url_path('/catalogue.php') . '?q=' . rawurlencode($query);
}
function public_page_url_pattern(string $base_url): string {
return $base_url . (strpos($base_url, '?') === false ? '?' : '&') . 'page=%d';
}
function public_catalogue_page_url_pattern(string $search = ''): string {
return public_page_url_pattern($search !== '' ? public_search_url($search) : public_catalogue_url());
}
function public_player_page_url_pattern(string $slug = ''): string {
$base = $slug !== '' ? public_video_url($slug) : public_home_url();
return public_page_url_pattern($base);
}
function public_request_path(): string {
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);
return is_string($path) && $path !== '' ? $path : '/';
}
function public_route_segments(): array {
$path = public_request_path();
$base = public_base_path();
if ($base !== '' && ($path === $base || str_starts_with($path, $base . '/'))) {
$path = substr($path, strlen($base));
}
$segments = array_values(array_filter(explode('/', trim($path, '/')), 'strlen'));
return array_map('rawurldecode', $segments);
}
function public_video_slug_from_request(): string {
$slug = trim((string)($_GET['v'] ?? ''));
if ($slug !== '') return $slug;
$segments = public_route_segments();
return (($segments[0] ?? '') === 'v' && isset($segments[1])) ? trim($segments[1]) : '';
}
function public_search_query_from_request(): string {
if (isset($_GET['q'])) return trim((string)$_GET['q']);
$segments = public_route_segments();
return (($segments[0] ?? '') === 'search' && isset($segments[1])) ? trim($segments[1]) : '';
}
function public_request_scheme(): string {
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
}
function public_origin(): string {
return public_request_scheme() . '://' . ($_SERVER['HTTP_HOST'] ?? 'localhost');
}
function public_absolute_url(string $url): string {
if (preg_match('#^[a-z][a-z0-9+.-]*://#i', $url)) return $url;
if (str_starts_with($url, '//')) return public_request_scheme() . ':' . $url;
if (str_starts_with($url, '/')) return public_origin() . $url;
return public_origin() . public_url_path($url);
}
function make_slug(string $title, int $id = 0): string { function make_slug(string $title, int $id = 0): string {
$slug = strtolower(trim(preg_replace('/[^a-z0-9]+/i', '-', $title), '-')); $slug = strtolower(trim(preg_replace('/[^a-z0-9]+/i', '-', $title), '-'));
if ($id) { if ($id) {
+3 -3
View File
@@ -224,16 +224,16 @@ function render_topbar(string $active = ''): void {
<header class="topbar"> <header class="topbar">
<div class="topbar-brand"> <div class="topbar-brand">
<div> <div>
<a href="index.php" class="brand-name"><?= h($title) ?></a> <a href="<?= h(public_home_url()) ?>" class="brand-name"><?= h($title) ?></a>
<div class="brand-sub"><?= h($sub) ?></div> <div class="brand-sub"><?= h($sub) ?></div>
</div> </div>
</div> </div>
<nav class="topbar-social" aria-label="Navigation"> <nav class="topbar-social" aria-label="Navigation">
<a class="social-pill <?= $active==='home' ? 'active' : '' ?>" href="index.php"> <a class="social-pill <?= $active==='home' ? 'active' : '' ?>" href="<?= h(public_home_url()) ?>">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9,22 9,12 15,12 15,22"/></svg>
Home Home
</a> </a>
<a class="social-pill <?= $active==='catalogue' ? 'active' : '' ?>" href="catalogue.php"> <a class="social-pill <?= $active==='catalogue' ? 'active' : '' ?>" href="<?= h(public_catalogue_url()) ?>">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>
Videos Videos
</a> </a>
+14 -14
View File
@@ -3,7 +3,14 @@ require_once __DIR__ . '/includes/db.php';
require_once __DIR__ . '/includes/layout.php'; require_once __DIR__ . '/includes/layout.php';
// Determine which video to play // Determine which video to play
$slug = $_GET['v'] ?? ''; $slug = public_video_slug_from_request();
if (url_rewrite_enabled() && $slug !== '' && isset($_GET['v']) && ((public_route_segments()[0] ?? '') !== 'v')) {
$target = public_video_url($slug);
$redirect_page = max(1, (int)($_GET['page'] ?? 1));
if ($redirect_page > 1) $target = media_append_query($target, ['page' => (string)$redirect_page]);
header('Location: ' . $target, true, 302);
exit;
}
$video = $slug ? get_video_by_slug($slug) : null; $video = $slug ? get_video_by_slug($slug) : null;
// Latest video as default if none specified // Latest video as default if none specified
@@ -30,12 +37,6 @@ foreach ($player_sources as $player_source) {
if (($player_source['kind'] ?? '') !== 'iframe') $native_player_sources[] = $player_source; if (($player_source['kind'] ?? '') !== 'iframe') $native_player_sources[] = $player_source;
} }
// Build absolute base URL used for embed snippet generation
$_scheme = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$_host = $_SERVER['HTTP_HOST'] ?? 'localhost';
$_script = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/');
$_base_url = $_scheme . '://' . $_host . $_script . '/';
render_head('Media — TyClifford.com', ' render_head('Media — TyClifford.com', '
/* ── Catalogue grid ── */ /* ── Catalogue grid ── */
.cat-section { display:flex; flex-direction:column; gap:1rem; } .cat-section { display:flex; flex-direction:column; gap:1rem; }
@@ -279,7 +280,7 @@ render_head('Media — TyClifford.com', '
</div> </div>
<?php if ($video): <?php if ($video):
$embed_url = $_base_url . 'embed.php?v=' . urlencode($video['slug']); $embed_url = public_absolute_url('embed.php?v=' . rawurlencode($video['slug']));
$iframe_code = '<iframe src="' . $embed_url . '" width="560" height="315" frameborder="0" allowfullscreen allow="fullscreen"></iframe>'; $iframe_code = '<iframe src="' . $embed_url . '" width="560" height="315" frameborder="0" allowfullscreen allow="fullscreen"></iframe>';
?> ?>
<div class="embed-panel" id="embed-panel" role="region" aria-label="Embed code"> <div class="embed-panel" id="embed-panel" role="region" aria-label="Embed code">
@@ -314,14 +315,14 @@ render_head('Media — TyClifford.com', '
<section class="cat-section"> <section class="cat-section">
<div class="cat-header"> <div class="cat-header">
<p class="eyebrow eyebrow-purple">Video Catalogue</p> <p class="eyebrow eyebrow-purple">Video Catalogue</p>
<form class="cat-search" method="get" action="catalogue.php" role="search"> <form class="cat-search" method="get" action="<?= h(public_catalogue_url()) ?>" role="search">
<input class="form-input" type="search" name="q" placeholder="Search public videos…"> <input class="form-input" type="search" name="q" placeholder="Search public videos…">
<button class="btn btn-secondary btn-sm" type="submit"> <button class="btn btn-secondary btn-sm" type="submit">
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
Search Search
</button> </button>
</form> </form>
<a href="catalogue.php" class="btn btn-secondary btn-sm">Browse all </a> <a href="<?= h(public_catalogue_url()) ?>" class="btn btn-secondary btn-sm">Browse all </a>
</div> </div>
<?php if (empty($paged['videos'])): ?> <?php if (empty($paged['videos'])): ?>
@@ -340,9 +341,9 @@ render_head('Media — TyClifford.com', '
$vthumb = $v['thumbnail']; $vthumb = $v['thumbnail'];
$vdur = (int)$v['duration']; $vdur = (int)$v['duration'];
$created = substr($v['created_at'], 0, 10); $created = substr($v['created_at'], 0, 10);
?> ?>
<a class="cat-thumb <?= $active ? 'active' : '' ?>" <a class="cat-thumb <?= $active ? 'active' : '' ?>"
href="?v=<?= urlencode($vslug) ?>" href="<?= h(public_video_url($vslug)) ?>"
title="<?= h($vtitle) ?>"> title="<?= h($vtitle) ?>">
<div class="cat-active-indicator"></div> <div class="cat-active-indicator"></div>
<div class="cat-thumb-img"> <div class="cat-thumb-img">
@@ -373,8 +374,7 @@ render_head('Media — TyClifford.com', '
· page <?= $paged['page'] ?> of <?= $paged['total_pages'] ?> · page <?= $paged['page'] ?> of <?= $paged['total_pages'] ?>
</span> </span>
<?php <?php
$url_v = $video ? '&v=' . urlencode($video['slug']) : ''; render_pagination($paged['page'], $paged['total_pages'], public_player_page_url_pattern($video['slug'] ?? ''));
render_pagination($paged['page'], $paged['total_pages'], '?page=%d' . $url_v);
?> ?>
</div> </div>
<?php endif; ?> <?php endif; ?>