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}