merge: video embed on detail page (Worker R2)
CI / Lint / Test / Vet (push) Successful in 13s
CI / Build & Import (push) Successful in 11s
CI / Mirror to GitHub (push) Failing after 2s

# Conflicts:
#	internal/web/views_templ.go
This commit is contained in:
2026-06-03 15:38:31 +02:00
5 changed files with 192 additions and 98 deletions
+18
View File
@@ -1,6 +1,7 @@
package web
import (
"regexp"
"strings"
"time"
@@ -9,6 +10,21 @@ import (
"gitea.d-ma.be/mathias/tapir/internal/adapters/store"
)
// youtubeIDRe matches a canonical 11-char YouTube video id (the provider's
// base64url alphabet). Anything else is rejected so we never emit a broken
// embed src.
var youtubeIDRe = regexp.MustCompile(`^[A-Za-z0-9_-]{11}$`)
// embedURL builds a privacy-friendly nocookie embed URL for a YouTube video id.
// It returns ("", false) for any id that isn't a valid 11-char YouTube id, so
// the caller can omit the embed instead of rendering a broken iframe.
func embedURL(providerVideoID string) (string, bool) {
if !youtubeIDRe.MatchString(providerVideoID) {
return "", false
}
return "https://www.youtube-nocookie.com/embed/" + providerVideoID, true
}
// actionVerbs is the fixed, ordered set of action toggles rendered in the button
// group. It mirrors the store's allowed actions (store/actions.go); order here is
// the display order, not the store's.
@@ -284,6 +300,8 @@ main { max-width: 60rem; margin: 0 auto; padding: var(--s4) var(--s3); }
.detail h1 { font-size: 1.7rem; line-height: 1.25; margin: 0 0 var(--s2); }
.detail .meta { color: var(--muted); font-size: .9rem; margin: 0 0 var(--s2); display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
.detail .source { margin: 0 0 var(--s4); font-size: .9rem; }
.detail .embed { margin: 0 0 var(--s4); aspect-ratio: 16 / 9; border-radius: var(--radius); overflow: hidden; background: #000; border: 1px solid var(--line); }
.detail .embed iframe { display: block; width: 100%; height: 100%; border: 0; }
.detail section { margin-top: var(--s4); }
.detail section h2 { font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); border-top: 1px solid var(--line); padding-top: var(--s3); margin: 0 0 var(--s2); }
.detail .body { white-space: pre-wrap; line-height: 1.7; margin: 0; }