all repos — fixyoutube-go @ 636c962ec2a61c90f7451b365aa808a957a9082e

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

add new volatile cache type
Marco Andronaco andronacomarco@gmail.com
Mon, 15 Jan 2024 14:05:45 +0100
commit

636c962ec2a61c90f7451b365aa808a957a9082e

parent

478e9f188969ba9b95c3eab2f0602c53792bfb7b

3 files changed, 143 insertions(+), 31 deletions(-)

jump to
M invidious/api.goinvidious/api.go

@@ -10,6 +10,13 @@ "strconv"

"time" ) +type Format struct { + Name string `json:"qualityLabel"` + Url string `json:"url"` + Container string `json:"container"` + Size string `json:"size"` +} + func (c *Client) fetchVideo(videoId string) (*Video, int) { endpoint := fmt.Sprintf(videosEndpoint, c.Instance, url.QueryEscape(videoId)) resp, err := c.http.Get(endpoint)

@@ -60,21 +67,13 @@ return res, http.StatusOK

} func (c *Client) isNotTimedOut(instance string) bool { - for i := range c.timeouts { - cur := c.timeouts[i] - if instance == cur.Instance { - return false - } - } - return true + return !c.timeouts.Has(instance) } func (c *Client) NewInstance() error { - now := time.Now() - - timeoutsTest := func(t Timeout) bool { return now.Sub(t.Timestamp) < timeoutDuration } - c.timeouts = filter(c.timeouts, timeoutsTest) - c.timeouts = append(c.timeouts, Timeout{c.Instance, now}) + if c.Instance != "" { + c.timeouts.Set(c.Instance, fmt.Errorf("Generic error")) + } resp, err := c.http.Get(instancesEndpoint) if err != nil {

@@ -100,9 +99,7 @@ }

for i := range jsonArray { instance := jsonArray[i][0].(string) - instanceTest := func(t Timeout) bool { return t.Instance == instance } - result := filter(c.timeouts, instanceTest) - if len(result) == 0 { + if !c.timeouts.Has(instance) { c.Instance = instance logger.Info("Using new instance: ", c.Instance) return nil
M invidious/invidious.goinvidious/invidious.go

@@ -6,6 +6,7 @@ "regexp"

"strconv" "time" + "github.com/BiRabittoh/fixyoutube-go/volatile" "github.com/sirupsen/logrus" )

@@ -17,22 +18,10 @@

var expireRegex = regexp.MustCompile(`(?i)expire=(\d+)`) var logger = logrus.New() -type Timeout struct { - Instance string - Timestamp time.Time -} - type Client struct { http *http.Client - timeouts []Timeout + timeouts *volatile.Volatile[string, error] Instance string -} - -type Format struct { - Name string `json:"qualityLabel"` - Url string `json:"url"` - Container string `json:"container"` - Size string `json:"size"` } type Video struct {

@@ -88,8 +77,6 @@ case http.StatusNotFound:

logger.Debug("Video does not exist or can't be retrieved.") return nil, err default: - fallthrough - case http.StatusInternalServerError: err = c.NewInstance() if err != nil { logger.Error("Could not get a new instance: ", err)

@@ -108,9 +95,10 @@ }

func NewClient(httpClient *http.Client) *Client { InitDB() + timeouts := volatile.NewVolatile[string, error](timeoutDuration) client := &Client{ http: httpClient, - timeouts: []Timeout{}, + timeouts: timeouts, } err := client.NewInstance() if err != nil {
A volatile/volatile.go

@@ -0,0 +1,127 @@

+package volatile + +import ( + "fmt" + "time" + + "github.com/sirupsen/logrus" +) + +var logger = logrus.New() + +type Element[K comparable, V any] struct { + key K + value V + Timestamp time.Time +} + +type Volatile[K comparable, V any] struct { + data []Element[K, V] + timeToLive time.Duration +} + +func reverseIntArray(arr []int) { + length := len(arr) + for i := 0; i < length/2; i++ { + arr[i], arr[length-i-1] = arr[length-i-1], arr[i] + } +} + +func NewVolatile[K comparable, V any](timeToLive time.Duration) *Volatile[K, V] { + return &Volatile[K, V]{ + data: nil, + timeToLive: timeToLive, + } +} + +func (v *Volatile[K, V]) removeIndex(index int) error { + if index < 0 || index >= len(v.data) { + return fmt.Errorf("Index out of bounds") + } + v.data = append(v.data[:index], v.data[index+1:]...) + return nil +} + +func (v *Volatile[K, V]) clean() error { + now := time.Now() + + for i := len(v.data) - 1; i >= 0; i-- { + if now.Sub(v.data[i].Timestamp) > v.timeToLive { + err := v.removeIndex(i) + if err != nil { + return err + } + } + } + return nil +} + +func (v *Volatile[K, V]) indexOf(key K) int { + for i := range v.data { + e := v.data[i] + if e.key == key { + return i + } + } + return -1 +} + +func (v *Volatile[K, V]) Has(key K) bool { + err := v.clean() + if err != nil { + logger.Println(err) + return false + } + return v.indexOf(key) != -1 +} + +func (v *Volatile[K, V]) Get(key K) (*V, error) { + err := v.clean() + if err != nil { + logger.Println(err) + return nil, err + } + + i := v.indexOf(key) + if i == -1 { + return nil, fmt.Errorf("Not found") + } + return &v.data[i].value, nil +} + +func (v *Volatile[K, V]) Remove(key K) (*V, error) { + err := v.clean() + if err != nil { + logger.Error(err) + return nil, err + } + i := v.indexOf(key) + if i == -1 { + logger.Error(err) + return nil, err + } + + value := &v.data[i].value + err = v.removeIndex(i) + if err != nil { + logger.Error(err) + return nil, err + } + return value, nil +} + +func (v *Volatile[K, V]) Set(key K, value V) error { + err := v.clean() + if err != nil { + logger.Error(err) + return err + } + _, err = v.Remove(key) + if err != nil { + logger.Error(err) + return err + } + e := Element[K, V]{key: key, value: value, Timestamp: time.Now()} + v.data = append(v.data, e) + return nil +}