75d38d4eb8
CMS changes landed in [layout.php](/Users/tyemeclifford/Documents/GH/blog/themes/neon/layout.php), [style.css](/Users/tyemeclifford/Documents/GH/blog/themes/neon/assets/style.css), [app.js](/Users/tyemeclifford/Documents/GH/blog/themes/neon/assets/app.js), and [lone-embed.php](/Users/tyemeclifford/Documents/GH/blog/lone-embed.php): skip link, named search/forms, current page state, live notices/empty states, labelled media/embed controls, visible focus states, theme control pressed states, and a focus-trapped/lightbox dialog with proper ARIA. Mac app changes landed in [main.m](/Users/tyemeclifford/Documents/GH/blog/macclient/AppKitClient/main.m), and I rebuilt [TCMS](/Users/tyemeclifford/Documents/GH/blog/macclient/TCMS.app/Contents/MacOS/TCMS): AppKit controls/tables/fields/previews now get accessibility labels/help/value text, workspace sections are named, media/comment tables expose useful context, and status messages are announced via macOS accessibility notifications.
79 lines
2.2 KiB
PHP
79 lines
2.2 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
$file = media_url((string) ($_GET['file'] ?? ''));
|
|
$poster = media_url((string) ($_GET['poster'] ?? ''));
|
|
$title = trim((string) ($_GET['title'] ?? 'Embedded video'));
|
|
|
|
if ($file === '') {
|
|
http_response_code(400);
|
|
echo 'Missing video file.';
|
|
exit;
|
|
}
|
|
|
|
function media_url(string $value): string
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '' || preg_match('/[\x00-\x1F\x7F]/', $value) || str_contains($value, '..') || preg_match('/^\s*(javascript|data):/i', $value)) {
|
|
return '';
|
|
}
|
|
if (preg_match('#^https?://#i', $value) || str_starts_with($value, '/')) {
|
|
return $value;
|
|
}
|
|
|
|
return str_contains($value, '/') ? $value : 'bl-content/uploads/' . $value;
|
|
}
|
|
|
|
function media_type(string $value): string
|
|
{
|
|
$extension = strtolower(pathinfo((string) parse_url($value, PHP_URL_PATH), PATHINFO_EXTENSION));
|
|
return match ($extension) {
|
|
'm4v', 'mp4' => 'video/mp4',
|
|
'ogv', 'ogg' => 'video/ogg',
|
|
'webm' => 'video/webm',
|
|
default => 'video/mp4',
|
|
};
|
|
}
|
|
|
|
function e(string $value): string
|
|
{
|
|
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
}
|
|
?><!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title><?= e($title !== '' ? $title : 'Embedded video') ?></title>
|
|
<style>
|
|
html,
|
|
body {
|
|
width: 100%;
|
|
height: 100%;
|
|
margin: 0;
|
|
background: #05070b;
|
|
color: #e8fff4;
|
|
}
|
|
|
|
body {
|
|
display: grid;
|
|
place-items: center;
|
|
}
|
|
|
|
video {
|
|
width: 100%;
|
|
height: 100%;
|
|
max-height: 100vh;
|
|
background: #000;
|
|
object-fit: contain;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<video controls playsinline preload="metadata" aria-label="<?= e($title !== '' ? $title : 'Embedded video') ?>"<?php if ($poster !== ''): ?> poster="<?= e($poster) ?>"<?php endif; ?>>
|
|
<source src="<?= e($file) ?>" type="<?= e(media_type($file)) ?>">
|
|
Your browser does not support the video element.
|
|
</video>
|
|
</body>
|
|
</html>
|