src/app/handlers.go (view raw)
1package app
2
3import (
4 "fmt"
5 "html/template"
6 "log"
7 "net/http"
8 "regexp"
9
10 g "github.com/birabittoh/gopipe/src/globals"
11 "github.com/kkdai/youtube/v2"
12 "golang.org/x/time/rate"
13)
14
15const (
16 fmtYouTubeURL = "https://www.youtube.com/watch?v=%s"
17 err404 = "Not Found"
18 err500 = "Internal Server Error"
19 err401 = "Unauthorized"
20 heading = `<!--
21 .d8888b. 88888888888 888
22d88P Y88b 888 888
23888 888 888 888
24888 .d88b. 888 888 888 88888b. .d88b.
25888 88888 d88""88b 888 888 888 888 "88b d8P Y8b
26888 888 888 888 888 888 888 888 888 88888888
27Y88b d88P Y88..88P 888 Y88b 888 888 d88P Y8b.
28 "Y8888P88 "Y88P" 888 "Y88888 88888P" "Y8888
29
30A better way to embed YouTube videos on Telegram.
31-->`
32)
33
34var (
35 videoRegex = regexp.MustCompile(`(?i)^[a-z0-9_-]{11}$`)
36)
37
38func limit(limiter *rate.Limiter, next http.Handler) http.Handler {
39 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
40 if !limiter.Allow() {
41 status := http.StatusTooManyRequests
42 http.Error(w, http.StatusText(status), status)
43 return
44 }
45 next.ServeHTTP(w, r)
46 })
47}
48
49func indexHandler(w http.ResponseWriter, r *http.Request) {
50 err := g.XT.ExecuteTemplate(w, "index.tmpl", nil)
51 if err != nil {
52 log.Println("indexHandler ERROR:", err)
53 http.Error(w, err500, http.StatusInternalServerError)
54 return
55 }
56}
57
58func videoHandler(w http.ResponseWriter, r *http.Request) {
59 videoID := r.URL.Query().Get("v")
60 if videoID == "" {
61 videoID = r.PathValue("videoID")
62 if videoID == "" {
63 http.Error(w, "Missing video ID", http.StatusBadRequest)
64 return
65 }
66 }
67
68 if !videoRegex.MatchString(videoID) {
69 log.Println("Invalid video ID:", videoID)
70 http.Error(w, err404, http.StatusNotFound)
71 return
72 }
73
74 formatID := getFormatID(r.PathValue("formatID"))
75
76 video, format, err := getVideo(videoID, formatID)
77 if err != nil {
78 http.Error(w, err500, http.StatusInternalServerError)
79 return
80 }
81
82 if video == nil || format == nil {
83 http.Error(w, err500, http.StatusInternalServerError)
84 return
85 }
86
87 var thumbnail string
88 if len(video.Thumbnails) > 0 {
89 thumbnail = video.Thumbnails[len(video.Thumbnails)-1].URL
90 }
91
92 videoURL := format.URL
93 if g.Proxy {
94 videoURL = fmt.Sprintf("/proxy/%s/%d", videoID, formatID)
95 }
96
97 data := map[string]interface{}{
98 "VideoID": videoID,
99 "VideoURL": videoURL,
100 "Author": video.Author,
101 "Title": video.Title,
102 "Description": video.Description,
103 "Thumbnail": thumbnail,
104 "Duration": video.Duration,
105 "Captions": getCaptions(*video),
106 "Heading": template.HTML(heading),
107 }
108
109 err = g.XT.ExecuteTemplate(w, "video.tmpl", data)
110 if err != nil {
111 log.Println("indexHandler ERROR:", err)
112 http.Error(w, err500, http.StatusInternalServerError)
113 return
114 }
115}
116
117func cacheHandler(w http.ResponseWriter, r *http.Request) {
118 username, password, ok := r.BasicAuth()
119 if !ok || username != g.AdminUser || password != g.AdminPass {
120 w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
121 http.Error(w, err401, http.StatusUnauthorized)
122 return
123 }
124
125 var videos []youtube.Video
126 for s := range g.KS.Keys() {
127 video, err := g.KS.Get(s)
128 if err != nil || video == nil {
129 continue
130 }
131 videos = append(videos, *video)
132 }
133
134 data := map[string]interface{}{"Videos": videos}
135 err := g.XT.ExecuteTemplate(w, "cache.tmpl", data)
136 if err != nil {
137 log.Println("cacheHandler ERROR:", err)
138 http.Error(w, err500, http.StatusInternalServerError)
139 return
140 }
141}