all repos — fixyoutube-go @ c0b6900ba8c94a0dba08b736131ca72692a9f8d9

A better way to embed YouTube videos everywhere (inspired by FixTweet).

handlers.go (view raw)

  1package main
  2
  3import (
  4	"bytes"
  5	"embed"
  6	"io"
  7	"net/http"
  8	"net/url"
  9	"regexp"
 10	"strconv"
 11	"text/template"
 12
 13	"github.com/birabittoh/fixyoutube-go/invidious"
 14)
 15
 16const templatesDirectory = "templates/"
 17
 18var (
 19	//go:embed templates/index.html templates/video.html
 20	templates     embed.FS
 21	indexTemplate = template.Must(template.ParseFS(templates, templatesDirectory+"index.html"))
 22	videoTemplate = template.Must(template.ParseFS(templates, templatesDirectory+"video.html"))
 23	// userAgentRegex = regexp.MustCompile(`(?i)bot|facebook|embed|got|firefox\/92|firefox\/38|curl|wget|go-http|yahoo|generator|whatsapp|preview|link|proxy|vkshare|images|analyzer|index|crawl|spider|python|cfnetwork|node`)
 24	videoRegex = regexp.MustCompile(`(?i)^[a-z0-9_-]{11}$`)
 25)
 26
 27func indexHandler(w http.ResponseWriter, r *http.Request) {
 28	buf := &bytes.Buffer{}
 29	err := indexTemplate.Execute(buf, nil)
 30	if err != nil {
 31		logger.Error("Failed to fill index template.")
 32		http.Error(w, err.Error(), http.StatusInternalServerError)
 33		return
 34	}
 35
 36	buf.WriteTo(w)
 37}
 38
 39func videoHandler(videoID string, w http.ResponseWriter, r *http.Request) {
 40	url := "https://www.youtube.com/watch?v=" + videoID
 41
 42	if !videoRegex.MatchString(videoID) {
 43		logger.Info("Invalid video ID: ", videoID)
 44		http.Error(w, "Invalid video ID.", http.StatusBadRequest)
 45		return
 46	}
 47
 48	video, err := invidious.RP.GetVideo(videoID)
 49	if err != nil || video == nil {
 50		logger.Info("Wrong video ID: ", videoID)
 51		http.Error(w, "Wrong video ID.", http.StatusNotFound)
 52		return
 53	}
 54
 55	if invidious.GetVideoURL(*video) == "" {
 56		logger.Debug("No URL available. Redirecting.")
 57		http.Redirect(w, r, url, http.StatusFound)
 58		return
 59	}
 60
 61	buf := &bytes.Buffer{}
 62	err = videoTemplate.Execute(buf, video)
 63	if err != nil {
 64		logger.Error("Failed to fill video template.")
 65		http.Error(w, err.Error(), http.StatusInternalServerError)
 66		return
 67	}
 68	buf.WriteTo(w)
 69}
 70
 71func watchHandler(w http.ResponseWriter, r *http.Request) {
 72	u, err := url.Parse(r.URL.String())
 73	if err != nil {
 74		logger.Error("Failed to parse URL: ", r.URL.String())
 75		http.Error(w, err.Error(), http.StatusInternalServerError)
 76		return
 77	}
 78
 79	q := u.Query()
 80	videoId := q.Get("v")
 81	videoHandler(videoId, w, r)
 82}
 83
 84func shortHandler(w http.ResponseWriter, r *http.Request) {
 85	videoId := r.PathValue("videoId")
 86	videoHandler(videoId, w, r)
 87}
 88
 89func proxyHandler(w http.ResponseWriter, r *http.Request) {
 90	videoId := r.PathValue("videoId")
 91
 92	vb, s := invidious.ProxyVideoId(videoId)
 93	if s != http.StatusOK {
 94		logger.Error("proxyHandler() failed. Final code: ", s)
 95		http.Error(w, http.StatusText(s), s)
 96		return
 97	}
 98	if !vb.ValidateLength() {
 99		logger.Error("Buffer length is inconsistent.")
100		status := http.StatusInternalServerError
101		http.Error(w, http.StatusText(status), status)
102		return
103	}
104	h := w.Header()
105	h.Set("Status", "200")
106	h.Set("Content-Type", "video/mp4")
107	h.Set("Content-Length", strconv.FormatInt(vb.Length, 10))
108	io.Copy(w, vb.Buffer)
109}