mirror of
https://github.com/bluenviron/mediamtx
synced 2024-12-13 10:14:58 +00:00
978 lines
22 KiB
Go
978 lines
22 KiB
Go
// Package api contains the API server.
|
|
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
"github.com/bluenviron/mediamtx/internal/defs"
|
|
"github.com/bluenviron/mediamtx/internal/logger"
|
|
"github.com/bluenviron/mediamtx/internal/protocols/httpserv"
|
|
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
|
)
|
|
|
|
func interfaceIsEmpty(i interface{}) bool {
|
|
return reflect.ValueOf(i).Kind() != reflect.Ptr || reflect.ValueOf(i).IsNil()
|
|
}
|
|
|
|
func paginate2(itemsPtr interface{}, itemsPerPage int, page int) int {
|
|
ritems := reflect.ValueOf(itemsPtr).Elem()
|
|
|
|
itemsLen := ritems.Len()
|
|
if itemsLen == 0 {
|
|
return 0
|
|
}
|
|
|
|
pageCount := (itemsLen / itemsPerPage)
|
|
if (itemsLen % itemsPerPage) != 0 {
|
|
pageCount++
|
|
}
|
|
|
|
min := page * itemsPerPage
|
|
if min > itemsLen {
|
|
min = itemsLen
|
|
}
|
|
|
|
max := (page + 1) * itemsPerPage
|
|
if max > itemsLen {
|
|
max = itemsLen
|
|
}
|
|
|
|
ritems.Set(ritems.Slice(min, max))
|
|
|
|
return pageCount
|
|
}
|
|
|
|
func paginate(itemsPtr interface{}, itemsPerPageStr string, pageStr string) (int, error) {
|
|
itemsPerPage := 100
|
|
|
|
if itemsPerPageStr != "" {
|
|
tmp, err := strconv.ParseUint(itemsPerPageStr, 10, 31)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
itemsPerPage = int(tmp)
|
|
}
|
|
|
|
page := 0
|
|
|
|
if pageStr != "" {
|
|
tmp, err := strconv.ParseUint(pageStr, 10, 31)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
page = int(tmp)
|
|
}
|
|
|
|
return paginate2(itemsPtr, itemsPerPage, page), nil
|
|
}
|
|
|
|
func sortedKeys(paths map[string]*conf.Path) []string {
|
|
ret := make([]string, len(paths))
|
|
i := 0
|
|
for name := range paths {
|
|
ret[i] = name
|
|
i++
|
|
}
|
|
sort.Strings(ret)
|
|
return ret
|
|
}
|
|
|
|
func paramName(ctx *gin.Context) (string, bool) {
|
|
name := ctx.Param("name")
|
|
|
|
if len(name) < 2 || name[0] != '/' {
|
|
return "", false
|
|
}
|
|
|
|
return name[1:], true
|
|
}
|
|
|
|
// PathManager contains methods used by the API and Metrics server.
|
|
type PathManager interface {
|
|
APIPathsList() (*defs.APIPathList, error)
|
|
APIPathsGet(string) (*defs.APIPath, error)
|
|
}
|
|
|
|
// HLSServer contains methods used by the API and Metrics server.
|
|
type HLSServer interface {
|
|
APIMuxersList() (*defs.APIHLSMuxerList, error)
|
|
APIMuxersGet(string) (*defs.APIHLSMuxer, error)
|
|
}
|
|
|
|
// RTSPServer contains methods used by the API and Metrics server.
|
|
type RTSPServer interface {
|
|
APIConnsList() (*defs.APIRTSPConnsList, error)
|
|
APIConnsGet(uuid.UUID) (*defs.APIRTSPConn, error)
|
|
APISessionsList() (*defs.APIRTSPSessionList, error)
|
|
APISessionsGet(uuid.UUID) (*defs.APIRTSPSession, error)
|
|
APISessionsKick(uuid.UUID) error
|
|
}
|
|
|
|
// RTMPServer contains methods used by the API and Metrics server.
|
|
type RTMPServer interface {
|
|
APIConnsList() (*defs.APIRTMPConnList, error)
|
|
APIConnsGet(uuid.UUID) (*defs.APIRTMPConn, error)
|
|
APIConnsKick(uuid.UUID) error
|
|
}
|
|
|
|
// SRTServer contains methods used by the API and Metrics server.
|
|
type SRTServer interface {
|
|
APIConnsList() (*defs.APISRTConnList, error)
|
|
APIConnsGet(uuid.UUID) (*defs.APISRTConn, error)
|
|
APIConnsKick(uuid.UUID) error
|
|
}
|
|
|
|
// WebRTCServer contains methods used by the API and Metrics server.
|
|
type WebRTCServer interface {
|
|
APISessionsList() (*defs.APIWebRTCSessionList, error)
|
|
APISessionsGet(uuid.UUID) (*defs.APIWebRTCSession, error)
|
|
APISessionsKick(uuid.UUID) error
|
|
}
|
|
|
|
type apiParent interface {
|
|
logger.Writer
|
|
APIConfigSet(conf *conf.Conf)
|
|
}
|
|
|
|
// API is an API server.
|
|
type API struct {
|
|
Address string
|
|
ReadTimeout conf.StringDuration
|
|
Conf *conf.Conf
|
|
PathManager PathManager
|
|
RTSPServer RTSPServer
|
|
RTSPSServer RTSPServer
|
|
RTMPServer RTMPServer
|
|
RTMPSServer RTMPServer
|
|
HLSServer HLSServer
|
|
WebRTCServer WebRTCServer
|
|
SRTServer SRTServer
|
|
Parent apiParent
|
|
|
|
httpServer *httpserv.WrappedServer
|
|
mutex sync.Mutex
|
|
}
|
|
|
|
// Initialize initializes API.
|
|
func (a *API) Initialize() error {
|
|
router := gin.New()
|
|
router.SetTrustedProxies(nil) //nolint:errcheck
|
|
|
|
group := router.Group("/")
|
|
|
|
group.GET("/v3/config/global/get", a.onConfigGlobalGet)
|
|
group.PATCH("/v3/config/global/patch", a.onConfigGlobalPatch)
|
|
|
|
group.GET("/v3/config/pathdefaults/get", a.onConfigPathDefaultsGet)
|
|
group.PATCH("/v3/config/pathdefaults/patch", a.onConfigPathDefaultsPatch)
|
|
|
|
group.GET("/v3/config/paths/list", a.onConfigPathsList)
|
|
group.GET("/v3/config/paths/get/*name", a.onConfigPathsGet)
|
|
group.POST("/v3/config/paths/add/*name", a.onConfigPathsAdd)
|
|
group.PATCH("/v3/config/paths/patch/*name", a.onConfigPathsPatch)
|
|
group.POST("/v3/config/paths/replace/*name", a.onConfigPathsReplace)
|
|
group.DELETE("/v3/config/paths/delete/*name", a.onConfigPathsDelete)
|
|
|
|
group.GET("/v3/paths/list", a.onPathsList)
|
|
group.GET("/v3/paths/get/*name", a.onPathsGet)
|
|
|
|
if !interfaceIsEmpty(a.HLSServer) {
|
|
group.GET("/v3/hlsmuxers/list", a.onHLSMuxersList)
|
|
group.GET("/v3/hlsmuxers/get/*name", a.onHLSMuxersGet)
|
|
}
|
|
|
|
if !interfaceIsEmpty(a.RTSPServer) {
|
|
group.GET("/v3/rtspconns/list", a.onRTSPConnsList)
|
|
group.GET("/v3/rtspconns/get/:id", a.onRTSPConnsGet)
|
|
group.GET("/v3/rtspsessions/list", a.onRTSPSessionsList)
|
|
group.GET("/v3/rtspsessions/get/:id", a.onRTSPSessionsGet)
|
|
group.POST("/v3/rtspsessions/kick/:id", a.onRTSPSessionsKick)
|
|
}
|
|
|
|
if !interfaceIsEmpty(a.RTSPSServer) {
|
|
group.GET("/v3/rtspsconns/list", a.onRTSPSConnsList)
|
|
group.GET("/v3/rtspsconns/get/:id", a.onRTSPSConnsGet)
|
|
group.GET("/v3/rtspssessions/list", a.onRTSPSSessionsList)
|
|
group.GET("/v3/rtspssessions/get/:id", a.onRTSPSSessionsGet)
|
|
group.POST("/v3/rtspssessions/kick/:id", a.onRTSPSSessionsKick)
|
|
}
|
|
|
|
if !interfaceIsEmpty(a.RTMPServer) {
|
|
group.GET("/v3/rtmpconns/list", a.onRTMPConnsList)
|
|
group.GET("/v3/rtmpconns/get/:id", a.onRTMPConnsGet)
|
|
group.POST("/v3/rtmpconns/kick/:id", a.onRTMPConnsKick)
|
|
}
|
|
|
|
if !interfaceIsEmpty(a.RTMPSServer) {
|
|
group.GET("/v3/rtmpsconns/list", a.onRTMPSConnsList)
|
|
group.GET("/v3/rtmpsconns/get/:id", a.onRTMPSConnsGet)
|
|
group.POST("/v3/rtmpsconns/kick/:id", a.onRTMPSConnsKick)
|
|
}
|
|
|
|
if !interfaceIsEmpty(a.WebRTCServer) {
|
|
group.GET("/v3/webrtcsessions/list", a.onWebRTCSessionsList)
|
|
group.GET("/v3/webrtcsessions/get/:id", a.onWebRTCSessionsGet)
|
|
group.POST("/v3/webrtcsessions/kick/:id", a.onWebRTCSessionsKick)
|
|
}
|
|
|
|
if !interfaceIsEmpty(a.SRTServer) {
|
|
group.GET("/v3/srtconns/list", a.onSRTConnsList)
|
|
group.GET("/v3/srtconns/get/:id", a.onSRTConnsGet)
|
|
group.POST("/v3/srtconns/kick/:id", a.onSRTConnsKick)
|
|
}
|
|
|
|
network, address := restrictnetwork.Restrict("tcp", a.Address)
|
|
|
|
var err error
|
|
a.httpServer, err = httpserv.NewWrappedServer(
|
|
network,
|
|
address,
|
|
time.Duration(a.ReadTimeout),
|
|
"",
|
|
"",
|
|
router,
|
|
a,
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
a.Log(logger.Info, "listener opened on "+address)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Close closes the API.
|
|
func (a *API) Close() {
|
|
a.Log(logger.Info, "listener is closing")
|
|
a.httpServer.Close()
|
|
}
|
|
|
|
// Log implements logger.Writer.
|
|
func (a *API) Log(level logger.Level, format string, args ...interface{}) {
|
|
a.Parent.Log(level, "[API] "+format, args...)
|
|
}
|
|
|
|
// error coming from something the user inserted into the request.
|
|
func (a *API) writeError(ctx *gin.Context, status int, err error) {
|
|
// show error in logs
|
|
a.Log(logger.Error, err.Error())
|
|
|
|
// send error in response
|
|
ctx.JSON(status, &defs.APIError{
|
|
Error: err.Error(),
|
|
})
|
|
}
|
|
|
|
func (a *API) onConfigGlobalGet(ctx *gin.Context) {
|
|
a.mutex.Lock()
|
|
c := a.Conf
|
|
a.mutex.Unlock()
|
|
|
|
ctx.JSON(http.StatusOK, c.Global())
|
|
}
|
|
|
|
func (a *API) onConfigGlobalPatch(ctx *gin.Context) {
|
|
var c conf.OptionalGlobal
|
|
err := json.NewDecoder(ctx.Request.Body).Decode(&c)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
|
|
newConf := a.Conf.Clone()
|
|
|
|
newConf.PatchGlobal(&c)
|
|
|
|
err = newConf.Check()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.Conf = newConf
|
|
|
|
// since reloading the configuration can cause the shutdown of the API,
|
|
// call it in a goroutine
|
|
go a.Parent.APIConfigSet(newConf)
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onConfigPathDefaultsGet(ctx *gin.Context) {
|
|
a.mutex.Lock()
|
|
c := a.Conf
|
|
a.mutex.Unlock()
|
|
|
|
ctx.JSON(http.StatusOK, c.PathDefaults)
|
|
}
|
|
|
|
func (a *API) onConfigPathDefaultsPatch(ctx *gin.Context) {
|
|
var p conf.OptionalPath
|
|
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
|
|
newConf := a.Conf.Clone()
|
|
|
|
newConf.PatchPathDefaults(&p)
|
|
|
|
err = newConf.Check()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.Conf = newConf
|
|
a.Parent.APIConfigSet(newConf)
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onConfigPathsList(ctx *gin.Context) {
|
|
a.mutex.Lock()
|
|
c := a.Conf
|
|
a.mutex.Unlock()
|
|
|
|
data := &defs.APIPathConfList{
|
|
Items: make([]*conf.Path, len(c.Paths)),
|
|
}
|
|
|
|
for i, key := range sortedKeys(c.Paths) {
|
|
data.Items[i] = c.Paths[key]
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onConfigPathsGet(ctx *gin.Context) {
|
|
name, ok := paramName(ctx)
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusBadRequest, fmt.Errorf("invalid name"))
|
|
return
|
|
}
|
|
|
|
a.mutex.Lock()
|
|
c := a.Conf
|
|
a.mutex.Unlock()
|
|
|
|
p, ok := c.Paths[name]
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusInternalServerError, fmt.Errorf("path configuration not found"))
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, p)
|
|
}
|
|
|
|
func (a *API) onConfigPathsAdd(ctx *gin.Context) { //nolint:dupl
|
|
name, ok := paramName(ctx)
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusBadRequest, fmt.Errorf("invalid name"))
|
|
return
|
|
}
|
|
|
|
var p conf.OptionalPath
|
|
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
|
|
newConf := a.Conf.Clone()
|
|
|
|
err = newConf.AddPath(name, &p)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = newConf.Check()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.Conf = newConf
|
|
a.Parent.APIConfigSet(newConf)
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onConfigPathsPatch(ctx *gin.Context) { //nolint:dupl
|
|
name, ok := paramName(ctx)
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusBadRequest, fmt.Errorf("invalid name"))
|
|
return
|
|
}
|
|
|
|
var p conf.OptionalPath
|
|
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
|
|
newConf := a.Conf.Clone()
|
|
|
|
err = newConf.PatchPath(name, &p)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = newConf.Check()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.Conf = newConf
|
|
a.Parent.APIConfigSet(newConf)
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onConfigPathsReplace(ctx *gin.Context) { //nolint:dupl
|
|
name, ok := paramName(ctx)
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusBadRequest, fmt.Errorf("invalid name"))
|
|
return
|
|
}
|
|
|
|
var p conf.OptionalPath
|
|
err := json.NewDecoder(ctx.Request.Body).Decode(&p)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
|
|
newConf := a.Conf.Clone()
|
|
|
|
err = newConf.ReplacePath(name, &p)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = newConf.Check()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.Conf = newConf
|
|
a.Parent.APIConfigSet(newConf)
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onConfigPathsDelete(ctx *gin.Context) {
|
|
name, ok := paramName(ctx)
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusBadRequest, fmt.Errorf("invalid name"))
|
|
return
|
|
}
|
|
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
|
|
newConf := a.Conf.Clone()
|
|
|
|
err := newConf.RemovePath(name)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = newConf.Check()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
a.Conf = newConf
|
|
a.Parent.APIConfigSet(newConf)
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onPathsList(ctx *gin.Context) {
|
|
data, err := a.PathManager.APIPathsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onPathsGet(ctx *gin.Context) {
|
|
name, ok := paramName(ctx)
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusBadRequest, fmt.Errorf("invalid name"))
|
|
return
|
|
}
|
|
|
|
data, err := a.PathManager.APIPathsGet(name)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPConnsList(ctx *gin.Context) {
|
|
data, err := a.RTSPServer.APIConnsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPConnsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.RTSPServer.APIConnsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPSessionsList(ctx *gin.Context) {
|
|
data, err := a.RTSPServer.APISessionsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPSessionsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.RTSPServer.APISessionsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPSessionsKick(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = a.RTSPServer.APISessionsKick(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onRTSPSConnsList(ctx *gin.Context) {
|
|
data, err := a.RTSPSServer.APIConnsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPSConnsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.RTSPSServer.APIConnsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPSSessionsList(ctx *gin.Context) {
|
|
data, err := a.RTSPSServer.APISessionsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPSSessionsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.RTSPSServer.APISessionsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTSPSSessionsKick(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = a.RTSPSServer.APISessionsKick(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onRTMPConnsList(ctx *gin.Context) {
|
|
data, err := a.RTMPServer.APIConnsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTMPConnsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.RTMPServer.APIConnsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTMPConnsKick(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = a.RTMPServer.APIConnsKick(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onRTMPSConnsList(ctx *gin.Context) {
|
|
data, err := a.RTMPSServer.APIConnsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTMPSConnsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.RTMPSServer.APIConnsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onRTMPSConnsKick(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = a.RTMPSServer.APIConnsKick(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onHLSMuxersList(ctx *gin.Context) {
|
|
data, err := a.HLSServer.APIMuxersList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onHLSMuxersGet(ctx *gin.Context) {
|
|
name, ok := paramName(ctx)
|
|
if !ok {
|
|
a.writeError(ctx, http.StatusBadRequest, fmt.Errorf("invalid name"))
|
|
return
|
|
}
|
|
|
|
data, err := a.HLSServer.APIMuxersGet(name)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onWebRTCSessionsList(ctx *gin.Context) {
|
|
data, err := a.WebRTCServer.APISessionsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onWebRTCSessionsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.WebRTCServer.APISessionsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onWebRTCSessionsKick(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = a.WebRTCServer.APISessionsKick(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
func (a *API) onSRTConnsList(ctx *gin.Context) {
|
|
data, err := a.SRTServer.APIConnsList()
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
data.ItemCount = len(data.Items)
|
|
pageCount, err := paginate(&data.Items, ctx.Query("itemsPerPage"), ctx.Query("page"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
data.PageCount = pageCount
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onSRTConnsGet(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
data, err := a.SRTServer.APIConnsGet(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, data)
|
|
}
|
|
|
|
func (a *API) onSRTConnsKick(ctx *gin.Context) {
|
|
uuid, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusBadRequest, err)
|
|
return
|
|
}
|
|
|
|
err = a.SRTServer.APIConnsKick(uuid)
|
|
if err != nil {
|
|
a.writeError(ctx, http.StatusInternalServerError, err)
|
|
return
|
|
}
|
|
|
|
ctx.Status(http.StatusOK)
|
|
}
|
|
|
|
// ReloadConf is called by core.
|
|
func (a *API) ReloadConf(conf *conf.Conf) {
|
|
a.mutex.Lock()
|
|
defer a.mutex.Unlock()
|
|
a.Conf = conf
|
|
}
|