all repos — fixyoutube-go @ 29d827408340269e64205c65fe96873275948e65

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	invidious.ClearDB()
 60	logger.Info("Cache cleared.")
 61	http.Error(w, "Done.", http.StatusOK)
 62}
 63
 64func videoHandler(videoId string, invidiousClient *invidious.Client, w http.ResponseWriter, r *http.Request) {
 65	url := "https://www.youtube.com/watch?v=" + videoId
 66	/*
 67		userAgent := r.UserAgent()
 68		res := userAgentRegex.MatchString(userAgent)
 69		if !res {
 70			logger.Debug("Regex did not match. Redirecting. UA:", userAgent)
 71			http.Redirect(w, r, url, http.StatusFound)
 72			return
 73		}
 74	*/
 75
 76	if !videoRegex.MatchString(videoId) {
 77		logger.Info("Invalid video ID: ", videoId)
 78		http.Error(w, "Invalid video ID.", http.StatusBadRequest)
 79		return
 80	}
 81
 82	video, err := invidiousClient.GetVideo(videoId, true)
 83	if err != nil {
 84		logger.Info("Wrong video ID: ", videoId)
 85		http.Error(w, "Wrong video ID.", http.StatusNotFound)
 86		return
 87	}
 88
 89	if video.Url == "" {
 90		logger.Debug("No URL available. Redirecting.")
 91		http.Redirect(w, r, url, http.StatusFound)
 92		return
 93	}
 94
 95	buf := &bytes.Buffer{}
 96	err = videoTemplate.Execute(buf, video)
 97	if err != nil {
 98		logger.Error("Failed to fill video template.")
 99		http.Error(w, err.Error(), http.StatusInternalServerError)
100		return
101	}
102	buf.WriteTo(w)
103}
104
105func watchHandler(invidiousClient *invidious.Client) http.HandlerFunc {
106	return func(w http.ResponseWriter, r *http.Request) {
107		u, err := url.Parse(r.URL.String())
108		if err != nil {
109			logger.Error("Failed to parse URL: ", r.URL.String())
110			http.Error(w, err.Error(), http.StatusInternalServerError)
111			return
112		}
113		q := u.Query()
114		videoId := q.Get("v")
115		videoHandler(videoId, invidiousClient, w, r)
116	}
117}
118
119func shortHandler(invidiousClient *invidious.Client) http.HandlerFunc {
120	return func(w http.ResponseWriter, r *http.Request) {
121		videoId := r.PathValue("videoId")
122		videoHandler(videoId, invidiousClient, w, r)
123	}
124}
125
126func proxyHandler(invidiousClient *invidious.Client) http.HandlerFunc {
127	return func(w http.ResponseWriter, r *http.Request) {
128		videoId := r.PathValue("videoId")
129
130		vb, s := invidiousClient.ProxyVideoId(videoId)
131		if s != http.StatusOK {
132			logger.Error("proxyHandler() failed. Final code: ", s)
133			http.Error(w, http.StatusText(s), s)
134			return
135		}
136		if !vb.ValidateLength() {
137			logger.Error("Buffer length is inconsistent.")
138			status := http.StatusInternalServerError
139			http.Error(w, http.StatusText(status), status)
140			return
141		}
142		h := w.Header()
143		h.Set("Status", "200")
144		h.Set("Content-Type", "video/mp4")
145		h.Set("Content-Length", strconv.FormatInt(vb.Length, 10))
146		io.Copy(w, vb.Buffer)
147	}
148}