all repos — fixyoutube-go @ 78a91dca196fbcf8499a86c8282cce3b83e1302d

A better way to embed YouTube videos everywhere (inspired by FixTweet).

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	b, l, i := c.findCompatibleFormat(res)
 59	if l == 0 {
 60		logger.Warn("No compatible formats found for video.")
 61		res.Url = ""
 62	} else {
 63		videoBuffer := NewVideoBuffer(b, l)
 64		c.buffers.Set(videoId, videoBuffer)
 65		res.Url = res.Formats[i].Url
 66	}
 67
 68	return res, http.StatusOK
 69}
 70
 71func (c *Client) isNotTimedOut(instance string) bool {
 72	return !c.timeouts.Has(instance)
 73}
 74
 75func (c *Client) NewInstance() error {
 76	if c.Instance != "" {
 77		err := fmt.Errorf("Generic error")
 78		c.timeouts.Set(c.Instance, &err)
 79	}
 80
 81	resp, err := c.http.Get(instancesEndpoint)
 82	if err != nil {
 83		return err
 84	}
 85	defer resp.Body.Close()
 86
 87	body, err := io.ReadAll(resp.Body)
 88	if err != nil {
 89		return err
 90	}
 91
 92	if resp.StatusCode != http.StatusOK {
 93		return fmt.Errorf("HTTP error: %d", resp.StatusCode)
 94	}
 95
 96	var jsonArray [][]interface{}
 97	err = json.Unmarshal(body, &jsonArray)
 98	if err != nil {
 99		logger.Error("Could not unmarshal JSON response for instances.")
100		return err
101	}
102
103	for i := range jsonArray {
104		instance := jsonArray[i][0].(string)
105		if !c.timeouts.Has(instance) {
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}