all repos — fixyoutube-go @ a2037dceb0c6ef1588c64eea205c9f909399c8e1

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 clearHandler(w http.ResponseWriter, r *http.Request) {
 40	if r.Method != http.MethodPost {
 41		http.Redirect(w, r, "/", http.StatusFound)
 42		return
 43	}
 44
 45	err := r.ParseForm()
 46	if err != nil {
 47		logger.Error("Failed to parse form in /clear.")
 48		http.Error(w, err.Error(), http.StatusInternalServerError)
 49		return
 50	}
 51
 52	providedKey := r.PostForm.Get("apiKey")
 53	if providedKey != apiKey {
 54		logger.Debug("Wrong API key: ", providedKey)
 55		http.Error(w, "Wrong or missing API key.", http.StatusForbidden)
 56		return
 57	}
 58
 59	// rabbitpipe.ClearDB()
 60	logger.Info("Cache cleared.")
 61	http.Error(w, "Done.", http.StatusOK)
 62}
 63
 64func videoHandler(videoID string, w http.ResponseWriter, r *http.Request) {
 65	url := "https://www.youtube.com/watch?v=" + videoID
 66
 67	if !videoRegex.MatchString(videoID) {
 68		logger.Info("Invalid video ID: ", videoID)
 69		http.Error(w, "Invalid video ID.", http.StatusBadRequest)
 70		return
 71	}
 72
 73	video, err := invidious.RP.GetVideo(videoID)
 74	if err != nil || video == nil {
 75		logger.Info("Wrong video ID: ", videoID)
 76		http.Error(w, "Wrong video ID.", http.StatusNotFound)
 77		return
 78	}
 79
 80	if invidious.GetVideoURL(*video) == "" {
 81		logger.Debug("No URL available. Redirecting.")
 82		http.Redirect(w, r, url, http.StatusFound)
 83		return
 84	}
 85
 86	buf := &bytes.Buffer{}
 87	err = videoTemplate.Execute(buf, video)
 88	if err != nil {
 89		logger.Error("Failed to fill video template.")
 90		http.Error(w, err.Error(), http.StatusInternalServerError)
 91		return
 92	}
 93	buf.WriteTo(w)
 94}
 95
 96func watchHandler(w http.ResponseWriter, r *http.Request) {
 97	u, err := url.Parse(r.URL.String())
 98	if err != nil {
 99		logger.Error("Failed to parse URL: ", r.URL.String())
100		http.Error(w, err.Error(), http.StatusInternalServerError)
101		return
102	}
103
104	q := u.Query()
105	videoId := q.Get("v")
106	videoHandler(videoId, w, r)
107}
108
109func shortHandler(w http.ResponseWriter, r *http.Request) {
110	videoId := r.PathValue("videoId")
111	videoHandler(videoId, w, r)
112	return
113}
114
115func proxyHandler(w http.ResponseWriter, r *http.Request) {
116	videoId := r.PathValue("videoId")
117
118	vb, s := invidious.ProxyVideoId(videoId)
119	if s != http.StatusOK {
120		logger.Error("proxyHandler() failed. Final code: ", s)
121		http.Error(w, http.StatusText(s), s)
122		return
123	}
124	if !vb.ValidateLength() {
125		logger.Error("Buffer length is inconsistent.")
126		status := http.StatusInternalServerError
127		http.Error(w, http.StatusText(status), status)
128		return
129	}
130	h := w.Header()
131	h.Set("Status", "200")
132	h.Set("Content-Type", "video/mp4")
133	h.Set("Content-Length", strconv.FormatInt(vb.Length, 10))
134	io.Copy(w, vb.Buffer)
135}