258 lines
5 KiB
Go
258 lines
5 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
rumblelivestreamlib "github.com/tylertravisty/rumble-livestream-lib-go"
|
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
|
)
|
|
|
|
type Api struct {
|
|
callers map[string]*caller
|
|
callersMu sync.Mutex
|
|
ctx context.Context
|
|
display string
|
|
displayMu sync.Mutex
|
|
logError *log.Logger
|
|
logInfo *log.Logger
|
|
}
|
|
|
|
type caller struct {
|
|
cancel context.CancelFunc
|
|
cancelMu sync.Mutex
|
|
display bool
|
|
displayMu sync.Mutex
|
|
interval time.Duration
|
|
name string
|
|
response *rumblelivestreamlib.LivestreamResponse
|
|
responseMu sync.Mutex
|
|
url string
|
|
}
|
|
|
|
type event struct {
|
|
close bool
|
|
err error
|
|
name string
|
|
resp *rumblelivestreamlib.LivestreamResponse
|
|
}
|
|
|
|
func NewApi(logError *log.Logger, logInfo *log.Logger) *Api {
|
|
return &Api{logError: logError, logInfo: logInfo}
|
|
}
|
|
|
|
func (a *Api) Response(name string) *rumblelivestreamlib.LivestreamResponse {
|
|
a.callersMu.Lock()
|
|
defer a.callersMu.Unlock()
|
|
caller, exists := a.callers[name]
|
|
if !exists {
|
|
return nil
|
|
}
|
|
|
|
caller.responseMu.Lock()
|
|
defer caller.responseMu.Unlock()
|
|
|
|
copy := *caller.response
|
|
return ©
|
|
}
|
|
|
|
func (a *Api) Display(name string) error {
|
|
a.displayMu.Lock()
|
|
defer a.displayMu.Unlock()
|
|
if name == a.display {
|
|
return nil
|
|
}
|
|
|
|
a.callersMu.Lock()
|
|
defer a.callersMu.Unlock()
|
|
|
|
if a.display != "" {
|
|
displaying, exists := a.callers[a.display]
|
|
if !exists {
|
|
return pkgErr("", fmt.Errorf("displaying caller does not exist: %s", a.display))
|
|
}
|
|
displaying.displayMu.Lock()
|
|
displaying.display = false
|
|
displaying.displayMu.Unlock()
|
|
a.display = ""
|
|
}
|
|
|
|
caller, exists := a.callers[name]
|
|
if !exists {
|
|
// return pkgErr("", fmt.Errorf("caller does not exist: %s", name))
|
|
runtime.EventsEmit(a.ctx, "PageActive", false)
|
|
return nil
|
|
}
|
|
caller.displayMu.Lock()
|
|
caller.display = true
|
|
caller.displayMu.Unlock()
|
|
|
|
a.display = name
|
|
|
|
a.handleResponse(caller)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *Api) Startup(ctx context.Context) {
|
|
a.ctx = ctx
|
|
a.callers = map[string]*caller{}
|
|
}
|
|
|
|
func (a *Api) Shutdown() error {
|
|
for _, caller := range a.callers {
|
|
caller.cancelMu.Lock()
|
|
if caller.cancel != nil {
|
|
caller.cancel()
|
|
}
|
|
caller.cancelMu.Unlock()
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *Api) Start(name string, url string, interval time.Duration) error {
|
|
if name == "" {
|
|
return fmt.Errorf("name is empty")
|
|
}
|
|
if url == "" {
|
|
return fmt.Errorf("url is empty")
|
|
}
|
|
|
|
a.callersMu.Lock()
|
|
defer a.callersMu.Unlock()
|
|
if _, active := a.callers[name]; active {
|
|
return nil
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
caller := &caller{
|
|
cancel: cancel,
|
|
interval: interval,
|
|
name: name,
|
|
url: url,
|
|
}
|
|
a.callers[name] = caller
|
|
go a.run(ctx, caller)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *Api) run(ctx context.Context, caller *caller) {
|
|
client := &rumblelivestreamlib.Client{StreamKey: caller.url}
|
|
for {
|
|
runtime.EventsEmit(a.ctx, "ApiActive-"+caller.name, true)
|
|
caller.displayMu.Lock()
|
|
if caller.display {
|
|
runtime.EventsEmit(a.ctx, "PageActive", true)
|
|
}
|
|
caller.displayMu.Unlock()
|
|
|
|
resp, err := a.query(client)
|
|
if err != nil {
|
|
a.logError.Println(pkgErr("error querying api", err))
|
|
// runtime.EventsEmit(a.ctx, "ApiActive-"+caller.name, false)
|
|
a.stop(caller)
|
|
return
|
|
}
|
|
caller.responseMu.Lock()
|
|
caller.response = resp
|
|
caller.responseMu.Unlock()
|
|
a.handleResponse(caller)
|
|
|
|
timer := time.NewTimer(caller.interval)
|
|
select {
|
|
case <-ctx.Done():
|
|
timer.Stop()
|
|
// runtime.EventsEmit(a.ctx, "ApiActive-"+caller.name, false)
|
|
a.stop(caller)
|
|
return
|
|
case <-timer.C:
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *Api) handleResponse(c *caller) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
|
|
c.responseMu.Lock()
|
|
defer c.responseMu.Unlock()
|
|
if c.response == nil {
|
|
return
|
|
}
|
|
|
|
c.displayMu.Lock()
|
|
if c.display {
|
|
runtime.EventsEmit(a.ctx, "PageActivity", c.response)
|
|
}
|
|
c.displayMu.Unlock()
|
|
|
|
isLive := len(c.response.Livestreams) > 0
|
|
runtime.EventsEmit(a.ctx, "PageLive-"+c.name, isLive)
|
|
}
|
|
|
|
func (a *Api) stop(c *caller) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
|
|
runtime.EventsEmit(a.ctx, "ApiActive-"+c.name, false)
|
|
c.displayMu.Lock()
|
|
if c.display {
|
|
c.display = false
|
|
runtime.EventsEmit(a.ctx, "PageActive", false)
|
|
}
|
|
c.displayMu.Unlock()
|
|
|
|
a.displayMu.Lock()
|
|
if a.display == c.name {
|
|
a.display = ""
|
|
}
|
|
a.displayMu.Unlock()
|
|
|
|
a.callersMu.Lock()
|
|
delete(a.callers, c.name)
|
|
a.callersMu.Unlock()
|
|
|
|
return
|
|
}
|
|
|
|
func (a *Api) Active(name string) bool {
|
|
a.callersMu.Lock()
|
|
defer a.callersMu.Unlock()
|
|
_, active := a.callers[name]
|
|
|
|
return active
|
|
}
|
|
|
|
func (a *Api) Stop(name string) error {
|
|
a.callersMu.Lock()
|
|
caller, exists := a.callers[name]
|
|
if !exists {
|
|
return pkgErr("", fmt.Errorf("caller does not exist: %s", name))
|
|
}
|
|
a.callersMu.Unlock()
|
|
|
|
caller.cancelMu.Lock()
|
|
if caller.cancel != nil {
|
|
caller.cancel()
|
|
}
|
|
caller.cancelMu.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *Api) query(client *rumblelivestreamlib.Client) (*rumblelivestreamlib.LivestreamResponse, error) {
|
|
resp, err := client.Request()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error executing client request: %v", err)
|
|
}
|
|
|
|
return resp, nil
|
|
}
|