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}