- 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:
@@ -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>
|
||||||
@@ -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** (1–50).
|
Configurable in **Admin → Settings → Videos per page** (1–50).
|
||||||
|
|||||||
+1
-1
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 (1–50).</span>
|
<span class="hint">How many thumbnails to show per page (1–50).</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/<slug>, /all, and /search/<query>. 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>
|
||||||
|
|||||||
@@ -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
@@ -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; ?>
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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>
|
||||||
|
|||||||
@@ -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; ?>
|
||||||
|
|||||||
Reference in New Issue
Block a user