Use official core API client

This commit is contained in:
Ingo Oppermann 2023-05-12 17:56:49 +02:00
parent adf43eaf01
commit 958b1232db
No known key found for this signature in database
GPG Key ID: 2AB32426E9DD229E
59 changed files with 2477 additions and 496 deletions

View File

@ -1,38 +0,0 @@
package client
import (
"bytes"
"encoding/json"
"github.com/datarhei/core/v16/http/api"
)
func (r *restclient) Config() (api.Config, error) {
var config api.Config
data, err := r.call("GET", "/config", "", nil)
if err != nil {
return config, err
}
err = json.Unmarshal(data, &config)
return config, err
}
func (r *restclient) ConfigSet(config api.ConfigData) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(config)
_, err := r.call("PUT", "/config", "application/json", &buf)
return err
}
func (r *restclient) ConfigReload() error {
_, err := r.call("GET", "/config/reload", "", nil)
return err
}

View File

@ -1,59 +0,0 @@
package client
import (
"encoding/json"
"io"
"net/url"
"github.com/datarhei/core/v16/http/api"
)
const (
SORT_DEFAULT = "none"
SORT_NONE = "none"
SORT_NAME = "name"
SORT_SIZE = "size"
SORT_LASTMOD = "lastmod"
ORDER_DEFAULT = "asc"
ORDER_ASC = "asc"
ORDER_DESC = "desc"
)
func (r *restclient) DiskFSList(sort, order string) ([]api.FileInfo, error) {
var files []api.FileInfo
values := url.Values{}
values.Set("sort", sort)
values.Set("order", order)
data, err := r.call("GET", "/fs/disk?"+values.Encode(), "", nil)
if err != nil {
return files, err
}
err = json.Unmarshal(data, &files)
return files, err
}
func (r *restclient) DiskFSHasFile(path string) bool {
_, err := r.call("HEAD", "/fs/disk"+path, "", nil)
return err == nil
}
func (r *restclient) DiskFSGetFile(path string) (io.ReadCloser, error) {
return r.stream("GET", "/fs/disk"+path, "", nil)
}
func (r *restclient) DiskFSDeleteFile(path string) error {
_, err := r.call("DELETE", "/fs/disk"+path, "", nil)
return err
}
func (r *restclient) DiskFSAddFile(path string, data io.Reader) error {
_, err := r.call("PUT", "/fs/disk"+path, "application/data", data)
return err
}

View File

@ -1,48 +0,0 @@
package client
import (
"encoding/json"
"io"
"net/url"
"github.com/datarhei/core/v16/http/api"
)
func (r *restclient) MemFSList(sort, order string) ([]api.FileInfo, error) {
var files []api.FileInfo
values := url.Values{}
values.Set("sort", sort)
values.Set("order", order)
data, err := r.call("GET", "/fs/mem?"+values.Encode(), "", nil)
if err != nil {
return files, err
}
err = json.Unmarshal(data, &files)
return files, err
}
func (r *restclient) MemFSHasFile(path string) bool {
_, err := r.call("HEAD", "/fs/mem"+path, "", nil)
return err == nil
}
func (r *restclient) MemFSGetFile(path string) (io.ReadCloser, error) {
return r.stream("GET", "/fs/mem"+path, "", nil)
}
func (r *restclient) MemFSDeleteFile(path string) error {
_, err := r.call("DELETE", "/fs/mem"+path, "", nil)
return err
}
func (r *restclient) MemFSAddFile(path string, data io.Reader) error {
_, err := r.call("PUT", "/fs/mem"+path, "application/data", data)
return err
}

View File

@ -1,40 +0,0 @@
package client
import (
"bytes"
"encoding/json"
"github.com/datarhei/core/v16/http/api"
)
func (r *restclient) Metadata(id, key string) (api.Metadata, error) {
var m api.Metadata
path := "/process/" + id + "/metadata"
if len(key) != 0 {
path += "/" + key
}
data, err := r.call("GET", path, "", nil)
if err != nil {
return m, err
}
err = json.Unmarshal(data, &m)
return m, err
}
func (r *restclient) MetadataSet(id, key string, metadata api.Metadata) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(metadata)
_, err := r.call("PUT", "/process/"+id+"/metadata/"+key, "application/json", &buf)
if err != nil {
return err
}
return nil
}

View File

@ -1,25 +0,0 @@
package client
import (
"bytes"
"encoding/json"
"github.com/datarhei/core/v16/http/api"
)
func (r *restclient) Metrics(query api.MetricsQuery) (api.MetricsResponse, error) {
var m api.MetricsResponse
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(query)
data, err := r.call("POST", "/metrics", "application/json", &buf)
if err != nil {
return m, err
}
err = json.Unmarshal(data, &m)
return m, err
}

View File

