all repos — gopipe @ 6eff4ae6fb13714b5c8a946da03292c0f530ac4e

Embed YouTube videos on Telegram, Discord and more!

src/app/proxy.go (view raw)

  1package app
  2
  3import (
  4	"bytes"
  5	"fmt"
  6	"io"
  7	"log"
  8	"net/http"
  9	"strconv"
 10	"strings"
 11	"time"
 12
 13	g "github.com/birabittoh/gopipe/src/globals"
 14	"github.com/birabittoh/gopipe/src/subs"
 15)
 16
 17func proxyHandler(w http.ResponseWriter, r *http.Request) {
 18	if !g.Proxy {
 19		http.Error(w, err404, http.StatusNotFound)
 20		return
 21	}
 22
 23	videoID := r.PathValue("videoID")
 24	formatID := getFormatID(r.PathValue("formatID"))
 25
 26	video, err := g.KS.Get(videoID)
 27	if err != nil || video == nil {
 28		http.Error(w, err404, http.StatusNotFound)
 29		return
 30	}
 31
 32	format := getFormat(*video, formatID)
 33	if format == nil {
 34		http.Error(w, err500, http.StatusInternalServerError)
 35		return
 36	}
 37
 38	key := fmt.Sprintf("%s:%d", videoID, formatID)
 39	content, err := g.PKS.Get(key)
 40	if err == nil && content != nil {
 41		log.Println("Using cached content for ", key)
 42		w.Header().Set("Content-Type", "video/mp4")
 43		w.Header().Set("Content-Length", strconv.Itoa(content.Len()))
 44		w.Write(content.Bytes())
 45		return
 46	}
 47
 48	res, err := g.C.Get(format.URL)
 49	if err != nil {
 50		http.Error(w, err500, http.StatusInternalServerError)
 51		return
 52	}
 53	defer res.Body.Close()
 54
 55	w.Header().Set("Content-Type", res.Header.Get("Content-Type"))
 56	w.Header().Set("Content-Length", res.Header.Get("Content-Length"))
 57
 58	pr, pw := io.Pipe()
 59
 60	// Save the content to the cache asynchronously
 61	go func() {
 62		var buf bytes.Buffer
 63		_, err := io.Copy(&buf, pr)
 64		if err != nil {
 65			log.Println("Error while copying to buffer for cache:", err)
 66			return
 67		}
 68
 69		g.PKS.Set(key, buf, 5*time.Minute)
 70		pw.Close()
 71	}()
 72
 73	// Stream the content to the client while it's being downloaded and piped
 74	_, err = io.Copy(io.MultiWriter(w, pw), res.Body)
 75	if err != nil {
 76		http.Error(w, err500, http.StatusInternalServerError)
 77		return
 78	}
 79}
 80
 81func subHandler(w http.ResponseWriter, r *http.Request) {
 82	videoID := r.PathValue("videoID")
 83
 84	video, err := g.KS.Get(videoID)
 85	if err != nil || video == nil {
 86		http.Error(w, err404, http.StatusNotFound)
 87		return
 88	}
 89
 90	captions := getCaptions(*video)
 91	caption, ok := captions[strings.TrimSuffix(r.PathValue("language"), ".vtt")]
 92	if !ok {
 93		http.Error(w, err404, http.StatusNotFound)
 94		return
 95	}
 96
 97	res, err := g.C.Get(caption.URL)
 98	if err != nil {
 99		http.Error(w, err500, http.StatusInternalServerError)
100		return
101	}
102	defer res.Body.Close()
103
104	w.Header().Set("Content-Type", "text/vtt")
105
106	content, err := io.ReadAll(res.Body)
107	if err != nil {
108		http.Error(w, err500, http.StatusInternalServerError)
109		return
110	}
111
112	err = subs.Parse(content, w)
113	if err != nil {
114		http.Error(w, err500, http.StatusInternalServerError)
115		return
116	}
117}