mirror of
https://github.com/datarhei/core.git
synced 2025-10-05 07:57:13 +08:00

The scheduler allows to define when a process should run. It can be either a timestamp in RFC3339 format or a crontab expression. If a scheduler is given, reconnect and the reconnect delay will only apply to processes that exited as failed. The timeout allows to define when a process should be gracefully stopped. It is measured from the actual start of that process including all reconnects due to failures. If the process finished regularly, the timeout will be reset.
225 lines
6.5 KiB
Go
225 lines
6.5 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
|
|
"github.com/datarhei/core/v16/restream/app"
|
|
"github.com/lithammer/shortuuid/v4"
|
|
)
|
|
|
|
// 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"`
|
|
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"`
|
|
Timeout uint64 `json:"timeout_seconds" format:"uint64"`
|
|
Scheduler string `json:"scheduler"`
|
|
Limits ProcessConfigLimits `json:"limits"`
|
|
}
|
|
|
|
// Marshal converts a process config in API representation to a restreamer process config
|
|
func (cfg *ProcessConfig) Marshal() *app.Config {
|
|
p := &app.Config{
|
|
ID: cfg.ID,
|
|
Reference: cfg.Reference,
|
|
Options: cfg.Options,
|
|
Reconnect: cfg.Reconnect,
|
|
ReconnectDelay: cfg.ReconnectDelay,
|
|
Autostart: cfg.Autostart,
|
|
StaleTimeout: cfg.StaleTimeout,
|
|
Timeout: cfg.Timeout,
|
|
Scheduler: cfg.Scheduler,
|
|
LimitCPU: cfg.Limits.CPU,
|
|
LimitMemory: cfg.Limits.Memory * 1024 * 1024,
|
|
LimitWaitFor: cfg.Limits.WaitFor,
|
|
}
|
|
|
|
cfg.generateInputOutputIDs(cfg.Input)
|
|
|
|
for _, x := range cfg.Input {
|
|
p.Input = append(p.Input, app.ConfigIO{
|
|
ID: x.ID,
|
|
Address: x.Address,
|
|
Options: x.Options,
|
|
})
|
|
}
|
|
|
|
cfg.generateInputOutputIDs(cfg.Output)
|
|
|
|
for _, x := range cfg.Output {
|
|
output := app.ConfigIO{
|
|
ID: x.ID,
|
|
Address: x.Address,
|
|
Options: x.Options,
|
|
}
|
|
|
|
for _, c := range x.Cleanup {
|
|
output.Cleanup = append(output.Cleanup, app.ConfigIOCleanup{
|
|
Pattern: c.Pattern,
|
|
MaxFiles: c.MaxFiles,
|
|
MaxFileAge: c.MaxFileAge,
|
|
PurgeOnDelete: c.PurgeOnDelete,
|
|
})
|
|
}
|
|
|
|
p.Output = append(p.Output, output)
|
|
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
func (cfg *ProcessConfig) generateInputOutputIDs(ioconfig []ProcessConfigIO) {
|
|
ids := map[string]struct{}{}
|
|
|
|
for _, io := range ioconfig {
|
|
if len(io.ID) == 0 {
|
|
continue
|
|
}
|
|
|
|
ids[io.ID] = struct{}{}
|
|
}
|
|
|
|
for i, io := range ioconfig {
|
|
if len(io.ID) != 0 {
|
|
continue
|
|
}
|
|
|
|
for {
|
|
id := shortuuid.New()
|
|
if _, ok := ids[id]; !ok {
|
|
ioconfig[i].ID = id
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Unmarshal converts a restream process config to a process config in API representation
|
|
func (cfg *ProcessConfig) Unmarshal(c *app.Config) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
|
|
cfg.ID = c.ID
|
|
cfg.Reference = c.Reference
|
|
cfg.Type = "ffmpeg"
|
|
cfg.Reconnect = c.Reconnect
|
|
cfg.ReconnectDelay = c.ReconnectDelay
|
|
cfg.Autostart = c.Autostart
|
|
cfg.StaleTimeout = c.StaleTimeout
|
|
cfg.Timeout = c.Timeout
|
|
cfg.Scheduler = c.Scheduler
|
|
cfg.Limits.CPU = c.LimitCPU
|
|
cfg.Limits.Memory = c.LimitMemory / 1024 / 1024
|
|
cfg.Limits.WaitFor = c.LimitWaitFor
|
|
|
|
cfg.Options = make([]string, len(c.Options))
|
|
copy(cfg.Options, c.Options)
|
|
|
|
for _, x := range c.Input {
|
|
io := ProcessConfigIO{
|
|
ID: x.ID,
|
|
Address: x.Address,
|
|
}
|
|
|
|
io.Options = make([]string, len(x.Options))
|
|
copy(io.Options, x.Options)
|
|
|
|
cfg.Input = append(cfg.Input, io)
|
|
}
|
|
|
|
for _, x := range c.Output {
|
|
io := ProcessConfigIO{
|
|
ID: x.ID,
|
|
Address: x.Address,
|
|
}
|
|
|
|
io.Options = make([]string, len(x.Options))
|
|
copy(io.Options, x.Options)
|
|
|
|
for _, c := range x.Cleanup {
|
|
io.Cleanup = append(io.Cleanup, ProcessConfigIOCleanup{
|
|
Pattern: c.Pattern,
|
|
MaxFiles: c.MaxFiles,
|
|
MaxFileAge: c.MaxFileAge,
|
|
PurgeOnDelete: c.PurgeOnDelete,
|
|
})
|
|
}
|
|
|
|
cfg.Output = append(cfg.Output, io)
|
|
}
|
|
}
|
|
|
|
// 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 json.Number `json:"cpu_usage" swaggertype:"number" jsonschema:"type=number"`
|
|
Command []string `json:"command"`
|
|
}
|
|
|
|
// Unmarshal converts a restreamer ffmpeg process state to a state in API representation
|
|
func (s *ProcessState) Unmarshal(state *app.State) {
|
|
if state == nil {
|
|
return
|
|
}
|
|
|
|
s.Order = state.Order
|
|
s.State = state.State
|
|
s.Runtime = int64(state.Duration)
|
|
s.Reconnect = int64(state.Reconnect)
|
|
s.LastLog = state.LastLog
|
|
s.Progress = &Progress{}
|
|
s.Memory = state.Memory
|
|
s.CPU = toNumber(state.CPU)
|
|
s.Command = state.Command
|
|
|
|
s.Progress.Unmarshal(&state.Progress)
|
|
}
|