@ -12,8 +12,8 @@ import (
"sync"
"time"
"github.com/datarhei/core/v16/client"
httpapi "github.com/datarhei/core/v16/http/api"
client "github.com/datarhei/core-client-go/v16"
clientapi "github.com/datarhei/core-client-go/v16/api"
"github.com/datarhei/core/v16/restream/app"
)
@ -172,20 +172,29 @@ func (n *node) Connect() error {
return fmt.Errorf("creating client failed (%s): %w", n.address, err)
}
config, err := peer.Config()
version, cfg, err := peer.Config()
if err != nil {
return err
}
if config.Config.RTMP.Enable {
if version != 3 {
return fmt.Errorf("unsupported core config version: %d", version)
}
config, ok := cfg.Config.(clientapi.ConfigV3)
if !ok {
return fmt.Errorf("failed to convert config to expected version")
}
if config.RTMP.Enable {
n.hasRTMP = true
n.rtmpAddress = "rtmp://"
isHostIP := net.ParseIP(host) != nil
address := config.Config.RTMP.Address
if n.secure && config.Config.RTMP.EnableTLS && !isHostIP {
address = config.Config.RTMP.AddressTLS
address := config.RTMP.Address
if n.secure && config.RTMP.EnableTLS && !isHostIP {
address = config.RTMP.AddressTLS
n.rtmpAddress = "rtmps://"
}
@ -194,21 +203,21 @@ func (n *node) Connect() error {
n.hasRTMP = false
} else {
n.rtmpAddress += host + ":" + port
n.rtmpToken = config.Config.RTMP.Token
n.rtmpToken = config.RTMP.Token
}
}
if config.Config.SRT.Enable {
if config.SRT.Enable {
n.hasSRT = true
n.srtAddress = "srt://"
_, port, err := net.SplitHostPort(config.Config.SRT.Address)
_, port, err := net.SplitHostPort(config.SRT.Address)
if err != nil {
n.hasSRT = false
} else {
n.srtAddress += host + ":" + port
n.srtPassphrase = config.Config.SRT.Passphrase
n.srtToken = config.Config.SRT.Token
n.srtPassphrase = config.SRT.Passphrase
n.srtToken = config.SRT.Token
}
}
@ -253,8 +262,8 @@ func (n *node) Connect() error {
select {
case <-ticker.C:
// Metrics
metrics, err := n.peer.Metrics(httpapi.MetricsQuery{
Metrics: []httpapi.MetricsQueryMetric{
metrics, err := n.peer.Metrics(clientapi.MetricsQuery{
Metrics: []clientapi.MetricsQueryMetric{
{
Name: "cpu_ncpu",
},
@ -655,9 +664,11 @@ func (n *node) ProcessList() ([]Process, error) {
return nil, fmt.Errorf("not connected")
}
list, err := n.peer.ProcessList(nil, []string{
"state",
"config",
list, err := n.peer.ProcessList(client.ProcessListOptions{
Filter: []string{
"state",
"config",
},
})
if err != nil {
return nil, err
@ -671,17 +682,56 @@ func (n *node) ProcessList() ([]Process, error) {
Order: p.State.Order,
State: p.State.State,
Mem: p.State.Memory,
CPU: p.State.CPU * n.resources.ncpu,
Runtime: time.Duration(p.State.Runtime) * time.Second,
UpdatedAt: time.Unix(p.UpdatedAt, 0),
Config: p.Config.Marshal(),
}
if x, err := p.State.CPU.Float64(); err == nil {
process.CPU = x * n.resources.ncpu
} else {
process.CPU = 100 * n.resources.ncpu
cfg := &app.Config{
ID: p.Config.ID,
Reference: p.Config.Reference,
Input: []app.ConfigIO{},
Output: []app.ConfigIO{},
Options: p.Config.Options,
Reconnect: p.Config.Reconnect,
ReconnectDelay: p.Config.ReconnectDelay,
Autostart: p.Config.Autostart,
StaleTimeout: p.Config.StaleTimeout,
LimitCPU: p.Config.Limits.CPU,
LimitMemory: p.Config.Limits.Memory,
LimitWaitFor: p.Config.Limits.WaitFor,
}
for _, d := range p.Config.Input {
cfg.Input = append(cfg.Input, app.ConfigIO{
ID: d.ID,
Address: d.Address,
Options: d.Options,
})
}
for _, d := range p.Config.Output {
output := app.ConfigIO{
ID: d.ID,
Address: d.Address,
Options: d.Options,
Cleanup: []app.ConfigIOCleanup{},
}
for _, c := range d.Cleanup {
output.Cleanup = append(output.Cleanup, app.ConfigIOCleanup{
Pattern: c.Pattern,
MaxFiles: c.MaxFiles,
MaxFileAge: c.MaxFileAge,
PurgeOnDelete: c.PurgeOnDelete,
})
}
cfg.Output = append(cfg.Output, output)
}
process.Config = cfg
processes = append(processes, process)
}
@ -696,8 +746,50 @@ func (n *node) ProcessAdd(config *app.Config) error {
return fmt.Errorf("not connected")
}
cfg := httpapi.ProcessConfig{}
cfg.Unmarshal(config)
cfg := clientapi.ProcessConfig{
ID: config.ID,
Reference: config.Reference,
Input: []clientapi.ProcessConfigIO{},
Output: []clientapi.ProcessConfigIO{},
Options: config.Options,
Reconnect: config.Reconnect,
ReconnectDelay: config.ReconnectDelay,
Autostart: config.Autostart,
StaleTimeout: config.StaleTimeout,
Limits: clientapi.ProcessConfigLimits{
CPU: config.LimitCPU,
Memory: config.LimitMemory,
WaitFor: config.LimitWaitFor,
},
}
for _, d := range config.Input {
cfg.Input = append(cfg.Input, clientapi.ProcessConfigIO{
ID: d.ID,
Address: d.Address,
Options: d.Options,
})
}
for _, d := range config.Output {
output := clientapi.ProcessConfigIO{
ID: d.ID,
Address: d.Address,
Options: d.Options,
Cleanup: []clientapi.ProcessConfigIOCleanup{},
}
for _, c := range d.Cleanup {
output.Cleanup = append(output.Cleanup, clientapi.ProcessConfigIOCleanup{
Pattern: c.Pattern,
MaxFiles: c.MaxFiles,
MaxFileAge: c.MaxFileAge,
PurgeOnDelete: c.PurgeOnDelete,
})
}
cfg.Output = append(cfg.Output, output)
}
return n.peer.ProcessAdd(cfg)
}

5
go.mod
View File

@ -4,9 +4,10 @@ go 1.18
require (
github.com/99designs/gqlgen v0.17.20
github.com/Masterminds/semver/v3 v3.1.1
github.com/Masterminds/semver/v3 v3.2.1
github.com/atrox/haikunatorgo/v2 v2.0.1
github.com/caddyserver/certmagic v0.17.2
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230512155342-18a7ac72df3a
github.com/datarhei/gosrt v0.3.1
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a
github.com/go-playground/validator/v10 v10.11.1
@ -35,6 +36,8 @@ require (
golang.org/x/mod v0.7.0
)
//replace github.com/datarhei/core-client-go/v16 => ../core-client-go
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect

6
go.sum
View File

@ -6,8 +6,8 @@ github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/agiledragon/gomonkey/v2 v2.3.1/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
@ -48,6 +48,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230512155342-18a7ac72df3a h1:GFT9alzx9UXytQ+lo3MBuPLqB8HsVE2jNqhu+UpAxaY=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230512155342-18a7ac72df3a/go.mod h1:2eAeJtBPTyiI+9uhGcCEHZqATBt9J06Bb7Fbxj07lw4=
github.com/datarhei/gosrt v0.3.1 h1:9A75hIvnY74IUFyeguqYXh1lsGF8Qt8fjxJS2Ewr12Q=
github.com/datarhei/gosrt v0.3.1/go.mod h1:M2nl2WPrawncUc1FtUBK6gZX4tpZRC7FqL8NjOdBZV0=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo=

View File

@ -4,23 +4,24 @@ run:
linters:
disable-all: true
enable:
- deadcode
- dupl
- errcheck
- gofmt
- goimports
- golint
- gosimple
- govet
- ineffassign
- misspell
- govet
- staticcheck
- errcheck
- unparam
- ineffassign
- nakedret
- structcheck
- gocyclo
- dupl
- goimports
- revive
- gosec
- gosimple
- typecheck
- unused
- varcheck
linters-settings:
gofmt:
simplify: true
dupl:
threshold: 400
threshold: 600

View File

@ -1,5 +1,25 @@
# Changelog
## 3.2.0 (2022-11-28)
### Added
- #190: Added text marshaling and unmarshaling
- #167: Added JSON marshalling for constraints (thanks @SimonTheLeg)
- #173: Implement encoding.TextMarshaler and encoding.TextUnmarshaler on Version (thanks @MarkRosemaker)
- #179: Added New() version constructor (thanks @kazhuravlev)
### Changed
- #182/#183: Updated CI testing setup
### Fixed
- #186: Fixing issue where validation of constraint section gave false positives
- #176: Fix constraints check with *-0 (thanks @mtt0)
- #181: Fixed Caret operator (^) gives unexpected results when the minor version in constraint is 0 (thanks @arshchimni)
- #161: Fixed godoc (thanks @afirth)
## 3.1.1 (2020-11-23)
### Fixed

View File

@ -1,7 +1,5 @@
GOPATH=$(shell go env GOPATH)
GOLANGCI_LINT=$(GOPATH)/bin/golangci-lint
GOFUZZBUILD = $(GOPATH)/bin/go-fuzz-build
GOFUZZ = $(GOPATH)/bin/go-fuzz
.PHONY: lint
lint: $(GOLANGCI_LINT)
@ -19,19 +17,14 @@ test-cover:
GO111MODULE=on go test -cover .
.PHONY: fuzz
fuzz: $(GOFUZZBUILD) $(GOFUZZ)
@echo "==> Fuzz testing"
$(GOFUZZBUILD)
$(GOFUZZ) -workdir=_fuzz
fuzz:
@echo "==> Running Fuzz Tests"
go test -fuzz=FuzzNewVersion -fuzztime=15s .
go test -fuzz=FuzzStrictNewVersion -fuzztime=15s .
go test -fuzz=FuzzNewConstraint -fuzztime=15s .
$(GOLANGCI_LINT):
# Install golangci-lint. The configuration for it is in the .golangci.yml
# file in the root of the repository
echo ${GOPATH}
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.17.1
$(GOFUZZBUILD):
cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz-build
$(GOFUZZ):
cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-dep

View File

@ -18,18 +18,20 @@ If you are looking for a command line tool for version comparisons please see
## Package Versions
Note, import `github.com/github.com/Masterminds/semver/v3` to use the latest version.
There are three major versions fo the `semver` package.
* 3.x.x is the new stable and active version. This version is focused on constraint
* 3.x.x is the stable and active version. This version is focused on constraint
compatibility for range handling in other tools from other languages. It has
a similar API to the v1 releases. The development of this version is on the master
branch. The documentation for this version is below.
* 2.x was developed primarily for [dep](https://github.com/golang/dep). There are
no tagged releases and the development was performed by [@sdboyer](https://github.com/sdboyer).
There are API breaking changes from v1. This version lives on the [2.x branch](https://github.com/Masterminds/semver/tree/2.x).
* 1.x.x is the most widely used version with numerous tagged releases. This is the
previous stable and is still maintained for bug fixes. The development, to fix
bugs, occurs on the release-1 branch. You can read the documentation [here](https://github.com/Masterminds/semver/blob/release-1/README.md).
* 1.x.x is the original release. It is no longer maintained. You should use the
v3 release instead. You can read the documentation for the 1.x.x release
[here](https://github.com/Masterminds/semver/blob/release-1/README.md).
## Parsing Semantic Versions
@ -242,3 +244,15 @@ for _, m := range msgs {
If you find an issue or want to contribute please file an [issue](https://github.com/Masterminds/semver/issues)
or [create a pull request](https://github.com/Masterminds/semver/pulls).
## Security
Security is an important consideration for this project. The project currently
uses the following tools to help discover security issues:
* [CodeQL](https://github.com/Masterminds/semver)
* [gosec](https://github.com/securego/gosec)
* Daily Fuzz testing
If you believe you have found a security vulnerability you can privately disclose
it through the [GitHub security page](https://github.com/Masterminds/semver/security).

19
vendor/github.com/Masterminds/semver/v3/SECURITY.md generated vendored Normal file
View File

@ -0,0 +1,19 @@
# Security Policy
## Supported Versions
The following versions of semver are currently supported:
| Version | Supported |
| ------- | ------------------ |
| 3.x | :white_check_mark: |
| 2.x | :x: |
| 1.x | :x: |
Fixes are only released for the latest minor version in the form of a patch release.
## Reporting a Vulnerability
You can privately disclose a vulnerability through GitHubs
[private vulnerability reporting](https://github.com/Masterminds/semver/security/advisories)
mechanism.

View File

@ -134,6 +134,23 @@ func (cs Constraints) String() string {
return strings.Join(buf, " || ")
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (cs *Constraints) UnmarshalText(text []byte) error {
temp, err := NewConstraint(string(text))
if err != nil {
return err
}
*cs = *temp
return nil
}
// MarshalText implements the encoding.TextMarshaler interface.
func (cs Constraints) MarshalText() ([]byte, error) {
return []byte(cs.String()), nil
}
var constraintOps map[string]cfunc
var constraintRegex *regexp.Regexp
var constraintRangeRegex *regexp.Regexp
@ -180,8 +197,13 @@ func init() {
ops,
cvRegex))
// The first time a constraint shows up will look slightly different from
// future times it shows up due to a leading space or comma in a given
// string.
validConstraintRegex = regexp.MustCompile(fmt.Sprintf(
`^(\s*(%s)\s*(%s)\s*\,?)+$`,
`^(\s*(%s)\s*(%s)\s*)((?:\s+|,\s*)(%s)\s*(%s)\s*)*$`,
ops,
cvRegex,
ops,
cvRegex))
}
@ -233,7 +255,7 @@ func parseConstraint(c string) (*constraint, error) {
patchDirty := false
dirty := false
if isX(m[3]) || m[3] == "" {
ver = "0.0.0"
ver = fmt.Sprintf("0.0.0%s", m[6])
dirty = true
} else if isX(strings.TrimPrefix(m[4], ".")) || m[4] == "" {
minorDirty = true
@ -534,6 +556,10 @@ func constraintCaret(v *Version, c *constraint) (bool, error) {
}
return false, fmt.Errorf("%s does not have same minor version as %s. Expected minor versions to match when constraint major version is 0", v, c.orig)
}
// ^ when the minor is 0 and minor > 0 is =0.0.z
if c.con.Minor() == 0 && v.Minor() > 0 {
return false, fmt.Errorf("%s does not have same minor version as %s", v, c.orig)
}
// At this point the major is 0 and the minor is 0 and not dirty. The patch
// is not dirty so we need to check if they are equal. If they are not equal
@ -560,7 +586,7 @@ func rewriteRange(i string) string {
}
o := i
for _, v := range m {
t := fmt.Sprintf(">= %s, <= %s", v[1], v[11])
t := fmt.Sprintf(">= %s, <= %s ", v[1], v[11])
o = strings.Replace(o, v[0], t, 1)
}

View File

@ -3,12 +3,12 @@ Package semver provides the ability to work with Semantic Versions (http://semve
Specifically it provides the ability to:
* Parse semantic versions
* Sort semantic versions
* Check if a semantic version fits within a set of constraints
* Optionally work with a `v` prefix
- Parse semantic versions
- Sort semantic versions
- Check if a semantic version fits within a set of constraints
- Optionally work with a `v` prefix
Parsing Semantic Versions
# Parsing Semantic Versions
There are two functions that can parse semantic versions. The `StrictNewVersion`
function only parses valid version 2 semantic versions as outlined in the
@ -21,48 +21,48 @@ that can be sorted, compared, and used in constraints.
When parsing a version an optional error can be returned if there is an issue
parsing the version. For example,
v, err := semver.NewVersion("1.2.3-beta.1+b345")
v, err := semver.NewVersion("1.2.3-beta.1+b345")
The version object has methods to get the parts of the version, compare it to
other versions, convert the version back into a string, and get the original
string. For more details please see the documentation
at https://godoc.org/github.com/Masterminds/semver.
Sorting Semantic Versions
# Sorting Semantic Versions
A set of versions can be sorted using the `sort` package from the standard library.
For example,
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
vs := make([]*semver.Version, len(raw))
for i, r := range raw {
v, err := semver.NewVersion(r)
if err != nil {
t.Errorf("Error parsing version: %s", err)
raw := []string{"1.2.3", "1.0", "1.3", "2", "0.4.2",}
vs := make([]*semver.Version, len(raw))
for i, r := range raw {
v, err := semver.NewVersion(r)
if err != nil {
t.Errorf("Error parsing version: %s", err)
}
vs[i] = v
}
vs[i] = v
}
sort.Sort(semver.Collection(vs))
sort.Sort(semver.Collection(vs))
Checking Version Constraints and Comparing Versions
# Checking Version Constraints and Comparing Versions
There are two methods for comparing versions. One uses comparison methods on
`Version` instances and the other is using Constraints. There are some important
differences to notes between these two methods of comparison.
1. When two versions are compared using functions such as `Compare`, `LessThan`,
and others it will follow the specification and always include prereleases
within the comparison. It will provide an answer valid with the comparison
spec section at https://semver.org/#spec-item-11
2. When constraint checking is used for checks or validation it will follow a
different set of rules that are common for ranges with tools like npm/js
and Rust/Cargo. This includes considering prereleases to be invalid if the
ranges does not include on. If you want to have it include pre-releases a
simple solution is to include `-0` in your range.
3. Constraint ranges can have some complex rules including the shorthard use of
~ and ^. For more details on those see the options below.
1. When two versions are compared using functions such as `Compare`, `LessThan`,
and others it will follow the specification and always include prereleases
within the comparison. It will provide an answer valid with the comparison
spec section at https://semver.org/#spec-item-11
2. When constraint checking is used for checks or validation it will follow a
different set of rules that are common for ranges with tools like npm/js
and Rust/Cargo. This includes considering prereleases to be invalid if the
ranges does not include on. If you want to have it include pre-releases a
simple solution is to include `-0` in your range.
3. Constraint ranges can have some complex rules including the shorthard use of
~ and ^. For more details on those see the options below.
There are differences between the two methods or checking versions because the
comparison methods on `Version` follow the specification while comparison ranges
@ -76,19 +76,19 @@ patters with their versions.
Checking a version against version constraints is one of the most featureful
parts of the package.
c, err := semver.NewConstraint(">= 1.2.3")
if err != nil {
// Handle constraint not being parsable.
}
c, err := semver.NewConstraint(">= 1.2.3")
if err != nil {
// Handle constraint not being parsable.
}
v, err := semver.NewVersion("1.3")
if err != nil {
// Handle version not being parsable.
}
// Check if the version meets the constraints. The a variable will be true.
a := c.Check(v)
v, err := semver.NewVersion("1.3")
if err != nil {
// Handle version not being parsable.
}
// Check if the version meets the constraints. The a variable will be true.
a := c.Check(v)
Basic Comparisons
# Basic Comparisons
There are two elements to the comparisons. First, a comparison string is a list
of comma or space separated AND comparisons. These are then separated by || (OR)
@ -99,31 +99,31 @@ greater than or equal to 4.2.3. This can also be written as
The basic comparisons are:
* `=`: equal (aliased to no operator)
* `!=`: not equal
* `>`: greater than
* `<`: less than
* `>=`: greater than or equal to
* `<=`: less than or equal to
- `=`: equal (aliased to no operator)
- `!=`: not equal
- `>`: greater than
- `<`: less than
- `>=`: greater than or equal to
- `<=`: less than or equal to
Hyphen Range Comparisons
# Hyphen Range Comparisons
There are multiple methods to handle ranges and the first is hyphens ranges.
These look like:
* `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
* `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5`
- `1.2 - 1.4.5` which is equivalent to `>= 1.2, <= 1.4.5`
- `2.3.4 - 4.5` which is equivalent to `>= 2.3.4 <= 4.5`
Wildcards In Comparisons
# Wildcards In Comparisons
The `x`, `X`, and `*` characters can be used as a wildcard character. This works
for all comparison operators. When used on the `=` operator it falls
back to the tilde operation. For example,
* `1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
* `>= 1.2.x` is equivalent to `>= 1.2.0`
* `<= 2.x` is equivalent to `<= 3`
* `*` is equivalent to `>= 0.0.0`
- `1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
- `>= 1.2.x` is equivalent to `>= 1.2.0`
- `<= 2.x` is equivalent to `<= 3`
- `*` is equivalent to `>= 0.0.0`
Tilde Range Comparisons (Patch)
@ -131,11 +131,11 @@ The tilde (`~`) comparison operator is for patch level ranges when a minor
version is specified and major level changes when the minor number is missing.
For example,
* `~1.2.3` is equivalent to `>= 1.2.3 < 1.3.0`
* `~1` is equivalent to `>= 1, < 2`
* `~2.3` is equivalent to `>= 2.3 < 2.4`
* `~1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
* `~1.x` is equivalent to `>= 1 < 2`
- `~1.2.3` is equivalent to `>= 1.2.3 < 1.3.0`
- `~1` is equivalent to `>= 1, < 2`
- `~2.3` is equivalent to `>= 2.3 < 2.4`
- `~1.2.x` is equivalent to `>= 1.2.0 < 1.3.0`
- `~1.x` is equivalent to `>= 1 < 2`
Caret Range Comparisons (Major)
@ -144,41 +144,41 @@ The caret (`^`) comparison operator is for major level changes once a stable
as the API stability level. This is useful when comparisons of API versions as a
major change is API breaking. For example,
* `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
* `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
* `^2.3` is equivalent to `>= 2.3, < 3`
* `^2.x` is equivalent to `>= 2.0.0, < 3`
* `^0.2.3` is equivalent to `>=0.2.3 <0.3.0`
* `^0.2` is equivalent to `>=0.2.0 <0.3.0`
* `^0.0.3` is equivalent to `>=0.0.3 <0.0.4`
* `^0.0` is equivalent to `>=0.0.0 <0.1.0`
* `^0` is equivalent to `>=0.0.0 <1.0.0`
- `^1.2.3` is equivalent to `>= 1.2.3, < 2.0.0`
- `^1.2.x` is equivalent to `>= 1.2.0, < 2.0.0`
- `^2.3` is equivalent to `>= 2.3, < 3`
- `^2.x` is equivalent to `>= 2.0.0, < 3`
- `^0.2.3` is equivalent to `>=0.2.3 <0.3.0`
- `^0.2` is equivalent to `>=0.2.0 <0.3.0`
- `^0.0.3` is equivalent to `>=0.0.3 <0.0.4`
- `^0.0` is equivalent to `>=0.0.0 <0.1.0`
- `^0` is equivalent to `>=0.0.0 <1.0.0`
Validation
# Validation
In addition to testing a version against a constraint, a version can be validated
against a constraint. When validation fails a slice of errors containing why a
version didn't meet the constraint is returned. For example,
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
if err != nil {
// Handle constraint not being parseable.
}
c, err := semver.NewConstraint("<= 1.2.3, >= 1.4")
if err != nil {
// Handle constraint not being parseable.
}
v, _ := semver.NewVersion("1.3")
if err != nil {
// Handle version not being parseable.
}
v, _ := semver.NewVersion("1.3")
if err != nil {
// Handle version not being parseable.
}
// Validate a version against a constraint.
a, msgs := c.Validate(v)
// a is false
for _, m := range msgs {
fmt.Println(m)
// Validate a version against a constraint.
a, msgs := c.Validate(v)
// a is false
for _, m := range msgs {
fmt.Println(m)
// Loops over the errors which would read
// "1.3 is greater than 1.2.3"
// "1.3 is less than 1.4"
}
// Loops over the errors which would read
// "1.3 is greater than 1.2.3"
// "1.3 is less than 1.4"
}
*/
package semver

View File

@ -1,22 +0,0 @@
// +build gofuzz
package semver
func Fuzz(data []byte) int {
d := string(data)
// Test NewVersion
_, _ = NewVersion(d)
// Test StrictNewVersion
_, _ = StrictNewVersion(d)
// Test NewConstraint
_, _ = NewConstraint(d)
// The return value should be 0 normally, 1 if the priority in future tests
// should be increased, and -1 if future tests should skip passing in that
// data. We do not have a reason to change priority so 0 is always returned.
// There are example tests that do this.
return 0
}

View File

@ -55,14 +55,16 @@ func init() {
versionRegex = regexp.MustCompile("^" + semVerRegex + "$")
}
const num string = "0123456789"
const allowed string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + num
const (
num string = "0123456789"
allowed string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-" + num
)
// StrictNewVersion parses a given version and returns an instance of Version or
// an error if unable to parse the version. Only parses valid semantic versions.
// Performs checking that can find errors within the version.
// If you want to coerce a version, such as 1 or 1.2, and perse that as the 1.x
// releases of semver provided use the NewSemver() function.
// If you want to coerce a version such as 1 or 1.2 and parse it as the 1.x
// releases of semver did, use the NewVersion() function.
func StrictNewVersion(v string) (*Version, error) {
// Parsing here does not use RegEx in order to increase performance and reduce
// allocations.
@ -207,6 +209,23 @@ func NewVersion(v string) (*Version, error) {
return sv, nil
}
// New creates a new instance of Version with each of the parts passed in as
// arguments instead of parsing a version string.
func New(major, minor, patch uint64, pre, metadata string) *Version {
v := Version{
major: major,
minor: minor,
patch: patch,
pre: pre,
metadata: metadata,
original: "",
}
v.original = v.String()
return &v
}
// MustParse parses a given version and panics on error.
func MustParse(v string) *Version {
sv, err := NewVersion(v)
@ -267,7 +286,6 @@ func (v Version) Metadata() string {
// originalVPrefix returns the original 'v' prefix if any.
func (v Version) originalVPrefix() string {
// Note, only lowercase v is supported as a prefix by the parser.
if v.original != "" && v.original[:1] == "v" {
return v.original[:1]
@ -436,6 +454,23 @@ func (v Version) MarshalJSON() ([]byte, error) {
return json.Marshal(v.String())
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (v *Version) UnmarshalText(text []byte) error {
temp, err := NewVersion(string(text))
if err != nil {
return err
}
*v = *temp
return nil
}
// MarshalText implements the encoding.TextMarshaler interface.
func (v Version) MarshalText() ([]byte, error) {
return []byte(v.String()), nil
}
// Scan implements the SQL.Scanner interface.
func (v *Version) Scan(value interface{}) error {
var s string
@ -470,7 +505,6 @@ func compareSegment(v, o uint64) int {
}
func comparePrerelease(v, o string) int {
// split the prelease versions by their part. The separator, per the spec,
// is a .
sparts := strings.Split(v, ".")
@ -562,7 +596,6 @@ func comparePrePart(s, o string) int {
return 1
}
return -1
}
// Like strings.ContainsAny but does an only instead of any.

21
vendor/github.com/datarhei/core-client-go/v16/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 FOSS GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

312
vendor/github.com/datarhei/core-client-go/v16/README.md generated vendored Normal file
View File

@ -0,0 +1,312 @@
# core-client-go
A golang client for the `github.com/datarhei/core` API.
---
- [Quick Start](#quick-start)
- [API definitions](#api-definitions)
- [General](#general)
- [Config](#config)
- [Disk filesystem](#disk-filesystem)
- [In-memory filesystem](#in-memory-filesystem)
- [Log](#log)
- [Metadata](#metadata)
- [Metrics](#metrics)
- [Process](#process)
- [RTMP](#rtmp)
- [Session](#session)
- [Skills](#skills)
- [Versioning](#versioning)
- [Contributing](#contributing)
- [Licence](#licence)
## Quick Start
Example for retrieving a list of all processes:
```
import "github.com/datarhei/core-client-go/v16"
client, err := coreclient.New(coreclient.Config{
Address: "https://example.com:8080",
Username: "foo",
Password: "bar",
})
if err != nil {
...
}
processes, err := client.ProcessList(coreclient.ProcessListOptions{})
if err != nil {
...
}
```
## API definitions
### General
- `GET` /api
```golang
About() api.About
```
### Config
- `GET` /api/v3/config
```golang
Config() (api.Config, error)
```
- `PUT` /api/v3/config
```golang
ConfigSet(config api.ConfigSet) error
```
- `GET` /api/v3/config/reload
```golang
ConfigReload() error
```
### Disk filesystem
- `GET` /api/v3/fs/disk
```golang
DiskFSList(sort, order string) ([]api.FileInfo, error)
```
- `HEAD` /api/v3/fs/disk/{path}
```golang
DiskFSHasFile(path string) bool
```
- `GET` /api/v3/fs/disk/{path}
```golang
DiskFSGetFile(path string) (io.ReadCloser, error)
```
- `DELETE` /api/v3/fs/disk/{path}
```golang
DiskFSDeleteFile(path string) error
```
- `PUT` /api/v3/fs/disk/{path}
```golang
DiskFSAddFile(path string, data io.Reader) error
```
### In-memory filesystem
- `GET` /api/v3/fs/mem
```golang
MemFSList(sort, order string) ([]api.FileInfo, error)
```
- `HEAD` /api/v3/fs/mem/{path}
```golang
MemFSHasFile(path string) bool
```
- `GET` /api/v3/fs/mem/{path}
```golang
MemFSGetFile(path string) (io.ReadCloser, error)
```
- `DELETE` /api/v3/fs/mem/{path}
```golang
MemFSDeleteFile(path string) error
```
- `PUT` /api/v3/fs/mem/{path}
```golang
MemFSAddFile(path string, data io.Reader) error
```
### Log
- `GET` /api/v3/log
```golang
Log() ([]api.LogEvent, error)
```
### Metadata
- `GET` /api/v3/metadata/{key}
```golang
Metadata(id, key string) (api.Metadata, error)
```
- `PUT` /api/v3/metadata/{key}
```golang
MetadataSet(id, key string, metadata api.Metadata) error
```
### Metrics
- `GET` /api/v3/metrics
```golang
MetricsList() ([]api.MetricsDescription, error)
```
- `POST` /api/v3/metrics
```golang
Metrics(query api.MetricsQuery) (api.MetricsResponse, error)
```
### Process
- `GET` /api/v3/process
```golang
ProcessList(opts ProcessListOptions) ([]api.Process, error)
```
- `POST` /api/v3/process
```golang
ProcessAdd(p api.ProcessConfig) error
```
- `GET` /api/v3/process/{id}
```golang
Process(id string, filter []string) (api.Process, error)
```
- `PUT` /api/v3/process/{id}
```golang
ProcessUpdate(id string, p api.ProcessConfig) error
```
- `DELETE` /api/v3/process/{id}
```golang
ProcessDelete(id string) error
```
- `PUT` /api/v3/process/{id}/command
```golang
ProcessCommand(id, command string) error
```
- `GET` /api/v3/process/{id}/probe
```golang
ProcessProbe(id string) (api.Probe, error)
```
- `GET` /api/v3/process/{id}/config
```golang
ProcessConfig(id string) (api.ProcessConfig, error)
```
- `GET` /api/v3/process/{id}/report
```golang
ProcessReport(id string) (api.ProcessReport, error)
```
- `GET` /api/v3/process/{id}/state
```golang
ProcessState(id string) (api.ProcessState, error)
```
- `GET` /api/v3/process/{id}/metadata/{key}
```golang
ProcessMetadata(id, key string) (api.Metadata, error)
```
- `PUT` /api/v3/process/{id}/metadata/{key}
```golang
ProcessMetadataSet(id, key string, metadata api.Metadata) error
```
### RTMP
- `GET` /api/v3/rtmp
```golang
RTMPChannels() ([]api.RTMPChannel, error)
```
### SRT
- `GET` /api/v3/srt
```golang
SRTChannels() (api.SRTChannels, error)
```
### Session
- `GET` /api/v3/session
```golang
Sessions(collectors []string) (api.SessionsSummary, error)
```
- `GET` /api/v3/session/active
```golang
SessionsActive(collectors []string) (api.SessionsActive, error)
```
### Skills
- `GET` /api/v3/skills
```golang
Skills() (api.Skills, error)
```
- `GET` /api/v3/skills/reload
```golang
SkillsReload() error
```
### Widget
- `GET` /api/v3/widget
```golang
WidgetProcess(id string) (api.WidgetProcess, error)
```
## Versioning
The version of this module is according to which version of the datarhei Core API
you want to connect to. Check the branches to find out which other versions are
implemented. If you want to connect to an API version 12, you have to import the client
module of the version 12, i.e. `import "github.com/datarhei/core-client-go/v12"`.
The latest implementation is on the `main` branch.
## Contributing
Found a mistake or misconduct? Create a [issue](https://github.com/datarhei/core-client-go/issues) or send a pull-request.
Suggestions for improvement are welcome.
## Licence
[MIT](https://github.com/datarhei/core-client-go/blob/main/LICENSE)

View File

@ -0,0 +1,33 @@
package api
// About is some general information about the API
type About struct {
App string `json:"app"`
Auths []string `json:"auths"`
Name string `json:"name"`
ID string `json:"id"`
CreatedAt string `json:"created_at"`
Uptime uint64 `json:"uptime_seconds"`
Version Version `json:"version"`
}
// Version is some information about the binary
type Version struct {
Number string `json:"number"`
Commit string `json:"repository_commit"`
Branch string `json:"repository_branch"`
Build string `json:"build_date"`
Arch string `json:"arch"`
Compiler string `json:"compiler"`
}
// MinimalAbout is the minimal information about the API
type MinimalAbout struct {
App string `json:"app"`
Auths []string `json:"auths"`
Version VersionMinimal `json:"version"`
}
type VersionMinimal struct {
Number string `json:"number"`
}

View File

@ -0,0 +1,22 @@
package api
type AVstreamIO struct {
State string `json:"state" enums:"running,idle" jsonschema:"enum=running,enum=idle"`
Packet uint64 `json:"packet" format:"uint64"`
Time uint64 `json:"time"`
Size uint64 `json:"size_kb"`
}
type AVstream struct {
Input AVstreamIO `json:"input"`
Output AVstreamIO `json:"output"`
Aqueue uint64 `json:"aqueue" format:"uint64"`
Queue uint64 `json:"queue" format:"uint64"`
Dup uint64 `json:"dup" format:"uint64"`
Drop uint64 `json:"drop" format:"uint64"`
Enc uint64 `json:"enc" format:"uint64"`
Looping bool `json:"looping"`
LoopingRuntime uint64 `json:"looping_runtime" format:"uint64"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
}

View File

@ -0,0 +1,6 @@
package api
// Command is a command to send to a process
type Command struct {
Command string `json:"command" validate:"required" enums:"start,stop,restart,reload" jsonschema:"enum=start,enum=stop,enum=restart,enum=reload"`
}

View File

@ -0,0 +1,497 @@
package api
import (
"fmt"
"strings"
"time"
)
type ConfigV1 struct {
Version int64 `json:"version" jsonschema:"minimum=1,maximum=1"`
ID string `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
CheckForUpdates bool `json:"update_check"`
Log struct {
Level string `json:"level" enums:"debug,info,warn,error,silent" jsonschema:"enum=debug,enum=info,enum=warn,enum=error,enum=silent"`
Topics []string `json:"topics"`
MaxLines int `json:"max_lines"`
} `json:"log"`
DB struct {
Dir string `json:"dir"`
} `json:"db"`
Host struct {
Name []string `json:"name"`
Auto bool `json:"auto"`
} `json:"host"`
API struct {
ReadOnly bool `json:"read_only"`
Access struct {
HTTP struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"http"`
HTTPS struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"https"`
} `json:"access"`
Auth struct {
Enable bool `json:"enable"`
DisableLocalhost bool `json:"disable_localhost"`
Username string `json:"username"`
Password string `json:"password"`
JWT struct {
Secret string `json:"secret"`
} `json:"jwt"`
Auth0 struct {
Enable bool `json:"enable"`
Tenants []Auth0Tenant `json:"tenants"`
} `json:"auth0"`
} `json:"auth"`
} `json:"api"`
TLS struct {
Address string `json:"address"`
Enable bool `json:"enable"`
Auto bool `json:"auto"`
CertFile string `json:"cert_file"`
KeyFile string `json:"key_file"`
} `json:"tls"`
Storage struct {
Disk struct {
Dir string `json:"dir"`
Size int64 `json:"max_size_mbytes"`
Cache struct {
Enable bool `json:"enable"`
Size uint64 `json:"max_size_mbytes"`
TTL int64 `json:"ttl_seconds"`
FileSize uint64 `json:"max_file_size_mbytes"`
Types []string `json:"types"`
} `json:"cache"`
} `json:"disk"`
Memory struct {
Auth struct {
Enable bool `json:"enable"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"auth"`
Size int64 `json:"max_size_mbytes"`
Purge bool `json:"purge"`
} `json:"memory"`
CORS struct {
Origins []string `json:"origins"`
} `json:"cors"`
MimeTypes string `json:"mimetypes_file"`
} `json:"storage"`
RTMP struct {
Enable bool `json:"enable"`
EnableTLS bool `json:"enable_tls"`
Address string `json:"address"`
App string `json:"app"`
Token string `json:"token"`
} `json:"rtmp"`
FFmpeg struct {
Binary string `json:"binary"`
MaxProcesses int64 `json:"max_processes"`
Access struct {
Input struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"input"`
Output struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"output"`
} `json:"access"`
Log struct {
MaxLines int `json:"max_lines"`
MaxHistory int `json:"max_history"`
} `json:"log"`
} `json:"ffmpeg"`
Playout struct {
Enable bool `json:"enable"`
MinPort int `json:"min_port"`
MaxPort int `json:"max_port"`
} `json:"playout"`
Debug struct {
Profiling bool `json:"profiling"`
ForceGC int `json:"force_gc"`
} `json:"debug"`
Metrics struct {
Enable bool `json:"enable"`
EnablePrometheus bool `json:"enable_prometheus"`
Range int64 `json:"range_sec"` // seconds
Interval int64 `json:"interval_sec"` // seconds
} `json:"metrics"`
Sessions struct {
Enable bool `json:"enable"`
IPIgnoreList []string `json:"ip_ignorelist"`
SessionTimeout int `json:"session_timeout_sec"`
Persist bool `json:"persist"`
PersistInterval int `json:"persist_interval_sec"`
MaxBitrate uint64 `json:"max_bitrate_mbit"`
MaxSessions uint64 `json:"max_sessions"`
} `json:"sessions"`
Service struct {
Enable bool `json:"enable"`
Token string `json:"token"`
URL string `json:"url"`
} `json:"service"`
Router struct {
BlockedPrefixes []string `json:"blocked_prefixes"`
Routes map[string]string `json:"routes"`
UIPath string `json:"ui_path"`
} `json:"router"`
}
type ConfigV2 struct {
Version int64 `json:"version" jsonschema:"minimum=2,maximum=2"`
ID string `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
CheckForUpdates bool `json:"update_check"`
Log struct {
Level string `json:"level" enums:"debug,info,warn,error,silent" jsonschema:"enum=debug,enum=info,enum=warn,enum=error,enum=silent"`
Topics []string `json:"topics"`
MaxLines int `json:"max_lines"`
} `json:"log"`
DB struct {
Dir string `json:"dir"`
} `json:"db"`
Host struct {
Name []string `json:"name"`
Auto bool `json:"auto"`
} `json:"host"`
API struct {
ReadOnly bool `json:"read_only"`
Access struct {
HTTP struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"http"`
HTTPS struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"https"`
} `json:"access"`
Auth struct {
Enable bool `json:"enable"`
DisableLocalhost bool `json:"disable_localhost"`
Username string `json:"username"`
Password string `json:"password"`
JWT struct {
Secret string `json:"secret"`
} `json:"jwt"`
Auth0 struct {
Enable bool `json:"enable"`
Tenants []Auth0Tenant `json:"tenants"`
} `json:"auth0"`
} `json:"auth"`
} `json:"api"`
TLS struct {
Address string `json:"address"`
Enable bool `json:"enable"`
Auto bool `json:"auto"`
CertFile string `json:"cert_file"`
KeyFile string `json:"key_file"`
} `json:"tls"`
Storage struct {
Disk struct {
Dir string `json:"dir"`
Size int64 `json:"max_size_mbytes"`
Cache struct {
Enable bool `json:"enable"`
Size uint64 `json:"max_size_mbytes"`
TTL int64 `json:"ttl_seconds"`
FileSize uint64 `json:"max_file_size_mbytes"`
Types []string `json:"types"`
} `json:"cache"`
} `json:"disk"`
Memory struct {
Auth struct {
Enable bool `json:"enable"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"auth"`
Size int64 `json:"max_size_mbytes"`
Purge bool `json:"purge"`
} `json:"memory"`
CORS struct {
Origins []string `json:"origins"`
} `json:"cors"`
MimeTypes string `json:"mimetypes_file"`
} `json:"storage"`
RTMP struct {
Enable bool `json:"enable"`
EnableTLS bool `json:"enable_tls"`
Address string `json:"address"`
AddressTLS string `json:"address_tls"`
App string `json:"app"`
Token string `json:"token"`
} `json:"rtmp"`
SRT struct {
Enable bool `json:"enable"`
Address string `json:"address"`
Passphrase string `json:"passphrase"`
Token string `json:"token"`
Log struct {
Enable bool `json:"enable"`
Topics []string `json:"topics"`
} `json:"log"`
} `json:"srt"`
FFmpeg struct {
Binary string `json:"binary"`
MaxProcesses int64 `json:"max_processes"`
Access struct {
Input struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"input"`
Output struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"output"`
} `json:"access"`
Log struct {
MaxLines int `json:"max_lines"`
MaxHistory int `json:"max_history"`
} `json:"log"`
} `json:"ffmpeg"`
Playout struct {
Enable bool `json:"enable"`
MinPort int `json:"min_port"`
MaxPort int `json:"max_port"`
} `json:"playout"`
Debug struct {
Profiling bool `json:"profiling"`
ForceGC int `json:"force_gc"`
} `json:"debug"`
Metrics struct {
Enable bool `json:"enable"`
EnablePrometheus bool `json:"enable_prometheus"`
Range int64 `json:"range_sec"` // seconds
Interval int64 `json:"interval_sec"` // seconds
} `json:"metrics"`
Sessions struct {
Enable bool `json:"enable"`
IPIgnoreList []string `json:"ip_ignorelist"`
SessionTimeout int `json:"session_timeout_sec"`
Persist bool `json:"persist"`
PersistInterval int `json:"persist_interval_sec"`
MaxBitrate uint64 `json:"max_bitrate_mbit"`
MaxSessions uint64 `json:"max_sessions"`
} `json:"sessions"`
Service struct {
Enable bool `json:"enable"`
Token string `json:"token"`
URL string `json:"url"`
} `json:"service"`
Router struct {
BlockedPrefixes []string `json:"blocked_prefixes"`
Routes map[string]string `json:"routes"`
UIPath string `json:"ui_path"`
} `json:"router"`
}
type ConfigV3 struct {
Version int64 `json:"version" jsonschema:"minimum=3,maximum=3" format:"int64"`
ID string `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
CheckForUpdates bool `json:"update_check"`
Log struct {
Level string `json:"level" enums:"debug,info,warn,error,silent" jsonschema:"enum=debug,enum=info,enum=warn,enum=error,enum=silent"`
Topics []string `json:"topics"`
MaxLines int `json:"max_lines" format:"int"`
} `json:"log"`
DB struct {
Dir string `json:"dir"`
} `json:"db"`
Host struct {
Name []string `json:"name"`
Auto bool `json:"auto"`
} `json:"host"`
API struct {
ReadOnly bool `json:"read_only"`
Access struct {
HTTP struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"http"`
HTTPS struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"https"`
} `json:"access"`
Auth struct {
Enable bool `json:"enable"`
DisableLocalhost bool `json:"disable_localhost"`
Username string `json:"username"`
Password string `json:"password"`
JWT struct {
Secret string `json:"secret"`
} `json:"jwt"`
Auth0 struct {
Enable bool `json:"enable"`
Tenants []Auth0Tenant `json:"tenants"`
} `json:"auth0"`
} `json:"auth"`
} `json:"api"`
TLS struct {
Address string `json:"address"`
Enable bool `json:"enable"`
Auto bool `json:"auto"`
Email string `json:"email"`
CertFile string `json:"cert_file"`
KeyFile string `json:"key_file"`
} `json:"tls"`
Storage struct {
Disk struct {
Dir string `json:"dir"`
Size int64 `json:"max_size_mbytes" format:"int64"`
Cache struct {
Enable bool `json:"enable"`
Size uint64 `json:"max_size_mbytes" format:"uint64"`
TTL int64 `json:"ttl_seconds" format:"int64"`
FileSize uint64 `json:"max_file_size_mbytes" format:"uint64"`
Types struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"types"`
} `json:"cache"`
} `json:"disk"`
Memory struct {
Auth struct {
Enable bool `json:"enable"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"auth"`
Size int64 `json:"max_size_mbytes" format:"int64"`
Purge bool `json:"purge"`
} `json:"memory"`
S3 []S3Storage `json:"s3"`
CORS struct {
Origins []string `json:"origins"`
} `json:"cors"`
MimeTypes string `json:"mimetypes_file"`
} `json:"storage"`
RTMP struct {
Enable bool `json:"enable"`
EnableTLS bool `json:"enable_tls"`
Address string `json:"address"`
AddressTLS string `json:"address_tls"`
App string `json:"app"`
Token string `json:"token"`
} `json:"rtmp"`
SRT struct {
Enable bool `json:"enable"`
Address string `json:"address"`
Passphrase string `json:"passphrase"`
Token string `json:"token"`
Log struct {
Enable bool `json:"enable"`
Topics []string `json:"topics"`
} `json:"log"`
} `json:"srt"`
FFmpeg struct {
Binary string `json:"binary"`
MaxProcesses int64 `json:"max_processes" format:"int64"`
Access struct {
Input struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"input"`
Output struct {
Allow []string `json:"allow"`
Block []string `json:"block"`
} `json:"output"`
} `json:"access"`
Log struct {
MaxLines int `json:"max_lines" format:"int"`
MaxHistory int `json:"max_history" format:"int"`
} `json:"log"`
} `json:"ffmpeg"`
Playout struct {
Enable bool `json:"enable"`
MinPort int `json:"min_port" format:"int"`
MaxPort int `json:"max_port" format:"int"`
} `json:"playout"`
Debug struct {
Profiling bool `json:"profiling"`
ForceGC int `json:"force_gc" format:"int"`
MemoryLimit int64 `json:"memory_limit_mbytes" format:"int64"`
} `json:"debug"`
Metrics struct {
Enable bool `json:"enable"`
EnablePrometheus bool `json:"enable_prometheus"`
Range int64 `json:"range_sec" format:"int64"` // seconds
Interval int64 `json:"interval_sec" format:"int64"` // seconds
} `json:"metrics"`
Sessions struct {
Enable bool `json:"enable"`
IPIgnoreList []string `json:"ip_ignorelist"`
SessionTimeout int `json:"session_timeout_sec" format:"int"`
Persist bool `json:"persist"`
PersistInterval int `json:"persist_interval_sec" format:"int"`
MaxBitrate uint64 `json:"max_bitrate_mbit" format:"uint64"`
MaxSessions uint64 `json:"max_sessions" format:"uint64"`
} `json:"sessions"`
Service struct {
Enable bool `json:"enable"`
Token string `json:"token"`
URL string `json:"url"`
} `json:"service"`
Router struct {
BlockedPrefixes []string `json:"blocked_prefixes"`
Routes map[string]string `json:"routes"`
UIPath string `json:"ui_path"`
} `json:"router"`
}
type Config struct {
CreatedAt time.Time `json:"created_at"`
LoadedAt time.Time `json:"loaded_at"`
UpdatedAt time.Time `json:"updated_at"`
Config interface{} `json:"config"`
Overrides []string `json:"overrides"`
}
type Auth0Tenant struct {
Domain string `json:"domain"`
Audience string `json:"audience"`
ClientID string `json:"clientid"`
Users []string `json:"users"`
}
type S3Storage struct {
Name string `json:"name"`
Mountpoint string `json:"mountpoint"`
Auth S3StorageAuth `json:"auth"`
Endpoint string `json:"endpoint"`
AccessKeyID string `json:"access_key_id"`
SecretAccessKey string `json:"secret_access_key"`
Bucket string `json:"bucket"`
Region string `json:"region"`
UseSSL bool `json:"use_ssl"`
}
type S3StorageAuth struct {
Enable bool `json:"enable"`
Username string `json:"username"`
Password string `json:"password"`
}
// ConfigError is used to return error messages when uploading a new config
type ConfigError map[string][]string
func (c ConfigError) Error() string {
s := strings.Builder{}
for key, messages := range map[string][]string(c) {
s.WriteString(fmt.Sprintf("%s: %s", key, strings.Join(messages, ",")))
}
return s.String()
}

View File

@ -0,0 +1,2 @@
// Package api provides types for communicating with the REST API
package api

View File

@ -0,0 +1,19 @@
package api
import (
"fmt"
"strings"
)
// Error represents an error response of the API
type Error struct {
Code int `json:"code" jsonschema:"required"`
Message string `json:"message" jsonschema:""`
Details []string `json:"details" jsonschema:""`
Body []byte `json:"-"`
}
// Error returns the string representation of the error
func (e Error) Error() string {
return fmt.Sprintf("code=%d, message=%s, details=%s", e.Code, e.Message, strings.Join(e.Details, " "))
}

View File

@ -0,0 +1,14 @@
package api
// FileInfo represents informatiion about a file on a filesystem
type FileInfo struct {
Name string `json:"name" jsonschema:"minLength=1"`
Size int64 `json:"size_bytes" jsonschema:"minimum=0"`
LastMod int64 `json:"last_modified" jsonschema:"minimum=0"`
}
type FilesystemInfo struct {
Name string `json:"name"`
Type string `json:"type"`
Mount string `json:"mount"`
}

View File

@ -0,0 +1,11 @@
package api
type GraphQuery struct {
Query string `json:"query"`
Variables interface{} `json:"variables"`
}
type GraphResponse struct {
Data interface{} `json:"data"`
Errors []interface{} `json:"errors"`
}

View File

@ -0,0 +1,11 @@
package api
// JWT is the JWT token and its expiry date
type JWT struct {
AccessToken string `json:"access_token" jsonschema:"minLength=1"`
RefreshToken string `json:"refresh_token" jsonschema:"minLength=1"`
}
type JWTRefresh struct {
AccessToken string `json:"access_token" jsonschema:"minLength=1"`
}

View File

@ -0,0 +1,4 @@
package api
// LogEvent represents a log event from the app
type LogEvent map[string]interface{}

View File

@ -0,0 +1,7 @@
package api
// Login are the requires login credentials
type Login struct {
Username string `json:"username" validate:"required" jsonschema:"minLength=1"`
Password string `json:"password" validate:"required" jsonschema:"minLength=1"`
}

View File

@ -0,0 +1,9 @@
package api
// Metadata represents arbitrary metadata for a process of for the app
type Metadata interface{}
// NewMetadata takes an interface and converts it to a Metadata type.
func NewMetadata(data interface{}) Metadata {
return Metadata(data)
}

View File

@ -0,0 +1,55 @@
package api
import (
"encoding/json"
"time"
)
type MetricsDescription struct {
Name string `json:"name"`
Description string `json:"description"`
Labels []string `json:"labels"`
}
type MetricsQueryMetric struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
}
type MetricsQuery struct {
Timerange int64 `json:"timerange_sec"`
Interval int64 `json:"interval_sec"`
Metrics []MetricsQueryMetric `json:"metrics"`
}
type MetricsResponseMetric struct {
Name string `json:"name"`
Labels map[string]string `json:"labels"`
Values []MetricsResponseValue `json:"values"`
}
type MetricsResponseValue struct {
TS time.Time `json:"ts"`
Value float64 `json:"value"`
}
// MarshalJSON unmarshals a JSON to MetricsResponseValue
func (v *MetricsResponseValue) UnmarshalJSON(data []byte) error {
x := []float64{}
err := json.Unmarshal(data, &x)
if err != nil {
return err
}
v.TS = time.Unix(int64(x[0]), 0)
v.Value = x[1]
return nil
}
type MetricsResponse struct {
Timerange int64 `json:"timerange_sec"`
Interval int64 `json:"interval_sec"`
Metrics []MetricsResponseMetric `json:"metrics"`
}

View File

@ -0,0 +1,33 @@
package api
type PlayoutStatusIO struct {
State string `json:"state" enums:"running,idle" jsonschema:"enum=running,enum=idle"`
Packet uint64 `json:"packet" format:"uint64"`
Time uint64 `json:"time" format:"uint64"`
Size uint64 `json:"size_kb" format:"uint64"`
}
type PlayoutStatusSwap struct {
Address string `json:"url"`
Status string `json:"status"`
LastAddress string `json:"lasturl"`
LastError string `json:"lasterror"`
}
type PlayoutStatus struct {
ID string `json:"id"`
Address string `json:"url"`
Stream uint64 `json:"stream" format:"uint64"`
Queue uint64 `json:"queue" format:"uint64"`
AQueue uint64 `json:"aqueue" format:"uint64"`
Dup uint64 `json:"dup" format:"uint64"`
Drop uint64 `json:"drop" format:"uint64"`
Enc uint64 `json:"enc" format:"uint64"`
Looping bool `json:"looping"`
Duplicating bool `json:"duplicating"`
GOP string `json:"gop"`
Debug interface{} `json:"debug"`
Input PlayoutStatusIO `json:"input"`
Output PlayoutStatusIO `json:"output"`
Swap PlayoutStatusSwap `json:"swap"`
}

View File

@ -0,0 +1,34 @@
package api
// ProbeIO represents a stream of a probed file
type ProbeIO struct {
// common
Address string `json:"url"`
Format string `json:"format"`
Index uint64 `json:"index" format:"uint64"`
Stream uint64 `json:"stream" format:"uint64"`
Language string `json:"language"`
Type string `json:"type"`
Codec string `json:"codec"`
Coder string `json:"coder"`
Bitrate float64 `json:"bitrate_kbps" swaggertype:"number" jsonschema:"type=number"`
Duration float64 `json:"duration_sec" swaggertype:"number" jsonschema:"type=number"`
// video
FPS float64 `json:"fps" swaggertype:"number" jsonschema:"type=number"`
Pixfmt string `json:"pix_fmt"`
Width uint64 `json:"width" format:"uint64"`
Height uint64 `json:"height" format:"uint64"`
// audio
Sampling uint64 `json:"sampling_hz" format:"uint64"`
Layout string `json:"layout"`
Channels uint64 `json:"channels" format:"uint64"`
}
// Probe represents the result of probing a file. It has a list of detected streams
// and a list of log lone from the probe process.
type Probe struct {
Streams []ProbeIO `json:"streams"`
Log []string `json:"log"`
}

View File

@ -0,0 +1,83 @@
package api
// Process represents all information on a process
type Process struct {
ID string `json:"id" jsonschema:"minLength=1"`
Type string `json:"type" jsonschema:"enum=ffmpeg"`
Reference string `json:"reference"`
CreatedAt int64 `json:"created_at" jsonschema:"minimum=0" format:"int64"`
UpdatedAt int64 `json:"updated_at" jsonschema:"minimum=0" format:"int64"`
Config *ProcessConfig `json:"config,omitempty"`
State *ProcessState `json:"state,omitempty"`
Report *ProcessReport `json:"report,omitempty"`
Metadata Metadata `json:"metadata,omitempty"`
}
// ProcessConfigIO represents an input or output of an ffmpeg process config
type ProcessConfigIO struct {
ID string `json:"id"`
Address string `json:"address" validate:"required" jsonschema:"minLength=1"`
Options []string `json:"options"`
Cleanup []ProcessConfigIOCleanup `json:"cleanup,omitempty"`
}
type ProcessConfigIOCleanup struct {
Pattern string `json:"pattern" validate:"required"`
MaxFiles uint `json:"max_files" format:"uint"`
MaxFileAge uint `json:"max_file_age_seconds" format:"uint"`
PurgeOnDelete bool `json:"purge_on_delete"`
}
type ProcessConfigLimits struct {
CPU float64 `json:"cpu_usage" jsonschema:"minimum=0,maximum=100"`
Memory uint64 `json:"memory_mbytes" jsonschema:"minimum=0" format:"uint64"`
WaitFor uint64 `json:"waitfor_seconds" jsonschema:"minimum=0" format:"uint64"`
}
// ProcessConfig represents the configuration of an ffmpeg process
type ProcessConfig struct {
ID string `json:"id"`
Type string `json:"type" validate:"oneof='ffmpeg' ''" jsonschema:"enum=ffmpeg,enum="`
Reference string `json:"reference"`
Input []ProcessConfigIO `json:"input" validate:"required"`
Output []ProcessConfigIO `json:"output" validate:"required"`
Options []string `json:"options"`
Reconnect bool `json:"reconnect"`
ReconnectDelay uint64 `json:"reconnect_delay_seconds" format:"uint64"`
Autostart bool `json:"autostart"`
StaleTimeout uint64 `json:"stale_timeout_seconds" format:"uint64"`
Limits ProcessConfigLimits `json:"limits"`
}
// ProcessState represents the current state of an ffmpeg process
type ProcessState struct {
Order string `json:"order" jsonschema:"enum=start,enum=stop"`
State string `json:"exec" jsonschema:"enum=finished,enum=starting,enum=running,enum=finishing,enum=killed,enum=failed"`
Runtime int64 `json:"runtime_seconds" jsonschema:"minimum=0" format:"int64"`
Reconnect int64 `json:"reconnect_seconds" format:"int64"`
LastLog string `json:"last_logline"`
Progress *Progress `json:"progress"`
Memory uint64 `json:"memory_bytes" format:"uint64"`
CPU float64 `json:"cpu_usage" swaggertype:"number" jsonschema:"type=number"`
Command []string `json:"command"`
}
type ProcessUsageCPU struct {
NCPU float64 `json:"ncpu" swaggertype:"number" jsonschema:"type=number"`
Current float64 `json:"cur" swaggertype:"number" jsonschema:"type=number"`
Average float64 `json:"avg" swaggertype:"number" jsonschema:"type=number"`
Max float64 `json:"max" swaggertype:"number" jsonschema:"type=number"`
Limit float64 `json:"limit" swaggertype:"number" jsonschema:"type=number"`
}
type ProcessUsageMemory struct {
Current uint64 `json:"cur" format:"uint64"`
Average float64 `json:"avg" swaggertype:"number" jsonschema:"type=number"`
Max uint64 `json:"max" format:"uint64"`
Limit uint64 `json:"limit" format:"uint64"`
}
type ProcessUsage struct {
CPU ProcessUsageCPU `json:"cpu_usage"`
Memory ProcessUsageMemory `json:"memory_bytes"`
}

View File

@ -0,0 +1,60 @@
package api
type ProgressIOFramerate struct {
Min float64 `json:"min" swaggertype:"number" jsonschema:"type=number"`
Max float64 `json:"max" swaggertype:"number" jsonschema:"type=number"`
Average float64 `json:"avg" swaggertype:"number" jsonschema:"type=number"`
}
// ProgressIO represents the progress of an ffmpeg input or output
type ProgressIO struct {
ID string `json:"id" jsonschema:"minLength=1"`
Address string `json:"address" jsonschema:"minLength=1"`
// General
Index uint64 `json:"index" format:"uint64"`
Stream uint64 `json:"stream" format:"uint64"`
Format string `json:"format"`
Type string `json:"type"`
Codec string `json:"codec"`
Coder string `json:"coder"`
Frame uint64 `json:"frame" format:"uint64"`
Keyframe uint64 `json:"keyframe" format:"uint64"`
Framerate ProgressIOFramerate `json:"framerate"`
FPS float64 `json:"fps" swaggertype:"number" jsonschema:"type=number"`
Packet uint64 `json:"packet" format:"uint64"`
PPS float64 `json:"pps" swaggertype:"number" jsonschema:"type=number"`
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
Bitrate float64 `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
Extradata uint64 `json:"extradata_size_bytes" format:"uint64"` // bytes
// Video
Pixfmt string `json:"pix_fmt,omitempty"`
Quantizer float64 `json:"q,omitempty" swaggertype:"number" jsonschema:"type=number"`
Width uint64 `json:"width,omitempty" format:"uint64"`
Height uint64 `json:"height,omitempty" format:"uint64"`
// Audio
Sampling uint64 `json:"sampling_hz,omitempty" format:"uint64"`
Layout string `json:"layout,omitempty"`
Channels uint64 `json:"channels,omitempty" format:"uint64"`
// avstream
AVstream *AVstream `json:"avstream"`
}
// Progress represents the progress of an ffmpeg process
type Progress struct {
Input []ProgressIO `json:"inputs"`
Output []ProgressIO `json:"outputs"`
Frame uint64 `json:"frame" format:"uint64"`
Packet uint64 `json:"packet" format:"uint64"`
FPS float64 `json:"fps" swaggertype:"number" jsonschema:"type=number"`
Quantizer float64 `json:"q" swaggertype:"number" jsonschema:"type=number"`
Size uint64 `json:"size_kb" format:"uint64"` // kbytes
Time float64 `json:"time" swaggertype:"number" jsonschema:"type=number"`
Bitrate float64 `json:"bitrate_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
Speed float64 `json:"speed" swaggertype:"number" jsonschema:"type=number"`
Drop uint64 `json:"drop" format:"uint64"`
Dup uint64 `json:"dup" format:"uint64"`
}

View File

@ -0,0 +1,31 @@
package api
// ProcessReportEntry represents the logs of a run of a restream process
type ProcessReportEntry struct {
CreatedAt int64 `json:"created_at" format:"int64"`
Prelude []string `json:"prelude,omitempty"`
Log [][2]string `json:"log,omitempty"`
Matches []string `json:"matches,omitempty"`
ExitedAt int64 `json:"exited_at,omitempty" format:"int64"`
ExitState string `json:"exit_state,omitempty"`
Progress *Progress `json:"progress,omitempty"`
Resources *ProcessUsage `json:"resources,omitempty"`
}
type ProcessReportHistoryEntry struct {
ProcessReportEntry
}
// ProcessReport represents the current log and the logs of previous runs of a restream process
type ProcessReport struct {
ProcessReportEntry
History []ProcessReportEntry `json:"history"`
}
type ProcessReportSearchResult struct {
ProcessID string `json:"id"`
Reference string `json:"reference"`
ExitState string `json:"exit_state"`
CreatedAt int64 `json:"created_at" format:"int64"`
ExitedAt int64 `json:"exited_at" format:"int64"`
}

View File

@ -0,0 +1,6 @@
package api
// RTMPChannel represents details about a currently connected RTMP publisher
type RTMPChannel struct {
Name string `json:"name" jsonschema:"minLength=1"`
}

View File

@ -0,0 +1,60 @@
package api
// SessionStats are the accumulated numbers for the session summary
type SessionStats struct {
TotalSessions uint64 `json:"sessions" format:"uint64"`
TotalRxBytes uint64 `json:"traffic_rx_mb" format:"uint64"`
TotalTxBytes uint64 `json:"traffic_tx_mb" format:"uint64"`
}
// SessionPeers is for the grouping by peers in the summary
type SessionPeers struct {
SessionStats
Locations map[string]SessionStats `json:"local"`
}
// Session represents an active session
type Session struct {
ID string `json:"id"`
Reference string `json:"reference"`
CreatedAt int64 `json:"created_at" format:"int64"`
Location string `json:"local"`
Peer string `json:"remote"`
Extra string `json:"extra"`
RxBytes uint64 `json:"bytes_rx" format:"uint64"`
TxBytes uint64 `json:"bytes_tx" format:"uint64"`
RxBitrate float64 `json:"bandwidth_rx_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
TxBitrate float64 `json:"bandwidth_tx_kbit" swaggertype:"number" jsonschema:"type=number"` // kbit/s
}
// SessionSummaryActive represents the currently active sessions
type SessionSummaryActive struct {
SessionList []Session `json:"list"`
Sessions uint64 `json:"sessions" format:"uint64"`
RxBitrate float64 `json:"bandwidth_rx_mbit" swaggertype:"number" jsonschema:"type=number"` // mbit/s
TxBitrate float64 `json:"bandwidth_tx_mbit" swaggertype:"number" jsonschema:"type=number"` // mbit/s
MaxSessions uint64 `json:"max_sessions" format:"uint64"`
MaxRxBitrate float64 `json:"max_bandwidth_rx_mbit" swaggertype:"number" jsonschema:"type=number"` // mbit/s
MaxTxBitrate float64 `json:"max_bandwidth_tx_mbit" swaggertype:"number" jsonschema:"type=number"` // mbit/s
}
// SessionSummarySummary represents the summary (history) of all finished sessions
type SessionSummarySummary struct {
Peers map[string]SessionPeers `json:"remote"`
Locations map[string]SessionStats `json:"local"`
References map[string]SessionStats `json:"reference"`
SessionStats
}
// SessionSummary is the API representation of a session.Summary
type SessionSummary struct {
Active SessionSummaryActive `json:"active"`
Summary SessionSummarySummary `json:"summary"`
}
type SessionsSummary map[string]SessionSummary
// SessionsActive is the API representation of all active sessions
type SessionsActive map[string][]Session

View File

@ -0,0 +1,89 @@
package api
// SkillsFilter represents an ffmpeg filter
type SkillsFilter struct {
ID string `json:"id"`
Name string `json:"name"`
}
// SkillsHWAccel represents an ffmpeg HW accelerator
type SkillsHWAccel struct {
ID string `json:"id"`
Name string `json:"name"`
}
// SkillsCodec represents an ffmpeg codec
type SkillsCodec struct {
ID string `json:"id"`
Name string `json:"name"`
Encoders []string `json:"encoders"`
Decoders []string `json:"decoders"`
}
// SkillsHWDevice represents an ffmpeg hardware device
type SkillsHWDevice struct {
ID string `json:"id"`
Name string `json:"name"`
Extra string `json:"extra"`
Media string `json:"media"`
}
// SkillsDevice represents a group of ffmpeg hardware devices
type SkillsDevice struct {
ID string `json:"id"`
Name string `json:"name"`
Devices []SkillsHWDevice `json:"devices"`
}
// SkillsFormat represents an ffmpeg format
type SkillsFormat struct {
ID string `json:"id"`
Name string `json:"name"`
}
// SkillsProtocol represents an ffmpeg protocol
type SkillsProtocol struct {
ID string `json:"id"`
Name string `json:"name"`
}
// SkillsLibrary represents an avlib ffmpeg is compiled and linked with
type SkillsLibrary struct {
Name string `json:"name"`
Compiled string `json:"compiled"`
Linked string `json:"linked"`
}
// Skills represents a set of ffmpeg capabilities
type Skills struct {
FFmpeg struct {
Version string `json:"version"`
Compiler string `json:"compiler"`
Configuration string `json:"configuration"`
Libraries []SkillsLibrary `json:"libraries"`
} `json:"ffmpeg"`
Filters []SkillsFilter `json:"filter"`
HWAccels []SkillsHWAccel `json:"hwaccels"`
Codecs struct {
Audio []SkillsCodec `json:"audio"`
Video []SkillsCodec `json:"video"`
Subtitle []SkillsCodec `json:"subtitle"`
} `json:"codecs"`
Devices struct {
Demuxers []SkillsDevice `json:"demuxers"`
Muxers []SkillsDevice `json:"muxers"`
} `json:"devices"`
Formats struct {
Demuxers []SkillsFormat `json:"demuxers"`
Muxers []SkillsFormat `json:"muxers"`
} `json:"formats"`
Protocols struct {
Input []SkillsProtocol `json:"input"`
Output []SkillsProtocol `json:"output"`
} `json:"protocols"`
}

View File

@ -0,0 +1,79 @@
package api
// SRTStatistics represents the statistics of a SRT connection
type SRTStatistics struct {
MsTimeStamp uint64 `json:"timestamp_ms" format:"uint64"` // The time elapsed, in milliseconds, since the SRT socket has been created
// Accumulated
PktSent uint64 `json:"sent_pkt" format:"uint64"` // The total number of sent DATA packets, including retransmitted packets
PktRecv uint64 `json:"recv_pkt" format:"uint64"` // The total number of received DATA packets, including retransmitted packets
PktSentUnique uint64 `json:"sent_unique_pkt" format:"uint64"` // The total number of unique DATA packets sent by the SRT sender
PktRecvUnique uint64 `json:"recv_unique_pkt" format:"uint64"` // The total number of unique original, retransmitted or recovered by the packet filter DATA packets received in time, decrypted without errors and, as a result, scheduled for delivery to the upstream application by the SRT receiver.
PktSndLoss uint64 `json:"send_loss_pkt" format:"uint64"` // The total number of data packets considered or reported as lost at the sender side. Does not correspond to the packets detected as lost at the receiver side.
PktRcvLoss uint64 `json:"recv_loss_pkt" format:"uint64"` // The total number of SRT DATA packets detected as presently missing (either reordered or lost) at the receiver side
PktRetrans uint64 `json:"sent_retrans_pkt" format:"uint64"` // The total number of retransmitted packets sent by the SRT sender
PktRcvRetrans uint64 `json:"recv_retran_pkts" format:"uint64"` // The total number of retransmitted packets registered at the receiver side
PktSentACK uint64 `json:"sent_ack_pkt" format:"uint64"` // The total number of sent ACK (Acknowledgement) control packets
PktRecvACK uint64 `json:"recv_ack_pkt" format:"uint64"` // The total number of received ACK (Acknowledgement) control packets
PktSentNAK uint64 `json:"sent_nak_pkt" format:"uint64"` // The total number of sent NAK (Negative Acknowledgement) control packets
PktRecvNAK uint64 `json:"recv_nak_pkt" format:"uint64"` // The total number of received NAK (Negative Acknowledgement) control packets
PktSentKM uint64 `json:"send_km_pkt" format:"uint64"` // The total number of sent KM (Key Material) control packets
PktRecvKM uint64 `json:"recv_km_pkt" format:"uint64"` // The total number of received KM (Key Material) control packets
UsSndDuration uint64 `json:"send_duration_us" format:"uint64"` // The total accumulated time in microseconds, during which the SRT sender has some data to transmit, including packets that have been sent, but not yet acknowledged
PktSndDrop uint64 `json:"send_drop_pkt" format:"uint64"` // The total number of dropped by the SRT sender DATA packets that have no chance to be delivered in time
PktRcvDrop uint64 `json:"recv_drop_pkt" format:"uint64"` // The total number of dropped by the SRT receiver and, as a result, not delivered to the upstream application DATA packets
PktRcvUndecrypt uint64 `json:"recv_undecrypt_pkt" format:"uint64"` // The total number of packets that failed to be decrypted at the receiver side
ByteSent uint64 `json:"sent_bytes" format:"uint64"` // Same as pktSent, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecv uint64 `json:"recv_bytes" format:"uint64"` // Same as pktRecv, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSentUnique uint64 `json:"sent_unique_bytes" format:"uint64"` // Same as pktSentUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRecvUnique uint64 `json:"recv_unique_bytes" format:"uint64"` // Same as pktRecvUnique, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvLoss uint64 `json:"recv_loss_bytes" format:"uint64"` // Same as pktRcvLoss, but expressed in bytes, including payload and all the headers (IP, TCP, SRT), bytes for the presently missing (either reordered or lost) packets' payloads are estimated based on the average packet size
ByteRetrans uint64 `json:"sent_retrans_bytes" format:"uint64"` // Same as pktRetrans, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteSndDrop uint64 `json:"send_drop_bytes" format:"uint64"` // Same as pktSndDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvDrop uint64 `json:"recv_drop_bytes" format:"uint64"` // Same as pktRcvDrop, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
ByteRcvUndecrypt uint64 `json:"recv_undecrypt_bytes" format:"uint64"` // Same as pktRcvUndecrypt, but expressed in bytes, including payload and all the headers (IP, TCP, SRT)
// Instantaneous
UsPktSndPeriod float64 `json:"pkt_send_period_us"` // Current minimum time interval between which consecutive packets are sent, in microseconds
PktFlowWindow uint64 `json:"flow_window_pkt" format:"uint64"` // The maximum number of packets that can be "in flight"
PktFlightSize uint64 `json:"flight_size_pkt" format:"uint64"` // The number of packets in flight
MsRTT float64 `json:"rtt_ms"` // Smoothed round-trip time (SRTT), an exponentially-weighted moving average (EWMA) of an endpoint's RTT samples, in milliseconds
MbpsBandwidth float64 `json:"bandwidth_mbit"` // Estimated bandwidth of the network link, in Mbps
ByteAvailSndBuf uint64 `json:"avail_send_buf_bytes" format:"uint64"` // The available space in the sender's buffer, in bytes
ByteAvailRcvBuf uint64 `json:"avail_recv_buf_bytes" format:"uint64"` // The available space in the receiver's buffer, in bytes
MbpsMaxBW float64 `json:"max_bandwidth_mbit"` // Transmission bandwidth limit, in Mbps
ByteMSS uint64 `json:"mss_bytes" format:"uint64"` // Maximum Segment Size (MSS), in bytes
PktSndBuf uint64 `json:"send_buf_pkt" format:"uint64"` // The number of packets in the sender's buffer that are already scheduled for sending or even possibly sent, but not yet acknowledged
ByteSndBuf uint64 `json:"send_buf_bytes" format:"uint64"` // Instantaneous (current) value of pktSndBuf, but expressed in bytes, including payload and all headers (IP, TCP, SRT)
MsSndBuf uint64 `json:"send_buf_ms" format:"uint64"` // The timespan (msec) of packets in the sender's buffer (unacknowledged packets)
MsSndTsbPdDelay uint64 `json:"send_tsbpd_delay_ms" format:"uint64"` // Timestamp-based Packet Delivery Delay value of the peer
PktRcvBuf uint64 `json:"recv_buf_pkt" format:"uint64"` // The number of acknowledged packets in receiver's buffer
ByteRcvBuf uint64 `json:"recv_buf_bytes" format:"uint64"` // Instantaneous (current) value of pktRcvBuf, expressed in bytes, including payload and all headers (IP, TCP, SRT)
MsRcvBuf uint64 `json:"recv_buf_ms" format:"uint64"` // The timespan (msec) of acknowledged packets in the receiver's buffer
MsRcvTsbPdDelay uint64 `json:"recv_tsbpd_delay_ms" format:"uint64"` // Timestamp-based Packet Delivery Delay value set on the socket via SRTO_RCVLATENCY or SRTO_LATENCY
PktReorderTolerance uint64 `json:"reorder_tolerance_pkt" format:"uint64"` // Instant value of the packet reorder tolerance
PktRcvAvgBelatedTime uint64 `json:"pkt_recv_avg_belated_time_ms" format:"uint64"` // Accumulated difference between the current time and the time-to-play of a packet that is received late
}
type SRTLog struct {
Timestamp int64 `json:"ts" format:"int64"`
Message []string `json:"msg"`
}
// SRTConnection represents a SRT connection with statistics and logs
type SRTConnection struct {
Log map[string][]SRTLog `json:"log"`
Stats SRTStatistics `json:"stats"`
}
// SRTChannel represents a SRT publishing connection with its subscribers
type SRTChannel struct {
Name string `json:"name"`
SocketId uint32 `json:"socketid"`
Subscriber []uint32 `json:"subscriber"`
Connections map[uint32]SRTConnection `json:"connections"`
Log map[string][]SRTLog `json:"log"`
}

View File

@ -0,0 +1,7 @@
package api
type WidgetProcess struct {
CurrentSessions uint64 `json:"current_sessions" format:"uint64"`
TotalSessions uint64 `json:"total_sessions" format:"uint64"`
Uptime int64 `json:"uptime"`
}

View File

@ -1,4 +1,4 @@
package client
package coreclient
import (
"bytes"
@ -10,14 +10,16 @@ import (
"strings"
"time"
"github.com/datarhei/core/v16/app"
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core-client-go/v16/api"
"github.com/Masterminds/semver/v3"
)
var coreapp = app.Name
var coreversion = "~" + app.Version.MinorString()
const (
coreapp = "datarhei-core"
coremajor = 16
coreversion = "^16.7.2" // first public release
)
type HTTPClient interface {
Do(req *http.Request) (*http.Response, error)
@ -30,55 +32,71 @@ type RestClient interface {
// ID returns the ID of the connected datarhei Core
ID() string
// Tokens returns the access and refresh token of the current session
Tokens() (string, string)
// Address returns the address of the connected datarhei Core
Address() string
About() api.About // GET /
Ping() (bool, time.Duration) // GET /ping
Ping() (bool, time.Duration)
Config() (api.Config, error) // GET /config
ConfigSet(config api.ConfigData) error // POST /config
ConfigReload() error // GET /config/reload
About() api.About // GET /
DiskFSList(sort, order string) ([]api.FileInfo, error) // GET /fs/disk
DiskFSHasFile(path string) bool // HEAD /fs/disk/{path}
DiskFSGetFile(path string) (io.ReadCloser, error) // GET /fs/disk/{path}
DiskFSDeleteFile(path string) error // DELETE /fs/disk/{path}
DiskFSAddFile(path string, data io.Reader) error // PUT /fs/disk/{path}
Config() (int64, api.Config, error) // GET /config
ConfigSet(config interface{}) error // POST /config
ConfigReload() error // GET /config/reload
MemFSList(sort, order string) ([]api.FileInfo, error) // GET /fs/mem
MemFSHasFile(path string) bool // HEAD /fs/mem/{path}
MemFSGetFile(path string) (io.ReadCloser, error) // GET /fs/mem/{path}
MemFSDeleteFile(path string) error // DELETE /fs/mem/{path}
MemFSAddFile(path string, data io.Reader) error // PUT /fs/mem/{path}
Graph(query api.GraphQuery) (api.GraphResponse, error) // POST /graph
DiskFSList(sort, order string) ([]api.FileInfo, error) // GET /v3/fs/disk
DiskFSHasFile(path string) bool // HEAD /v3/fs/disk/{path}
DiskFSGetFile(path string) (io.ReadCloser, error) // GET /v3/fs/disk/{path}
DiskFSDeleteFile(path string) error // DELETE /v3/fs/disk/{path}
DiskFSAddFile(path string, data io.Reader) error // PUT /v3/fs/disk/{path}
MemFSList(sort, order string) ([]api.FileInfo, error) // GET /v3/fs/mem
MemFSHasFile(path string) bool // HEAD /v3/fs/mem/{path}
MemFSGetFile(path string) (io.ReadCloser, error) // GET /v3/fs/mem/{path}
MemFSDeleteFile(path string) error // DELETE /v3/fs/mem/{path}
MemFSAddFile(path string, data io.Reader) error // PUT /v3/fs/mem/{path}
FilesystemList(name, pattern, sort, order string) ([]api.FileInfo, error) // GET /v3/fs/{name}
FilesystemHasFile(name, path string) bool // HEAD /v3/fs/{name}/{path}
FilesystemGetFile(name, path string) (io.ReadCloser, error) // GET /v3/fs/{name}/{path}
FilesystemDeleteFile(name, path string) error // DELETE /v3/fs/{name}/{path}
FilesystemAddFile(name, path string, data io.Reader) error // PUT /v3/fs/{name}/{path}
Log() ([]api.LogEvent, error) // GET /log
Metadata(id, key string) (api.Metadata, error) // GET /metadata/{key}
MetadataSet(id, key string, metadata api.Metadata) error // PUT /metadata/{key}
Metadata(key string) (api.Metadata, error) // GET /v3/metadata/{key}
MetadataSet(key string, metadata api.Metadata) error // PUT /v3/metadata/{key}
Metrics(api.MetricsQuery) (api.MetricsResponse, error) // POST /metrics
MetricsList() ([]api.MetricsDescription, error) // GET /v3/metrics
Metrics(query api.MetricsQuery) (api.MetricsResponse, error) // POST /v3/metrics
ProcessList(id, filter []string) ([]api.Process, error) // GET /process
Process(id string, filter []string) (api.Process, error) // GET /process/{id}
ProcessAdd(p api.ProcessConfig) error // POST /process
ProcessDelete(id string) error // DELETE /process/{id}
ProcessCommand(id, command string) error // PUT /process/{id}/command
ProcessProbe(id string) (api.Probe, error) // GET /process/{id}/probe
ProcessConfig(id string) (api.ProcessConfig, error) // GET /process/{id}/config
ProcessReport(id string) (api.ProcessReport, error) // GET /process/{id}/report
ProcessState(id string) (api.ProcessState, error) // GET /process/{id}/state
ProcessMetadata(id, key string) (api.Metadata, error) // GET /process/{id}/metadata/{key}
ProcessMetadataSet(id, key string, metadata api.Metadata) error // PUT /process/{id}/metadata/{key}
ProcessList(opts ProcessListOptions) ([]api.Process, error) // GET /v3/process
ProcessAdd(p api.ProcessConfig) error // POST /v3/process
Process(id string, filter []string) (api.Process, error) // GET /v3/process/{id}
ProcessUpdate(id string, p api.ProcessConfig) error // PUT /v3/process/{id}
ProcessDelete(id string) error // DELETE /v3/process/{id}
ProcessCommand(id, command string) error // PUT /v3/process/{id}/command
ProcessProbe(id string) (api.Probe, error) // GET /v3/process/{id}/probe
ProcessConfig(id string) (api.ProcessConfig, error) // GET /v3/process/{id}/config
ProcessReport(id string) (api.ProcessReport, error) // GET /v3/process/{id}/report
ProcessState(id string) (api.ProcessState, error) // GET /v3/process/{id}/state
ProcessMetadata(id, key string) (api.Metadata, error) // GET /v3/process/{id}/metadata/{key}
ProcessMetadataSet(id, key string, metadata api.Metadata) error // PUT /v3/process/{id}/metadata/{key}
RTMPChannels() ([]api.RTMPChannel, error) // GET /rtmp
SRTChannels() ([]api.SRTChannel, error) // GET /srt
RTMPChannels() ([]api.RTMPChannel, error) // GET /v3/rtmp
SRTChannels() ([]api.SRTChannel, error) // GET /v3/srt
Sessions(collectors []string) (api.SessionsSummary, error) // GET /session
SessionsActive(collectors []string) (api.SessionsActive, error) // GET /session/active
Sessions(collectors []string) (api.SessionsSummary, error) // GET /v3/session
SessionsActive(collectors []string) (api.SessionsActive, error) // GET /v3/session/active
Skills() (api.Skills, error) // GET /skills
SkillsReload() error // GET /skills/reload
Skills() (api.Skills, error) // GET /v3/skills
SkillsReload() error // GET /v3/skills/reload
WidgetProcess(id string) (api.WidgetProcess, error) // GET /v3/widget/process/{id}
}
// Config is the configuration for a new REST API client.
@ -86,10 +104,14 @@ type Config struct {
// Address is the address of the datarhei Core to connect to.
Address string
// Username and Password are credentials to authorize access to the API.
// Username and password are credentials to authorize access to the API.
Username string
Password string
// Access and refresh token from an existing session to authorize access to the API.
AccessToken string
RefreshToken string
// Auth0Token is a valid Auth0 token to authorize access to the API.
Auth0Token string
@ -108,18 +130,25 @@ type restclient struct {
auth0Token string
client HTTPClient
about api.About
version struct {
connectedCore *semver.Version
methods map[string]*semver.Constraints
}
}
// New returns a new REST API client for the given config. The error is non-nil
// in case of an error.
func New(config Config) (RestClient, error) {
r := &restclient{
address: config.Address,
prefix: "/api",
username: config.Username,
password: config.Password,
auth0Token: config.Auth0Token,
client: config.Client,
address: config.Address,
prefix: "/api",
username: config.Username,
password: config.Password,
accessToken: config.AccessToken,
refreshToken: config.RefreshToken,
auth0Token: config.Auth0Token,
client: config.Client,
}
u, err := url.Parse(r.address)
@ -159,22 +188,43 @@ func New(config Config) (RestClient, error) {
return nil, fmt.Errorf("didn't receive the expected API response (got: %s, want: %s)", r.about.Name, coreapp)
}
if len(r.about.ID) == 0 {
mustNewConstraint := func(constraint string) *semver.Constraints {
v, _ := semver.NewConstraint(constraint)
return v
}
r.version.methods = map[string]*semver.Constraints{
"GET/api/v3/srt": mustNewConstraint("^16.9.0"),
"GET/api/v3/metrics": mustNewConstraint("^16.10.0"),
}
if len(r.about.ID) != 0 {
c, _ := semver.NewConstraint(coreversion)
v, err := semver.NewVersion(r.about.Version.Number)
if err != nil {
return nil, err
}
if !c.Check(v) {
return nil, fmt.Errorf("the core version (%s) is not supported, because a version %s is required", r.about.Version.Number, coreversion)
}
r.version.connectedCore = v
} else {
v, err := semver.NewVersion(r.about.Version.Number)
if err != nil {
return nil, err
}
if coremajor != v.Major() {
return nil, fmt.Errorf("the core major version (%d) is not supported, because %d is required", v.Major(), coremajor)
}
if err := r.login(); err != nil {
return nil, err
}
}
c, _ := semver.NewConstraint(coreversion)
v, err := semver.NewVersion(r.about.Version.Number)
if err != nil {
return nil, err
}
if !c.Check(v) {
return nil, fmt.Errorf("the core version (%s) is not supported (%s)", r.about.Version.Number, coreversion)
}
return r, nil
}
@ -186,6 +236,10 @@ func (r *restclient) ID() string {
return r.about.ID
}
func (r *restclient) Tokens() (string, string) {
return r.accessToken, r.refreshToken
}
func (r *restclient) Address() string {
return r.address
}
@ -299,11 +353,35 @@ func (r *restclient) login() error {
return fmt.Errorf("login to the API failed")
}
c, _ := semver.NewConstraint(coreversion)
v, err := semver.NewVersion(about.Version.Number)
if err != nil {
return err
}
if !c.Check(v) {
return fmt.Errorf("the core version (%s) is not supported, because a version %s is required", about.Version.Number, coreversion)
}
r.version.connectedCore = v
r.about = about
return nil
}
func (r *restclient) checkVersion(method, path string) error {
c := r.version.methods[method+path]
if c == nil {
return nil
}
if !c.Check(r.version.connectedCore) {
return fmt.Errorf("this method is only available in version %s of the core", c.String())
}
return nil
}
func (r *restclient) refresh() error {
req, err := http.NewRequest("GET", r.address+r.prefix+"/login/refresh", nil)
if err != nil {
@ -364,8 +442,21 @@ func (r *restclient) info() (api.About, error) {
return about, nil
}
func (r *restclient) request(req *http.Request) (int, io.ReadCloser, error) {
resp, err := r.client.Do(req)
if err != nil {
return -1, nil, err
}
return resp.StatusCode, resp.Body, nil
}
func (r *restclient) stream(method, path, contentType string, data io.Reader) (io.ReadCloser, error) {
req, err := http.NewRequest(method, r.address+r.prefix+"/v3"+path, data)
if err := r.checkVersion(method, r.prefix+path); err != nil {
return nil, err
}
req, err := http.NewRequest(method, r.address+r.prefix+path, data)
if err != nil {
return nil, err
}
@ -395,15 +486,31 @@ func (r *restclient) stream(method, path, contentType string, data io.Reader) (i
}
if status < 200 || status >= 300 {
e := api.Error{}
e := api.Error{
Code: status,
}
defer body.Close()
x, _ := io.ReadAll(body)
data, err := io.ReadAll(body)
if err != nil {
return nil, e
}
json.Unmarshal(x, &e)
e.Body = data
return nil, fmt.Errorf("%w", e)
err = json.Unmarshal(data, &e)
if err != nil {
return nil, e
}
// In case it's not an api.Error, reconstruct the return code. With this
// and the body, the caller can reconstruct the correct error.
if e.Code == 0 {
e.Code = status
}
return nil, e
}
return body, nil
@ -417,16 +524,7 @@ func (r *restclient) call(method, path, contentType string, data io.Reader) ([]b
defer body.Close()
x, _ := io.ReadAll(body)
x, err := io.ReadAll(body)
return x, nil
}
func (r *restclient) request(req *http.Request) (int, io.ReadCloser, error) {
resp, err := r.client.Do(req)
if err != nil {
return -1, nil, err
}
return resp.StatusCode, resp.Body, nil
return x, err
}

View File

@ -0,0 +1,91 @@
package coreclient
import (
"bytes"
"encoding/json"
"github.com/datarhei/core-client-go/v16/api"
)
type configVersion struct {
Config struct {
Version int64 `json:"version"`
} `json:"config"`
}
func (r *restclient) Config() (int64, api.Config, error) {
version := configVersion{}
data, err := r.call("GET", "/v3/config", "", nil)
if err != nil {
return 0, api.Config{}, err
}
if err := json.Unmarshal(data, &version); err != nil {
return 0, api.Config{}, err
}
config := api.Config{}
if err := json.Unmarshal(data, &config); err != nil {
return 0, api.Config{}, err
}
configdata, err := json.Marshal(config.Config)
if err != nil {
return 0, api.Config{}, err
}
switch version.Config.Version {
case 1:
cfg := api.ConfigV1{}
err := json.Unmarshal(configdata, &cfg)
if err != nil {
return 0, api.Config{}, err
}
config.Config = cfg
case 2:
cfg := api.ConfigV2{}
err := json.Unmarshal(configdata, &cfg)
if err != nil {
return 0, api.Config{}, err
}
config.Config = cfg
case 3:
cfg := api.ConfigV3{}
err := json.Unmarshal(configdata, &cfg)
if err != nil {
return 0, api.Config{}, err
}
config.Config = cfg
}
return version.Config.Version, config, nil
}
func (r *restclient) ConfigSet(config interface{}) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(config)
_, err := r.call("PUT", "/v3/config", "application/json", &buf)
if e, ok := err.(api.Error); ok {
if e.Code == 409 {
ce := api.ConfigError{}
if err := json.Unmarshal(e.Body, &ce); err != nil {
return e
}
return ce
}
}
return err
}
func (r *restclient) ConfigReload() error {
_, err := r.call("GET", "/v3/config/reload", "", nil)
return err
}

View File

@ -0,0 +1,27 @@
package coreclient
import (
"io"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) DiskFSList(sort, order string) ([]api.FileInfo, error) {
return r.FilesystemList("disk", "", sort, order)
}
func (r *restclient) DiskFSHasFile(path string) bool {
return r.FilesystemHasFile("disk", path)
}
func (r *restclient) DiskFSGetFile(path string) (io.ReadCloser, error) {
return r.FilesystemGetFile("disk", path)
}
func (r *restclient) DiskFSDeleteFile(path string) error {
return r.FilesystemDeleteFile("disk", path)
}
func (r *restclient) DiskFSAddFile(path string, data io.Reader) error {
return r.FilesystemAddFile("disk", path, data)
}

22
vendor/github.com/datarhei/core-client-go/v16/doc.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
/*
Package coreclient provides a client for the datarhei Core API (https://github.com/datarhei/core)
Example for retrieving a list of all processes:
import "github.com/datarhei/core-client-go/v16"
client, err := coreclient.New(coreclient.Config{
Address: "https://example.com:8080",
Username: "foo",
Password: "bar",
})
if err != nil {
...
}
processes, err := client.ProcessList(coreclient.ProcessListOptions{})
if err != nil {
...
}
*/
package coreclient

77
vendor/github.com/datarhei/core-client-go/v16/fs.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
package coreclient
import (
"encoding/json"
"io"
"net/url"
"path/filepath"
"github.com/datarhei/core-client-go/v16/api"
)
const (
SORT_DEFAULT = "none"
SORT_NONE = "none"
SORT_NAME = "name"
SORT_SIZE = "size"
SORT_LASTMOD = "lastmod"
ORDER_DEFAULT = "asc"
ORDER_ASC = "asc"
ORDER_DESC = "desc"
)
func (r *restclient) FilesystemList(name, pattern, sort, order string) ([]api.FileInfo, error) {
var files []api.FileInfo
values := url.Values{}
values.Set("glob", pattern)
values.Set("sort", sort)
values.Set("order", order)
data, err := r.call("GET", "/v3/fs/"+url.PathEscape(name)+"?"+values.Encode(), "", nil)
if err != nil {
return files, err
}
err = json.Unmarshal(data, &files)
return files, err
}
func (r *restclient) FilesystemHasFile(name, path string) bool {
if !filepath.IsAbs(path) {
path = "/" + path
}
_, err := r.call("HEAD", "/v3/fs/"+url.PathEscape(name)+path, "", nil)
return err == nil
}
func (r *restclient) FilesystemGetFile(name, path string) (io.ReadCloser, error) {
if !filepath.IsAbs(path) {
path = "/" + path
}
return r.stream("GET", "/v3/fs/"+url.PathEscape(name)+path, "", nil)
}
func (r *restclient) FilesystemDeleteFile(name, path string) error {
if !filepath.IsAbs(path) {
path = "/" + path
}
_, err := r.call("DELETE", "/v3/fs/"+url.PathEscape(name)+path, "", nil)
return err
}
func (r *restclient) FilesystemAddFile(name, path string, data io.Reader) error {
if !filepath.IsAbs(path) {
path = "/" + path
}
_, err := r.call("PUT", "/v3/fs/"+url.PathEscape(name)+path, "application/data", data)
return err
}

25
vendor/github.com/datarhei/core-client-go/v16/graph.go generated vendored Normal file
View File

@ -0,0 +1,25 @@
package coreclient
import (
"bytes"
"encoding/json"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) Graph(query api.GraphQuery) (api.GraphResponse, error) {
var resp api.GraphResponse
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(query)
data, err := r.call("PUT", "/v3/graph", "application/json", &buf)
if err != nil {
return resp, err
}
err = json.Unmarshal(data, &resp)
return resp, err
}

View File

@ -1,15 +1,15 @@
package client
package coreclient
import (
"encoding/json"
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) Log() ([]api.LogEvent, error) {
var log []api.LogEvent
data, err := r.call("GET", "/log", "", nil)
data, err := r.call("GET", "/v3/log?format=raw", "", nil)
if err != nil {
return log, err
}

27
vendor/github.com/datarhei/core-client-go/v16/memfs.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package coreclient
import (
"io"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) MemFSList(sort, order string) ([]api.FileInfo, error) {
return r.FilesystemList("mem", "", sort, order)
}
func (r *restclient) MemFSHasFile(path string) bool {
return r.FilesystemHasFile("mem", path)
}
func (r *restclient) MemFSGetFile(path string) (io.ReadCloser, error) {
return r.FilesystemGetFile("mem", path)
}
func (r *restclient) MemFSDeleteFile(path string) error {
return r.FilesystemDeleteFile("mem", path)
}
func (r *restclient) MemFSAddFile(path string, data io.Reader) error {
return r.FilesystemAddFile("mem", path, data)
}

View File

@ -0,0 +1,46 @@
package coreclient
import (
"bytes"
"encoding/json"
"net/url"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) Metadata(key string) (api.Metadata, error) {
var m api.Metadata
path := "/v3/metadata"
if len(key) != 0 {
path += "/" + url.PathEscape(key)
}
data, err := r.call("GET", path, "", nil)
if err != nil {
return m, err
}
err = json.Unmarshal(data, &m)
return m, err
}
func (r *restclient) MetadataSet(key string, metadata api.Metadata) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(metadata)
path := "/v3/metadata"
if len(key) != 0 {
path += "/" + url.PathEscape(key)
}
_, err := r.call("PUT", path, "application/json", &buf)
if err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,38 @@
package coreclient
import (
"bytes"
"encoding/json"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) MetricsList() ([]api.MetricsDescription, error) {
descriptions := []api.MetricsDescription{}
data, err := r.call("GET", "/v3/metrics", "application/json", nil)
if err != nil {
return descriptions, err
}
err = json.Unmarshal(data, &descriptions)
return descriptions, err
}
func (r *restclient) Metrics(query api.MetricsQuery) (api.MetricsResponse, error) {
var m api.MetricsResponse
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(query)
data, err := r.call("POST", "/v3/metrics", "application/json", &buf)
if err != nil {
return m, err
}
err = json.Unmarshal(data, &m)
return m, err
}

View File

@ -1,4 +1,4 @@
package client
package coreclient
import (
"bytes"
@ -6,17 +6,32 @@ import (
"net/url"
"strings"
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) ProcessList(id []string, filter []string) ([]api.Process, error) {
type ProcessListOptions struct {
ID []string
Filter []string
Reference string
IDPattern string
RefPattern string
}
func (p *ProcessListOptions) Query() string {
values := url.Values{}
values.Set("id", strings.Join(p.ID, ","))
values.Set("filter", strings.Join(p.Filter, ","))
values.Set("reference", p.Reference)
values.Set("idpattern", p.IDPattern)
values.Set("refpattern", p.RefPattern)
return values.Encode()
}
func (r *restclient) ProcessList(opts ProcessListOptions) ([]api.Process, error) {
var processes []api.Process
values := url.Values{}
values.Set("id", strings.Join(id, ","))
values.Set("filter", strings.Join(filter, ","))
data, err := r.call("GET", "/process?"+values.Encode(), "", nil)
data, err := r.call("GET", "/v3/process?"+opts.Query(), "", nil)
if err != nil {
return processes, err
}
@ -32,7 +47,7 @@ func (r *restclient) Process(id string, filter []string) (api.Process, error) {
values := url.Values{}
values.Set("filter", strings.Join(filter, ","))
data, err := r.call("GET", "/process/"+id+"?"+values.Encode(), "", nil)
data, err := r.call("GET", "/v3/process/"+url.PathEscape(id)+"?"+values.Encode(), "", nil)
if err != nil {
return info, err
}
@ -48,7 +63,21 @@ func (r *restclient) ProcessAdd(p api.ProcessConfig) error {
e := json.NewEncoder(&buf)
e.Encode(p)
_, err := r.call("POST", "/process", "application/json", &buf)
_, err := r.call("POST", "/v3/process", "application/json", &buf)
if err != nil {
return err
}
return nil
}
func (r *restclient) ProcessUpdate(id string, p api.ProcessConfig) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(p)
_, err := r.call("PUT", "/v3/process/"+url.PathEscape(id)+"", "application/json", &buf)
if err != nil {
return err
}
@ -57,7 +86,7 @@ func (r *restclient) ProcessAdd(p api.ProcessConfig) error {
}
func (r *restclient) ProcessDelete(id string) error {
r.call("DELETE", "/process/"+id, "", nil)
r.call("DELETE", "/v3/process/"+url.PathEscape(id), "", nil)
return nil
}
@ -70,7 +99,7 @@ func (r *restclient) ProcessCommand(id, command string) error {
Command: command,
})
_, err := r.call("PUT", "/process/"+id+"/command", "application/json", &buf)
_, err := r.call("PUT", "/v3/process/"+url.PathEscape(id)+"/command", "application/json", &buf)
if err != nil {
return err
}
@ -81,7 +110,7 @@ func (r *restclient) ProcessCommand(id, command string) error {
func (r *restclient) ProcessProbe(id string) (api.Probe, error) {
var p api.Probe
data, err := r.call("GET", "/process/"+id+"/probe", "", nil)
data, err := r.call("GET", "/v3/process/"+url.PathEscape(id)+"/probe", "", nil)
if err != nil {
return p, err
}
@ -94,7 +123,7 @@ func (r *restclient) ProcessProbe(id string) (api.Probe, error) {
func (r *restclient) ProcessConfig(id string) (api.ProcessConfig, error) {
var p api.ProcessConfig
data, err := r.call("GET", "/process/"+id+"/config", "", nil)
data, err := r.call("GET", "/v3/process/"+url.PathEscape(id)+"/config", "", nil)
if err != nil {
return p, err
}
@ -107,7 +136,7 @@ func (r *restclient) ProcessConfig(id string) (api.ProcessConfig, error) {
func (r *restclient) ProcessReport(id string) (api.ProcessReport, error) {
var p api.ProcessReport
data, err := r.call("GET", "/process/"+id+"/report", "", nil)
data, err := r.call("GET", "/v3/process/"+url.PathEscape(id)+"/report", "", nil)
if err != nil {
return p, err
}
@ -120,7 +149,7 @@ func (r *restclient) ProcessReport(id string) (api.ProcessReport, error) {
func (r *restclient) ProcessState(id string) (api.ProcessState, error) {
var p api.ProcessState
data, err := r.call("GET", "/process/"+id+"/state", "", nil)
data, err := r.call("GET", "/v3/process/"+url.PathEscape(id)+"/state", "", nil)
if err != nil {
return p, err
}
@ -133,9 +162,9 @@ func (r *restclient) ProcessState(id string) (api.ProcessState, error) {
func (r *restclient) ProcessMetadata(id, key string) (api.Metadata, error) {
var m api.Metadata
path := "/process/" + id + "/metadata"
path := "/v3/process/" + url.PathEscape(id) + "/metadata"
if len(key) != 0 {
path += "/" + key
path += "/" + url.PathEscape(key)
}
data, err := r.call("GET", path, "", nil)
@ -154,7 +183,7 @@ func (r *restclient) ProcessMetadataSet(id, key string, metadata api.Metadata) e
e := json.NewEncoder(&buf)
e.Encode(metadata)
_, err := r.call("PUT", "/process/"+id+"/metadata/"+key, "application/json", &buf)
_, err := r.call("PUT", "/v3/process/"+url.PathEscape(id)+"/metadata/"+url.PathEscape(key), "application/json", &buf)
if err != nil {
return err
}

View File

@ -1,15 +1,15 @@
package client
package coreclient
import (
"encoding/json"
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) RTMPChannels() ([]api.RTMPChannel, error) {
var m []api.RTMPChannel
data, err := r.call("GET", "/rtmp", "", nil)
data, err := r.call("GET", "/v3/rtmp", "", nil)
if err != nil {
return m, err
}

View File

@ -1,11 +1,11 @@
package client
package coreclient
import (
"encoding/json"
"net/url"
"strings"
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) Sessions(collectors []string) (api.SessionsSummary, error) {
@ -14,7 +14,7 @@ func (r *restclient) Sessions(collectors []string) (api.SessionsSummary, error)
values := url.Values{}
values.Set("collectors", strings.Join(collectors, ","))
data, err := r.call("GET", "/sessions?"+values.Encode(), "", nil)
data, err := r.call("GET", "/v3/sessions?"+values.Encode(), "", nil)
if err != nil {
return sessions, err
}
@ -30,7 +30,7 @@ func (r *restclient) SessionsActive(collectors []string) (api.SessionsActive, er
values := url.Values{}
values.Set("collectors", strings.Join(collectors, ","))
data, err := r.call("GET", "/sessions/active?"+values.Encode(), "", nil)
data, err := r.call("GET", "/v3/sessions/active?"+values.Encode(), "", nil)
if err != nil {
return sessions, err
}

View File

@ -1,15 +1,15 @@
package client
package coreclient
import (
"encoding/json"
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) Skills() (api.Skills, error) {
var skills api.Skills
data, err := r.call("GET", "/skills", "", nil)
data, err := r.call("GET", "/v3/skills", "", nil)
if err != nil {
return skills, err
}
@ -20,7 +20,7 @@ func (r *restclient) Skills() (api.Skills, error) {
}
func (r *restclient) SkillsReload() error {
_, err := r.call("GET", "/skills/reload", "", nil)
_, err := r.call("GET", "/v3/skills/reload", "", nil)
return err
}

View File

@ -1,17 +1,17 @@
package client
package coreclient
import (
"encoding/json"
"github.com/datarhei/core/v16/http/api"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) SRTChannels() ([]api.SRTChannel, error) {
var m []api.SRTChannel
data, err := r.call("GET", "/srt", "", nil)
data, err := r.call("GET", "/v3/srt", "", nil)
if err != nil {
return m, err
return nil, err
}
err = json.Unmarshal(data, &m)

View File

@ -0,0 +1,21 @@
package coreclient
import (
"encoding/json"
"net/url"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) WidgetProcess(id string) (api.WidgetProcess, error) {
var w api.WidgetProcess
data, err := r.call("GET", "/v3/widget/process"+url.PathEscape(id), "", nil)
if err != nil {
return w, err
}
err = json.Unmarshal(data, &w)
return w, err
}

8
vendor/modules.txt vendored
View File

@ -27,8 +27,8 @@ github.com/99designs/gqlgen/plugin/servergen
# github.com/KyleBanks/depth v1.2.1
## explicit
github.com/KyleBanks/depth
# github.com/Masterminds/semver/v3 v3.1.1
## explicit; go 1.12
# github.com/Masterminds/semver/v3 v3.2.1
## explicit; go 1.18
github.com/Masterminds/semver/v3
# github.com/agnivade/levenshtein v1.1.1
## explicit; go 1.13
@ -57,6 +57,10 @@ github.com/cespare/xxhash/v2
# github.com/cpuguy83/go-md2man/v2 v2.0.1
## explicit; go 1.11
github.com/cpuguy83/go-md2man/v2/md2man
# github.com/datarhei/core-client-go/v16 v16.11.1-0.20230512155342-18a7ac72df3a
## explicit; go 1.18
github.com/datarhei/core-client-go/v16
github.com/datarhei/core-client-go/v16/api
# github.com/datarhei/gosrt v0.3.1
## explicit; go 1.16
github.com/datarhei/gosrt