all repos — gopipe @ 72f5236b15f0ae2fd00b01b36a7bce52dbd2c429

Embed YouTube videos on Telegram, Discord and more!

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		"VideoFormats": video.Formats.Select(formatsSelectFnVideo),
108		"AudioFormats": video.Formats.Select(formatsSelectFnAudio),
109	}
110
111	err = g.XT.ExecuteTemplate(w, "video.tmpl", data)
112	if err != nil {
113		log.Println("indexHandler ERROR:", err)
114		http.Error(w, err500, http.StatusInternalServerError)
115		return
116	}
117}
118
119func downloadHandler(w http.ResponseWriter, r *http.Request) {
120	videoID := r.FormValue("video")
121	if videoID == "" {
122		http.Error(w, "Missing video ID", http.StatusBadRequest)
123		return
124	}
125
126	if !videoRegex.MatchString(videoID) {
127		log.Println("Invalid video ID:", videoID)
128		http.Error(w, err404, http.StatusNotFound)
129		return
130	}
131
132	itagno := r.FormValue("itagno")
133	if itagno == "" {
134		http.Error(w, "Missing ItagNo", http.StatusBadRequest)
135		return
136	}
137
138	video, err := g.KS.Get(videoID)
139	if err != nil || video == nil {
140		http.Error(w, err404, http.StatusNotFound)
141		return
142	}
143
144	formats := video.Formats.Quality(itagno)
145	if len(formats) == 0 {
146		http.Error(w, err404, http.StatusNotFound)
147		return
148	}
149
150	http.Redirect(w, r, formats[0].URL, http.StatusFound)
151}
152
153func cacheHandler(w http.ResponseWriter, r *http.Request) {
154	username, password, ok := r.BasicAuth()
155	if !ok || username != g.AdminUser || password != g.AdminPass {
156		w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
157		http.Error(w, err401, http.StatusUnauthorized)
158		return
159	}
160
161	var videos []youtube.Video
162	for s := range g.KS.Keys() {
163		video, err := g.KS.Get(s)
164		if err != nil || video == nil {
165			continue
166		}
167		videos = append(videos, *video)
168	}
169
170	data := map[string]interface{}{"Videos": videos}
171	err := g.XT.ExecuteTemplate(w, "cache.tmpl", data)
172	if err != nil {
173		log.Println("cacheHandler ERROR:", err)
174		http.Error(w, err500, http.StatusInternalServerError)
175		return
176	}
177}