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
13func (c *Client) fetchVideo(videoId string) (*Video, int) {
14 endpoint := fmt.Sprintf(videosEndpoint, c.Instance, url.QueryEscape(videoId))
15 resp, err := c.http.Get(endpoint)
16 if err != nil {
17 logger.Error(err)
18 return nil, http.StatusInternalServerError
19 }
20 defer resp.Body.Close()
21
22 if resp.StatusCode != http.StatusOK {
23 logger.Warn("Invidious gave the following status code: ", resp.StatusCode)
24 return nil, http.StatusNotFound
25 }
26
27 body, err := io.ReadAll(resp.Body)
28 if err != nil {
29 logger.Error(err)
30 return nil, http.StatusInternalServerError
31 }
32
33 res := &Video{}
34 err = json.Unmarshal(body, res)
35 if err != nil {
36 logger.Error(err)
37 return nil, http.StatusInternalServerError
38 }
39
40 mp4Test := func(f Format) bool { return f.Container == "mp4" }
41 res.Formats = filter(res.Formats, mp4Test)
42
43 expireString := expireRegex.FindStringSubmatch(res.Formats[0].Url)
44 expireTimestamp, err := strconv.ParseInt(expireString[1], 10, 64)
45 if err != nil {
46 logger.Error(err)
47 return nil, http.StatusInternalServerError
48 }
49 res.Expire = time.Unix(expireTimestamp, 0)
50
51 _, l, i := c.findCompatibleFormat(res)
52 if l == 0 {
53 logger.Warn("No compatible formats found for video.")
54 res.Url = ""
55 } else {
56 res.Url = res.Formats[i].Url
57 }
58
59 return res, http.StatusOK
60}
61
62func (c *Client) isNotTimedOut(instance string) bool {
63 for i := range c.timeouts {
64 cur := c.timeouts[i]
65 if instance == cur.Instance {
66 return false
67 }
68 }
69 return true
70}
71
72func (c *Client) NewInstance() error {
73 now := time.Now()
74
75 timeoutsTest := func(t Timeout) bool { return now.Sub(t.Timestamp) < timeoutDuration }
76 c.timeouts = filter(c.timeouts, timeoutsTest)
77 c.timeouts = append(c.timeouts, Timeout{c.Instance, now})
78
79 resp, err := c.http.Get(instancesEndpoint)
80 if err != nil {
81 return err
82 }
83 defer resp.Body.Close()
84
85 body, err := io.ReadAll(resp.Body)
86 if err != nil {
87 return err
88 }
89
90 if resp.StatusCode != http.StatusOK {
91 return fmt.Errorf("HTTP error: %d", resp.StatusCode)
92 }
93
94 var jsonArray [][]interface{}
95 err = json.Unmarshal(body, &jsonArray)
96 if err != nil {
97 logger.Error("Could not unmarshal JSON response for instances.")
98 return err
99 }
100
101 for i := range jsonArray {
102 instance := jsonArray[i][0].(string)
103 instanceTest := func(t Timeout) bool { return t.Instance == instance }
104 result := filter(c.timeouts, instanceTest)
105 if len(result) == 0 {
106 c.Instance = instance
107 logger.Info("Using new instance: ", c.Instance)
108 return nil
109 }
110 }
111
112 return fmt.Errorf("Cannot find a valid instance.")
113}