feat(web): parse YouTube video id from pasted URL forms
Pure parser for watch?v=, youtu.be/, shorts/, embed/, and bare ids; rejects non-YouTube hosts and malformed input. Foundation for paste-a-URL summarize (Feature 2). No fetch, no gate interaction — parsing only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// youtubeVideoID matches a canonical YouTube video id: exactly 11 URL-safe chars.
|
||||
var youtubeVideoID = regexp.MustCompile(`^[A-Za-z0-9_-]{11}$`)
|
||||
|
||||
// parseYouTubeVideoID extracts the 11-character video id from a pasted YouTube
|
||||
// URL (watch?v=, youtu.be/, shorts/, embed/) or a bare id. It rejects non-YouTube
|
||||
// hosts and anything that doesn't yield a valid id, so the paste flow never tries
|
||||
// to fetch a video that can't exist (Feature 2).
|
||||
func parseYouTubeVideoID(raw string) (string, error) {
|
||||
s := strings.TrimSpace(raw)
|
||||
if s == "" {
|
||||
return "", fmt.Errorf("empty input")
|
||||
}
|
||||
|
||||
// Bare id (no URL) — accept directly.
|
||||
if youtubeVideoID.MatchString(s) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Accept scheme-less URLs (youtube.com/watch?v=...) by giving url.Parse a host.
|
||||
if !strings.Contains(s, "://") {
|
||||
s = "https://" + s
|
||||
}
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("not a URL: %w", err)
|
||||
}
|
||||
|
||||
host := strings.ToLower(u.Hostname())
|
||||
isYouTube := host == "youtu.be" || host == "youtube.com" || strings.HasSuffix(host, ".youtube.com")
|
||||
if !isYouTube {
|
||||
return "", fmt.Errorf("not a YouTube URL: %q", host)
|
||||
}
|
||||
|
||||
var id string
|
||||
switch {
|
||||
case host == "youtu.be":
|
||||
// youtu.be/<id>
|
||||
id = strings.Trim(u.Path, "/")
|
||||
case u.Path == "/watch":
|
||||
id = u.Query().Get("v")
|
||||
default:
|
||||
// /shorts/<id>, /embed/<id>
|
||||
parts := strings.Split(strings.Trim(u.Path, "/"), "/")
|
||||
if len(parts) == 2 && (parts[0] == "shorts" || parts[0] == "embed") {
|
||||
id = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
if !youtubeVideoID.MatchString(id) {
|
||||
return "", fmt.Errorf("no YouTube video id in %q", raw)
|
||||
}
|
||||
return id, nil
|
||||
}
|
||||
Reference in New Issue
Block a user