141 lines
3.4 KiB
Go
141 lines
3.4 KiB
Go
package internal
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// ────────────────────────────────
|
|
// API DATA TYPES
|
|
// ────────────────────────────────
|
|
|
|
type Client struct {
|
|
base string
|
|
http *http.Client
|
|
}
|
|
|
|
func NewClient(base string, timeout time.Duration) *Client {
|
|
return &Client{
|
|
base: base,
|
|
http: &http.Client{Timeout: timeout},
|
|
}
|
|
}
|
|
|
|
func BaseURLFromEnv() string {
|
|
val := strings.TrimSpace(os.Getenv("STREAMED_BASE"))
|
|
if val == "" {
|
|
val = "https://streamed.pk"
|
|
}
|
|
return strings.TrimRight(val, "/")
|
|
}
|
|
|
|
type Sport struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
type Team struct {
|
|
Name string `json:"name"`
|
|
Badge string `json:"badge"`
|
|
}
|
|
|
|
type Teams struct {
|
|
Home *Team `json:"home"`
|
|
Away *Team `json:"away"`
|
|
}
|
|
|
|
type Match struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Category string `json:"category"`
|
|
Date int64 `json:"date"`
|
|
Poster string `json:"poster"`
|
|
Popular bool `json:"popular"`
|
|
Teams *Teams `json:"teams"`
|
|
Sources []struct {
|
|
Source string `json:"source"`
|
|
ID string `json:"id"`
|
|
} `json:"sources"`
|
|
}
|
|
|
|
type Stream struct {
|
|
ID string `json:"id"`
|
|
StreamNo int `json:"streamNo"`
|
|
Language string `json:"language"`
|
|
HD bool `json:"hd"`
|
|
EmbedURL string `json:"embedUrl"`
|
|
Source string `json:"source"`
|
|
Viewers int `json:"viewers"`
|
|
}
|
|
|
|
// ────────────────────────────────
|
|
// API CLIENT
|
|
// ────────────────────────────────
|
|
|
|
func (c *Client) GetSports(ctx context.Context) ([]Sport, error) {
|
|
url := c.base + "/api/sports"
|
|
var out []Sport
|
|
if err := c.get(ctx, url, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (c *Client) GetPopularMatches(ctx context.Context) ([]Match, error) {
|
|
url := c.base + "/api/matches/all/popular"
|
|
return c.getMatches(ctx, url)
|
|
}
|
|
|
|
func (c *Client) GetMatchesBySport(ctx context.Context, sportID string) ([]Match, error) {
|
|
url := fmt.Sprintf("%s/api/matches/%s", c.base, sportID)
|
|
return c.getMatches(ctx, url)
|
|
}
|
|
|
|
func (c *Client) GetStreamsForMatch(ctx context.Context, mt Match) ([]Stream, error) {
|
|
var all []Stream
|
|
for _, src := range mt.Sources {
|
|
url := fmt.Sprintf("%s/api/stream/%s/%s", c.base, src.Source, src.ID)
|
|
var list []Stream
|
|
if err := c.get(ctx, url, &list); err != nil {
|
|
return nil, err
|
|
}
|
|
all = append(all, list...)
|
|
}
|
|
return all, nil
|
|
}
|
|
|
|
func (c *Client) getMatches(ctx context.Context, url string) ([]Match, error) {
|
|
var out []Match
|
|
if err := c.get(ctx, url, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
sort.Slice(out, func(i, j int) bool { return out[i].Date < out[j].Date })
|
|
return out, nil
|
|
}
|
|
|
|
func (c *Client) get(ctx context.Context, url string, v any) error {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Set("User-Agent", "StreamedTUI/1.0 (+https://github.com/Salastil/streamed-tui)")
|
|
req.Header.Set("Accept", "application/json")
|
|
|
|
resp, err := c.http.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
|
return fmt.Errorf("GET %s: %s", url, resp.Status)
|
|
}
|
|
return json.NewDecoder(resp.Body).Decode(v)
|
|
}
|