diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-30 12:45:18 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-30 12:45:18 +0300 |
| commit | 54bc9013aa0463962a1cfd67fa278de3d401d85a (patch) | |
| tree | 526c719285a6153fdc82f8a7feb7b4ef98c1ce78 /web/share.html | |
| parent | 38e318e71d50e4340ddbf1ae63b3d85e958b4644 (diff) | |
ja: implement public share landing page and improve error mapping
- Add web/share.html with a minimal HTML5 video player, theme variables,
centered layout, play overlay, and Back to Home link.
- Update handleSharePage to return HTML for browser Accept headers
(text/html or empty) and JSON for application/json. Return 410 Gone
for expired shares.
- Update handleShareStream to map service errors to distinct HTTP codes:
ErrShareExpired -> 410, ErrShareNotFound/ErrMediaNotFound -> 404.
- Improve ValidateShareToken and StreamSharedMedia to return sentinel
errors (ErrShareNotFound, ErrShareExpired, ErrMediaNotFound) instead
of generic string errors.
- Update and add tests for share page HTML/JSON negotiation and share
stream 404/410 responses.
Diffstat (limited to 'web/share.html')
| -rw-r--r-- | web/share.html | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/web/share.html b/web/share.html new file mode 100644 index 0000000..065e839 --- /dev/null +++ b/web/share.html @@ -0,0 +1,105 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>Shared Media</title> +<link rel="stylesheet" href="/css/theme.css"> +<style> + body { + margin: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + background: var(--bg-body); + color: var(--text-primary); + font-family: var(--font-sans); + padding: 1rem; + box-sizing: border-box; + } + .container { + width: 100%; + max-width: 720px; + text-align: center; + } + h1 { + margin: 0 0 1rem; + font-size: 1.25rem; + color: var(--text-primary); + } + .player-wrapper { + position: relative; + background: var(--player-bg); + border-radius: var(--radius-md); + overflow: hidden; + box-shadow: var(--shadow-md); + } + video, audio { + width: 100%; + display: block; + } + .play-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + background: var(--bg-overlay); + cursor: pointer; + transition: opacity var(--transition-base); + } + .play-overlay.hidden { + opacity: 0; + pointer-events: none; + } + .play-icon { + font-size: 3rem; + color: var(--text-primary); + background: var(--accent-soft); + border-radius: 50%; + width: 4rem; + height: 4rem; + display: flex; + align-items: center; + justify-content: center; + line-height: 1; + } + .back-link { + margin-top: 1.5rem; + color: var(--accent); + text-decoration: none; + font-size: 0.9rem; + } + .back-link:hover { + color: var(--accent-hover); + } +</style> +</head> +<body> +<div class="container"> + <h1>Shared Media</h1> + <div class="player-wrapper"> + <video id="player" playsinline preload="metadata" src="./stream"></video> + <div id="overlay" class="play-overlay" role="button" aria-label="Play"> + <div class="play-icon">▶</div> + </div> + </div> + <a class="back-link" href="/">Back to Home</a> +</div> +<script> + const player = document.getElementById('player'); + const overlay = document.getElementById('overlay'); + overlay.addEventListener('click', () => { + player.play(); + }); + player.addEventListener('play', () => { + overlay.classList.add('hidden'); + }); + player.addEventListener('pause', () => { + overlay.classList.remove('hidden'); + }); +</script> +</body> +</html> |
