1abe5218ca
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).
236 lines
12 KiB
PHP
236 lines
12 KiB
PHP
<?php
|
||
require_once __DIR__ . '/auth.php';
|
||
require_once __DIR__ . '/../includes/layout.php';
|
||
admin_require_auth();
|
||
|
||
$msg = '';
|
||
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$action = $_POST['action'] ?? 'settings';
|
||
|
||
if ($action === 'settings') {
|
||
$per_page = max(1, min(50, (int)($_POST['catalogue_per_page'] ?? 10)));
|
||
$site_title = trim($_POST['site_title'] ?? 'Ty Clifford');
|
||
$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('site_title', $site_title);
|
||
set_setting('site_sub', $site_sub);
|
||
set_setting('url_rewrite_mode', $url_mode);
|
||
$msg = 'Settings saved.';
|
||
}
|
||
|
||
if ($action === 'change_password') {
|
||
$current = $_POST['current_password'] ?? '';
|
||
$new1 = $_POST['new_password'] ?? '';
|
||
$new2 = $_POST['new_password2'] ?? '';
|
||
|
||
if (!password_verify($current, setting('admin_password'))) {
|
||
$msg = 'ERROR: Current password is incorrect.';
|
||
} elseif (strlen($new1) < 6) {
|
||
$msg = 'ERROR: New password must be at least 6 characters.';
|
||
} elseif ($new1 !== $new2) {
|
||
$msg = 'ERROR: New passwords do not match.';
|
||
} else {
|
||
set_setting('admin_password', password_hash($new1, PASSWORD_DEFAULT));
|
||
$msg = 'Password changed successfully.';
|
||
}
|
||
}
|
||
}
|
||
|
||
$per_page = setting('catalogue_per_page', '10');
|
||
$site_title = setting('site_title', 'Ty Clifford');
|
||
$site_sub = setting('site_sub', 'tyclifford.com / media');
|
||
$url_rewrite_mode = url_rewrite_mode();
|
||
|
||
// DB stats
|
||
$db = get_db();
|
||
$total_videos = $db->query("SELECT COUNT(*) FROM videos")->fetchColumn();
|
||
$total_sources = $db->query("SELECT COUNT(*) FROM video_sources")->fetchColumn();
|
||
$db_size = file_exists(DB_PATH) ? round(filesize(DB_PATH)/1024, 1) : 0;
|
||
$media_size = 0;
|
||
if (is_dir(MEDIA_DIR)) {
|
||
$iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(MEDIA_DIR, FilesystemIterator::SKIP_DOTS));
|
||
foreach ($iter as $f) $media_size += $f->getSize();
|
||
$media_size = round($media_size / 1048576, 1);
|
||
}
|
||
|
||
$msg_type = str_starts_with($msg, 'ERROR') ? 'error' : 'success';
|
||
|
||
render_head('Settings — Admin', '
|
||
.admin-layout { display:grid; grid-template-columns:1fr; gap:1.5rem; }
|
||
@media(min-width:900px) { .admin-layout { grid-template-columns:220px 1fr; } }
|
||
.admin-nav { background:var(--surface); border:1px solid var(--border); border-radius:calc(var(--r)+2px); padding:1.2rem; display:flex; flex-direction:column; gap:.4rem; align-self:start; position:sticky; top:1rem; }
|
||
.admin-nav-label { font-family:"Share Tech Mono",monospace; font-size:.58rem; letter-spacing:.18em; text-transform:uppercase; color:var(--text-3); margin-bottom:.3rem; padding-left:.5rem; }
|
||
.admin-nav a { display:flex; align-items:center; gap:.6rem; padding:.55rem .75rem; border-radius:var(--r); font-size:.82rem; color:var(--text-2); text-decoration:none; transition:background .15s,color .15s; }
|
||
.admin-nav a:hover { background:rgba(255,255,255,.05); color:var(--text); }
|
||
.admin-nav a.active { background:rgba(176,96,255,.12); color:var(--purple); }
|
||
.nav-divider { border:none; border-top:1px solid var(--border); margin:.4rem 0; }
|
||
.form-grid { display:grid; grid-template-columns:1fr; gap:1rem; }
|
||
@media(min-width:600px) { .form-grid { grid-template-columns:1fr 1fr; } }
|
||
.section-heading { font-family:"Share Tech Mono",monospace; font-size:.65rem; letter-spacing:.15em; text-transform:uppercase; color:var(--text-3); margin-bottom:.75rem; }
|
||
.hint { font-size:.72rem; color:var(--text-3); margin-top:.2rem; }
|
||
.stat-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(130px,1fr)); gap:.75rem; }
|
||
.stat-tile { background:var(--surface-2); border:1px solid var(--border); border-radius:var(--r); padding:1rem; display:flex; flex-direction:column; gap:.3rem; }
|
||
.stat-val { font-family:"Share Tech Mono",monospace; font-size:1.4rem; color:var(--text); font-weight:700; }
|
||
.stat-label{ font-family:"Share Tech Mono",monospace; font-size:.6rem; letter-spacing:.1em; text-transform:uppercase; color:var(--text-3); }
|
||
');
|
||
?>
|
||
<main class="page" role="main">
|
||
<header class="topbar">
|
||
<div class="topbar-brand">
|
||
<div>
|
||
<div class="brand-name">Ty Clifford</div>
|
||
<div class="brand-sub">admin / settings</div>
|
||
</div>
|
||
</div>
|
||
<nav class="topbar-social">
|
||
<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>
|
||
View Site
|
||
</a>
|
||
<a class="social-pill" href="logout.php">Logout</a>
|
||
</nav>
|
||
</header>
|
||
|
||
<?php if ($msg): ?>
|
||
<div class="notice notice-<?= $msg_type ?>"><?= h($msg) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<div class="admin-layout">
|
||
<aside class="admin-nav">
|
||
<div class="admin-nav-label">Navigation</div>
|
||
<a href="index.php">
|
||
<svg width="14" height="14" 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
|
||
</a>
|
||
<a href="add.php">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="16"/><line x1="8" y1="12" x2="16" y2="12"/></svg>
|
||
Add Video
|
||
</a>
|
||
<a href="youtube_import.php">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22.54 6.42a2.78 2.78 0 00-1.95-2C18.88 4 12 4 12 4s-6.88 0-8.59.46a2.78 2.78 0 00-1.95 2A29 29 0 001 12a29 29 0 00.46 5.58 2.78 2.78 0 001.95 2C5.12 20 12 20 12 20s6.88 0 8.59-.46a2.78 2.78 0 001.95-2A29 29 0 0023 12a29 29 0 00-.46-5.58z"/><polygon points="10,15 15,12 10,9"/></svg>
|
||
YouTube Importer
|
||
</a>
|
||
<hr class="nav-divider">
|
||
<a href="settings.php" class="active">
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93l-1.41 1.41M4.93 4.93l1.41 1.41M19.07 19.07l-1.41-1.41M4.93 19.07l1.41-1.41M12 2v2M12 20v2M2 12h2M20 12h2"/></svg>
|
||
Settings
|
||
</a>
|
||
</aside>
|
||
|
||
<div style="display:flex;flex-direction:column;gap:1.25rem;">
|
||
|
||
<!-- Stats -->
|
||
<div class="card">
|
||
<p class="section-heading">Overview</p>
|
||
<div class="stat-grid">
|
||
<div class="stat-tile">
|
||
<span class="stat-val"><?= $total_videos ?></span>
|
||
<span class="stat-label">Videos</span>
|
||
</div>
|
||
<div class="stat-tile">
|
||
<span class="stat-val"><?= $total_sources ?></span>
|
||
<span class="stat-label">Sources</span>
|
||
</div>
|
||
<div class="stat-tile">
|
||
<span class="stat-val"><?= $db_size ?> KB</span>
|
||
<span class="stat-label">Database</span>
|
||
</div>
|
||
<div class="stat-tile">
|
||
<span class="stat-val"><?= $media_size ?> MB</span>
|
||
<span class="stat-label">Media Storage</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- General settings -->
|
||
<div class="card">
|
||
<p class="section-heading">General Settings</p>
|
||
<form method="post" action="settings.php">
|
||
<input type="hidden" name="action" value="settings">
|
||
<div class="form-grid" style="margin-bottom:1rem;">
|
||
<div class="form-group">
|
||
<label class="form-label" for="site_title">Site Title</label>
|
||
<input class="form-input" type="text" id="site_title" name="site_title"
|
||
value="<?= h($site_title) ?>">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label" for="site_sub">Site Subtitle</label>
|
||
<input class="form-input" type="text" id="site_sub" name="site_sub"
|
||
value="<?= h($site_sub) ?>">
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label" for="catalogue_per_page">Videos per page</label>
|
||
<input class="form-input" type="number" id="catalogue_per_page"
|
||
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>
|
||
</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>
|
||
<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>
|
||
Save Settings
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Change password -->
|
||
<div class="card">
|
||
<p class="section-heading">Change Password</p>
|
||
<form method="post" action="settings.php">
|
||
<input type="hidden" name="action" value="change_password">
|
||
<div class="form-grid" style="margin-bottom:1rem;">
|
||
<div class="form-group">
|
||
<label class="form-label" for="current_password">Current Password</label>
|
||
<input class="form-input" type="password" id="current_password" name="current_password"
|
||
autocomplete="current-password">
|
||
</div>
|
||
<div></div>
|
||
<div class="form-group">
|
||
<label class="form-label" for="new_password">New Password</label>
|
||
<input class="form-input" type="password" id="new_password" name="new_password"
|
||
autocomplete="new-password" minlength="6">
|
||
<span class="hint">Minimum 6 characters.</span>
|
||
</div>
|
||
<div class="form-group">
|
||
<label class="form-label" for="new_password2">Confirm New Password</label>
|
||
<input class="form-input" type="password" id="new_password2" name="new_password2"
|
||
autocomplete="new-password">
|
||
</div>
|
||
</div>
|
||
<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"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg>
|
||
Change Password
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- File locations reference -->
|
||
<div class="card">
|
||
<p class="section-heading">File Locations</p>
|
||
<div style="display:flex;flex-direction:column;gap:.5rem;font-family:'Share Tech Mono',monospace;font-size:.7rem;color:var(--text-3);">
|
||
<div><span style="color:var(--text-2);">Database:</span> data/media.db</div>
|
||
<div><span style="color:var(--text-2);">Videos:</span> media/videos/</div>
|
||
<div><span style="color:var(--text-2);">Thumbnails:</span> media/thumbs/</div>
|
||
<div><span style="color:var(--text-2);">Default password:</span> <span style="color:var(--red);">admin</span> — change immediately!</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<?php render_footer(); ?>
|
||
</main>
|
||
</body>
|
||
</html>
|