invidious/api.go (view raw)
1package invidious
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "net/http"
8 "net/url"
9 "strconv"
10 "time"
11)
12
13type Format struct {
14 Name string `json:"qualityLabel"`
15 Url string `json:"url"`
16 Container string `json:"container"`
17 Size string `json:"size"`
18}
19
20func (c *Client) fetchVideo(videoId string) (*Video, int) {
21 endpoint := fmt.Sprintf(videosEndpoint, c.Instance, url.QueryEscape(videoId))
22 resp, err := c.http.Get(endpoint)
23 if err != nil {
24 logger.Error(err)
25 return nil, http.StatusInternalServerError
26 }
27 defer resp.Body.Close()
28
29 if resp.StatusCode != http.StatusOK {
30 logger.Warn("Invidious gave the following status code: ", resp.StatusCode)
31 return nil, http.StatusNotFound
32 }
33
34 body, err := io.ReadAll(resp.Body)
35 if err != nil {
36 logger.Error(err)
37 return nil, http.StatusInternalServerError
38 }
39
40 res := &Video{}
41 err = json.Unmarshal(body, res)
42 if err != nil {
43 logger.Error(err)
44 return nil, http.StatusInternalServerError
45 }
46
47 mp4Test := func(f Format) bool { return f.Container == "mp4" }
48 res.Formats = filter(res.Formats, mp4Test)
49
50 expireString := expireRegex.FindStringSubmatch(res.Formats[0].Url)
51 expireTimestamp, err := strconv.ParseInt(expireString[1], 10, 64)
52 if err != nil {
53 logger.Error(err)
54 return nil, http.StatusInternalServerError
55 }
56 res.Expire = time.Unix(expireTimestamp, 0)
57
58 _, l, i := c.findCompatibleFormat(res)
59 if l == 0 {
60 logger.Warn("No compatible formats found for video.")
61 res.Url = ""
62 } else {
63 res.Url = res.Formats[i].Url
64 }
65
66 return res, http.StatusOK
67}
68
69func (c *Client) isNotTimedOut(instance string) bool {
70 return !c.timeouts.Has(instance)
71}
72
73func (c *Client) NewInstance() error {
74 if c.Instance != "" {
75 c.timeouts.Set(c.Instance, fmt.Errorf("Generic error"))
76 }
77
78 resp, err := c.http.Get(instancesEndpoint)
79 if err != nil {
80 return err
81 }
82 defer resp.Body.Close()
83
84 body, err := io.ReadAll(resp.Body)
85 if err != nil {
86 return err
87 }
88
89 if resp.StatusCode != http.StatusOK {
90 return fmt.Errorf("HTTP error: %d", resp.StatusCode)
91 }
92
93 var jsonArray [][]interface{}
94 err = json.Unmarshal(body, &jsonArray)
95 if err != nil {
96 logger.Error("Could not unmarshal JSON response for instances.")
97 return err
98 }
99
100 for i := range jsonArray {
101 instance := jsonArray[i][0].(string)
102 if !c.timeouts.Has(instance) {
103 c.Instance = instance
104 logger.Info("Using new instance: ", c.Instance)
105 return nil
106 }
107 }
108
109 return fmt.Errorf("Cannot find a valid instance.")
110}