invidious/proxy.go (view raw)
1package invidious
2
3import (
4 "bytes"
5 "io"
6 "net/http"
7)
8
9func (c *Client) proxyUrl(url string) (*bytes.Buffer, int64, int) {
10 req, err := http.NewRequest(http.MethodGet, url, nil)
11 if err != nil {
12 logger.Error(err) // bad request
13 return nil, 0, http.StatusInternalServerError
14 }
15
16 resp, err := c.http.Do(req)
17 if err != nil {
18 logger.Error(err) // request failed
19 return nil, 0, http.StatusGone
20 }
21
22 if resp.StatusCode != http.StatusOK {
23 return nil, 0, resp.StatusCode
24 }
25
26 if resp.ContentLength == 0 {
27 return nil, 0, http.StatusNoContent
28 }
29
30 if resp.ContentLength > maxSizeBytes {
31 logger.Debug("Content-Length exceeds max size.")
32 return nil, 0, http.StatusBadRequest
33 }
34 defer resp.Body.Close()
35
36 b := new(bytes.Buffer)
37 l, err := io.Copy(b, resp.Body)
38 if l != resp.ContentLength {
39 logger.Debug("Content-Length is inconsistent.")
40 return nil, 0, http.StatusBadRequest
41 }
42
43 return b, l, http.StatusOK
44}
45
46func (c *Client) proxyVideo(video *Video) (*bytes.Buffer, int64, int) {
47 for i := len(video.Formats) - 1; i >= 0; i-- {
48 url := video.Formats[i].Url
49 logger.Debug(url)
50 b, l, httpStatus := c.proxyUrl(url)
51 if httpStatus == http.StatusOK {
52 return b, l, i
53 }
54 logger.Debug("Format ", i, "failed with status code ", httpStatus)
55 }
56 return nil, 0, -1
57}
58
59func (c *Client) ProxyVideoId(videoId string) (*bytes.Buffer, int64, int) {
60 video, err := GetVideoDB(videoId)
61 if err != nil {
62 logger.Info("Cannot proxy a video that is not cached: https://youtu.be/", videoId)
63 return nil, 0, http.StatusBadRequest
64 }
65
66 return c.proxyVideo(video)
67}