pull/132/head
macbookpro 2018-12-08 14:51:49 +08:00
commit 2b79eec814
17 changed files with 853 additions and 262 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
/EasyDarwin.exe
/EasyDarwin.exe~
/logs
/*.log
node_modules
*.db
*.db.bak

View File

@ -77,9 +77,14 @@
### 准备工具
# go tools
go get -u github.com/kardianos/govendor
go get -u github.com/caixw/gobuild
# npm tools
npm i -g apidoc
npm i -g rimraf
### 编译命令

View File

@ -5,9 +5,21 @@ default_password=admin
[rtsp]
port=554
timeout=28800; rtsp 超时时间包括RTSP建立连接与数据收发。
gop_cache_enable=1; 是否使能gop cache。如果使能服务器会缓存最后一个I帧以及其后的非I帧以提高播放速度。但是可能在高并发的情况下带来内存压力。
save_stream_to_local=1; 是否使能推送的同事进行本地存储,使能后则可以进行录像查询与回放。
ffmpeg_path=~/Downloads/ffmpeg-20180719-9cb3d8f-macos64-shared/bin/ffmpeg;easydarwin使用ffmpeg工具来进行存储。这里表示ffmpeg的可执行程序的路径
m3u8_dir_path=~/Downloads/EasyDarwinGoM3u8;本地存储所将要保存的根目录。如果不存在,程序会尝试创建该目录。
ts_duration_second=600;切片文件时长。本地存储时将以该时间段为标准来生成ts文件单位秒
; rtsp 超时时间包括RTSP建立连接与数据收发。
timeout=28800
; 是否使能gop cache。如果使能服务器会缓存最后一个I帧以及其后的非I帧以提高播放速度。但是可能在高并发的情况下带来内存压力。
gop_cache_enable=1
; 是否使能推送的同事进行本地存储,使能后则可以进行录像查询与回放。
save_stream_to_local=0
;easydarwin使用ffmpeg工具来进行存储。这里表示ffmpeg的可执行程序的路径
ffmpeg_path=~/Downloads/ffmpeg-20180719-9cb3d8f-macos64-shared/bin/ffmpeg
;本地存储所将要保存的根目录。如果不存在,程序会尝试创建该目录。
m3u8_dir_path=~/Downloads/EasyDarwinGoM3u8
;切片文件时长。本地存储时将以该时间段为标准来生成ts文件单位秒
ts_duration_second=600

View File

@ -7,9 +7,9 @@
"build:ico": "rsrc -arch amd64 -ico ed.ico -o EasyDarwin_windows.syso",
"build:www": "cd web_src && npm run build && cd .. && apidoc -i routers -o www/apidoc",
"build:doc": "apidoc -i routers -o www/apidoc",
"build:win": "go clean -cache -i ./vendor/github.com/penggy/EasyGoLib/buildtime && go build -tags release -ldflags \"-s -w\" -o EasyDarwin.exe",
"build:lin": "go clean -cache -i ./vendor/github.com/penggy/EasyGoLib/buildtime && go build -tags release -ldflags \"-s -w\" -o easydarwin",
"build:dev": "go clean -cache -i ./vendor/github.com/penggy/EasyGoLib/buildtime && go build -o EasyDarwin.exe",
"build:win": "go build -tags release -ldflags \"-s -w\" -o EasyDarwin.exe",
"build:lin": "go build -tags release -ldflags \"-s -w\" -o easydarwin",
"build:dev": "go build -o EasyDarwin.exe",
"dev": "gobuild -o EasyDarwin.exe",
"dev:lin": "gobuild -o easydarwin",
"dev:www": "cd web_src && npm run start",
@ -23,6 +23,14 @@
"Pushers",
"Players",
"stream",
"StreamStart",
"StreamStop",
"record",
"RecordFolders",
"RecordFiles",
"sys",
"Login",
"Logout",

188
routers/record.go Normal file
View File

@ -0,0 +1,188 @@
package routers
import (
"bytes"
"log"
"math"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/penggy/EasyGoLib/utils"
)
/**
* @apiDefine record
*/
/**
* @apiDefine fileInfo
* @apiSuccess (200) {String} duration
* @apiSuccess (200) {Number} durationMillis
* @apiSuccess (200) {String} path ,http[s]://host:port/record/[path]。
* @apiSuccess (200) {String} folder
*/
/**
* @api {get} /api/v1/record/folders
* @apiGroup record
* @apiName RecordFolders
* @apiParam {Number} [start] ,
* @apiParam {Number} [limit]
* @apiParam {String} [sort]
* @apiParam {String=ascending,descending} [order]
* @apiParam {String} [q]
* @apiSuccess (200) {Number} total
* @apiSuccess (200) {Array} rows
* @apiSuccess (200) {String} rows.folder
*/
func (h *APIHandler) RecordFolders(c *gin.Context) {
mp4Path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
form := utils.NewPageForm()
if err := c.Bind(form); err != nil {
log.Printf("record folder bind err:%v", err)
return
}
var files = make([]interface{}, 0)
if mp4Path != "" {
visit := func(files *[]interface{}) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == mp4Path {
return nil
}
if !info.IsDir() {
return nil
}
*files = append(*files, map[string]interface{}{"folder": info.Name()})
return filepath.SkipDir
}
}
err := filepath.Walk(mp4Path, visit(&files))
if err != nil {
log.Printf("Query RecordFolders err:%v", err)
}
}
pr := utils.NewPageResult(files)
if form.Sort != "" {
pr.Sort(form.Sort, form.Order)
}
pr.Slice(form.Start, form.Limit)
c.IndentedJSON(200, pr)
}
/**
* @api {get} /api/v1/record/files
* @apiGroup record
* @apiName RecordFiles
* @apiParam {Number} folder
* @apiParam {Number} [start] ,
* @apiParam {Number} [limit]
* @apiParam {String} [sort]
* @apiParam {String=ascending,descending} [order]
* @apiParam {String} [q]
* @apiSuccess (200) {Number} total
* @apiSuccess (200) {Array} rows
* @apiSuccess (200) {String} rows.duration
* @apiSuccess (200) {Number} rows.durationMillis
* @apiSuccess (200) {String} rows.path ,m3u8video便http[s]://host:port/record/[path]。
*/
func (h *APIHandler) RecordFiles(c *gin.Context) {
type Form struct {
utils.PageForm
Folder string `form:"folder" binding:"required"`
StartAt int `form:"beginUTCSecond"`
StopAt int `form:"endUTCSecond"`
}
var form = Form{}
form.Limit = math.MaxUint32
err := c.Bind(&form)
if err != nil {
log.Printf("record file bind err:%v", err)
return
}
files := make([]interface{}, 0)
mp4Path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
if mp4Path != "" {
ffmpeg_path := utils.Conf().Section("rtsp").Key("ffmpeg_path").MustString("")
ffmpeg_folder, executable := filepath.Split(ffmpeg_path)
split := strings.Split(executable, ".")
suffix := ""
if len(split) > 1 {
suffix = split[1]
}
ffprobe := ffmpeg_folder + "ffprobe" + suffix
folder := filepath.Join(mp4Path, form.Folder)
visit := func(files *[]interface{}) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == folder {
return nil
}
if info.IsDir() {
return nil
}
if info.Size() == 0 {
return nil
}
if info.Name() == ".DS_Store" {
return nil
}
if !strings.HasSuffix(info.Name(), ".m3u8") {
return filepath.SkipDir
}
cmd := exec.Command(ffprobe, "-i", path)
cmdOutput := &bytes.Buffer{}
//cmd.Stdout = cmdOutput
cmd.Stderr = cmdOutput
err = cmd.Run()
bytes := cmdOutput.Bytes()
output := string(bytes)
//log.Printf("%v result:%v", cmd, output)
var average = regexp.MustCompile(`Duration: ((\d+):(\d+):(\d+).(\d+))`)
result := average.FindStringSubmatch(output)
duration := time.Duration(0)
durationStr := ""
if len(result) > 0 {
durationStr = result[1]
h, _ := strconv.Atoi(result[2])
duration += time.Duration(h) * time.Hour
m, _ := strconv.Atoi(result[3])
duration += time.Duration(m) * time.Minute
s, _ := strconv.Atoi(result[4])
duration += time.Duration(s) * time.Second
millis, _ := strconv.Atoi(result[5])
duration += time.Duration(millis) * time.Millisecond
}
*files = append(*files, map[string]interface{}{
"path": path[len(mp4Path):],
"durationMillis": duration / time.Millisecond,
"duration": durationStr})
return nil
}
}
err = filepath.Walk(folder, visit(&files))
if err != nil {
log.Printf("Query RecordFolders err:%v", err)
}
}
pr := utils.NewPageResult(files)
if form.Sort != "" {
pr.Sort(form.Sort, form.Order)
}
pr.Slice(form.Start, form.Limit)
c.IndentedJSON(200, pr)
}

View File

@ -10,7 +10,7 @@ import (
)
/**
* @apiDefine stats
* @apiDefine stats
*/
/**

View File

@ -1,36 +1,28 @@
package routers
import (
"bytes"
"fmt"
"log"
"math"
"net/http"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/EasyDarwin/EasyDarwin/rtsp"
"github.com/gin-gonic/gin"
"github.com/penggy/EasyGoLib/utils"
)
/**
* @apiDefine pull
* @apiDefine stream
*/
/**
* @api {get} /api/vi/stream/start
* @apiGroup pull
* @api {get} /api/v1/stream/start
* @apiGroup stream
* @apiName StreamStart
* @apiParam {String} url RTSP
* @apiParam {String} [customPath] PATH
* @apiParam {Number} [IdleTimeout]
* @apiParam {Number} [HeartbeatInterval] 0OPTION
* @apiParam {Number} [idleTimeout]
* @apiParam {Number} [heartbeatInterval] 0OPTION
* @apiSuccess (200) {String} ID IDID
*/
func (h *APIHandler) StreamStart(c *gin.Context) {
@ -73,8 +65,8 @@ func (h *APIHandler) StreamStart(c *gin.Context) {
}
/**
* @api {get} /api/vi/stream/stop
* @apiGroup pull
* @api {get} /api/v1/stream/stop
* @apiGroup stream
* @apiName StreamStop
* @apiParam {String} id ID
* @apiUse simpleSuccess
@ -100,174 +92,3 @@ func (h *APIHandler) StreamStop(c *gin.Context) {
}
c.AbortWithStatusJSON(http.StatusBadRequest, fmt.Sprintf("Pusher[%s] not found", form.ID))
}
/**
* @apiDefine record Record
*/
/**
* @apiDefine fileInfo
* @apiSuccess (200) {String} duration
* @apiSuccess (200) {Number} durationMillis
* @apiSuccess (200) {String} path ,http[s]://host:port/record/[path]。
* @apiSuccess (200) {String} folder
*/
/**
* @api {get} /api/vi/record/folders
* @apiGroup record
* @apiName RecordFolders
* @apiParam {Number} [start] ,
* @apiParam {Number} [limit]
* @apiParam {String} [sort]
* @apiParam {String=ascending,descending} [order]
* @apiParam {String} [q]
* @apiSuccess (200) {Number} total
* @apiSuccess (200) {Array} rows
* @apiSuccess (200) {String} rows.folder
*/
func (h *APIHandler) RecordFolders(c *gin.Context) {
mp4Path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
form := utils.NewPageForm()
if err := c.Bind(form); err != nil {
log.Printf("record folder bind err:%v", err)
return
}
var files = make([]interface{}, 0)
if mp4Path != "" {
visit := func(files *[]interface{}) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == mp4Path {
return nil
}
if !info.IsDir() {
return nil
}
*files = append(*files, map[string]interface{}{"folder": info.Name()})
return filepath.SkipDir
}
}
err := filepath.Walk(mp4Path, visit(&files))
if err != nil {
log.Printf("Query RecordFolders err:%v", err)
}
}
pr := utils.NewPageResult(files)
if form.Sort != "" {
pr.Sort(form.Sort, form.Order)
}
pr.Slice(form.Start, form.Limit)
c.IndentedJSON(200, pr)
}
/**
* @api {get} /api/vi/record/files
* @apiGroup record
* @apiName RecordFiles
* @apiParam {Number} folder
* @apiParam {Number} [start] ,
* @apiParam {Number} [limit]
* @apiParam {String} [sort]
* @apiParam {String=ascending,descending} [order]
* @apiParam {String} [q]
* @apiSuccess (200) {Number} total
* @apiSuccess (200) {Array} rows
* @apiSuccess (200) {String} rows.duration
* @apiSuccess (200) {Number} rows.durationMillis
* @apiSuccess (200) {String} rows.path ,m3u8video便http[s]://host:port/record/[path]。
*/
func (h *APIHandler) RecordFiles(c *gin.Context) {
type Form struct {
utils.PageForm
Folder string `form:"folder" binding:"required"`
StartAt int `form:"beginUTCSecond"`
StopAt int `form:"endUTCSecond"`
}
var form = Form{}
form.Limit = math.MaxUint32
err := c.Bind(&form)
if err != nil {
log.Printf("record file bind err:%v", err)
return
}
files := make([]interface{}, 0)
mp4Path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
if mp4Path != "" {
ffmpeg_path := utils.Conf().Section("rtsp").Key("ffmpeg_path").MustString("")
ffmpeg_folder, executable := filepath.Split(ffmpeg_path)
split := strings.Split(executable, ".")
suffix := ""
if len(split) > 1 {
suffix = split[1]
}
ffprobe := ffmpeg_folder + "ffprobe" + suffix
folder := filepath.Join(mp4Path, form.Folder)
visit := func(files *[]interface{}) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == folder {
return nil
}
if info.IsDir() {
return nil
}
if info.Size() == 0 {
return nil
}
if info.Name() == ".DS_Store" {
return nil
}
if !strings.HasSuffix(info.Name(), ".m3u8") {
return filepath.SkipDir
}
cmd := exec.Command(ffprobe, "-i", path)
cmdOutput := &bytes.Buffer{}
//cmd.Stdout = cmdOutput
cmd.Stderr = cmdOutput
err = cmd.Run()
bytes := cmdOutput.Bytes()
output := string(bytes)
//log.Printf("%v result:%v", cmd, output)
var average = regexp.MustCompile(`Duration: ((\d+):(\d+):(\d+).(\d+))`)
result := average.FindStringSubmatch(output)
duration := time.Duration(0)
durationStr := ""
if len(result) > 0 {
durationStr = result[1]
h, _ := strconv.Atoi(result[2])
duration += time.Duration(h) * time.Hour
m, _ := strconv.Atoi(result[3])
duration += time.Duration(m) * time.Minute
s, _ := strconv.Atoi(result[4])
duration += time.Duration(s) * time.Second
millis, _ := strconv.Atoi(result[5])
duration += time.Duration(millis) * time.Millisecond
}
*files = append(*files, map[string]interface{}{
"path": path[len(mp4Path):],
"durationMillis": duration / time.Millisecond,
"duration": durationStr})
return nil
}
}
err = filepath.Walk(folder, visit(&files))
if err != nil {
log.Printf("Query RecordFolders err:%v", err)
}
}
pr := utils.NewPageResult(files)
if form.Sort != "" {
pr.Sort(form.Sort, form.Order)
}
pr.Slice(form.Start, form.Limit)
c.IndentedJSON(200, pr)
}

View File

@ -11,7 +11,6 @@ import (
"github.com/EasyDarwin/EasyDarwin/models"
"github.com/EasyDarwin/EasyDarwin/rtsp"
"github.com/gin-gonic/gin"
"github.com/penggy/EasyGoLib/buildtime"
"github.com/penggy/EasyGoLib/db"
"github.com/penggy/EasyGoLib/utils"
"github.com/penggy/sessions"
@ -20,7 +19,7 @@ import (
)
/**
* @apiDefine sys API
* @apiDefine sys
*/
type APIHandler struct {
@ -100,7 +99,7 @@ func (h *APIHandler) ModifyPassword(c *gin.Context) {
}
/**
* @api {get} /getserverinfo
* @api {get} /api/v1/getserverinfo
* @apiGroup sys
* @apiName GetServerInfo
* @apiSuccess (200) {String} Hardware
@ -114,7 +113,7 @@ func (h *APIHandler) GetServerInfo(c *gin.Context) {
"InterfaceVersion": "V1",
"RunningTime": utils.UpTimeString(),
"StartUpTime": utils.DateTime(utils.StartTime),
"Server": fmt.Sprintf("%s/%s (Build/%s; Platform/%s;)", "EasyDarwin", BuildVersion, buildtime.BuildTime.Format(utils.BuildTimeLayout), strings.Title(runtime.GOOS)),
"Server": fmt.Sprintf("%s/%s (Platform/%s;)", "EasyDarwin", BuildVersion, strings.Title(runtime.GOOS)),
"memData": memData,
"cpuData": cpuData,
"pusherData": pusherData,
@ -123,7 +122,7 @@ func (h *APIHandler) GetServerInfo(c *gin.Context) {
}
/**
* @api {get} /restart
* @api {get} /api/v1/restart
* @apiGroup sys
* @apiName Restart
* @apiUse simpleSuccess
@ -147,7 +146,7 @@ func (h *APIHandler) Restart(c *gin.Context) {
*/
/**
* @api {get} /login
* @api {get} /api/v1/login
* @apiGroup sys
* @apiName Login
* @apiParam {String} username
@ -184,7 +183,7 @@ func (h *APIHandler) Login(c *gin.Context) {
}
/**
* @api {get} /userInfo
* @api {get} /api/v1/userInfo
* @apiGroup sys
* @apiName UserInfo
* @apiUse userInfo
@ -203,7 +202,7 @@ func (h *APIHandler) UserInfo(c *gin.Context) {
}
/**
* @api {get} /logout
* @api {get} /api/v1/logout
* @apiGroup sys
* @apiName Logout
* @apiUse simpleSuccess

View File

@ -3,7 +3,7 @@ package routers
import "github.com/penggy/EasyGoLib/utils"
const (
BuildVersion = "v8.0"
BuildVersion = "v8.1"
)
type PercentData struct {

View File

@ -1,23 +0,0 @@
package buildtime
/*
const char* utils_build_time(void)
{
static const char* psz_build_time = __DATE__ " " __TIME__;
return psz_build_time;
}
*/
import "C"
import "time"
const (
CBuildTimeLayout = "Jan _2 2006 15:04:05"
)
var BuildTime = GetBuildTime()
func GetBuildTime() time.Time {
t, _ := time.ParseInLocation(CBuildTimeLayout, C.GoString(C.utils_build_time()), time.Local)
return t
}

6
vendor/vendor.json vendored
View File

@ -236,12 +236,6 @@
"revision": "58118c1ea9161250907268a484af4dd6ed314280",
"revisionTime": "2018-05-11T05:30:14Z"
},
{
"checksumSHA1": "GMWMVmq6+1O+MOr1n0WMKN+T4m0=",
"path": "github.com/penggy/EasyGoLib/buildtime",
"revision": "d0926230dda8c9e4e61040cb7825a026dee7d2d3",
"revisionTime": "2018-09-07T02:33:35Z"
},
{
"checksumSHA1": "xLzWQBx93nfL7iWFGbwmyYX/LZA=",
"path": "github.com/penggy/EasyGoLib/db",

View File

@ -1,4 +1,189 @@
define({ "api": [
{
"type": "get",
"url": "/api/v1/record/files",
"title": "获取所有录像文件",
"group": "record",
"name": "RecordFiles",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "Number",
"optional": false,
"field": "folder",
"description": "<p>录像文件所在的文件夹</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "start",
"description": "<p>分页开始,从零开始</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "limit",
"description": "<p>分页大小</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "sort",
"description": "<p>排序字段</p>"
},
{
"group": "Parameter",
"type": "String",
"allowedValues": [
"ascending",
"descending"
],
"optional": true,
"field": "order",
"description": "<p>排序顺序</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "q",
"description": "<p>查询参数</p>"
}
]
}
},
"success": {
"fields": {
"200": [
{
"group": "200",
"type": "Number",
"optional": false,
"field": "total",
"description": "<p>总数</p>"
},
{
"group": "200",
"type": "Array",
"optional": false,
"field": "rows",
"description": "<p>文件列表</p>"
},
{
"group": "200",
"type": "String",
"optional": false,
"field": "rows.duration",
"description": "<p>格式化好的录像时长</p>"
},
{
"group": "200",
"type": "Number",
"optional": false,
"field": "rows.durationMillis",
"description": "<p>录像时长,毫秒为单位</p>"
},
{
"group": "200",
"type": "String",
"optional": false,
"field": "rows.path",
"description": "<p>录像文件的相对路径,录像文件为m3u8格式将其放到video标签中便可直接播放。其绝对路径为http[s]://host:port/record/[path]。</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/record.go",
"groupTitle": "录像"
},
{
"type": "get",
"url": "/api/v1/record/folders",
"title": "获取所有录像文件夹",
"group": "record",
"name": "RecordFolders",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "start",
"description": "<p>分页开始,从零开始</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "limit",
"description": "<p>分页大小</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "sort",
"description": "<p>排序字段</p>"
},
{
"group": "Parameter",
"type": "String",
"allowedValues": [
"ascending",
"descending"
],
"optional": true,
"field": "order",
"description": "<p>排序顺序</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "q",
"description": "<p>查询参数</p>"
}
]
}
},
"success": {
"fields": {
"200": [
{
"group": "200",
"type": "Number",
"optional": false,
"field": "total",
"description": "<p>总数</p>"
},
{
"group": "200",
"type": "Array",
"optional": false,
"field": "rows",
"description": "<p>文件夹列表</p>"
},
{
"group": "200",
"type": "String",
"optional": false,
"field": "rows.folder",
"description": "<p>录像文件夹名称</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/record.go",
"groupTitle": "录像"
},
{
"type": "get",
"url": "/api/v1/players",
@ -114,7 +299,7 @@ define({ "api": [
},
"version": "0.0.0",
"filename": "routers/stats.go",
"groupTitle": "查询接口"
"groupTitle": "统计"
},
{
"type": "get",
@ -238,11 +423,100 @@ define({ "api": [
},
"version": "0.0.0",
"filename": "routers/stats.go",
"groupTitle": "查询接口"
"groupTitle": "统计"
},
{
"type": "get",
"url": "/getserverinfo",
"url": "/api/v1/stream/start",
"title": "启动拉转推",
"group": "stream",
"name": "StreamStart",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "String",
"optional": false,
"field": "url",
"description": "<p>RTSP源地址</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "customPath",
"description": "<p>转推时的推送PATH</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "idleTimeout",
"description": "<p>拉流时的超时时间</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "heartbeatInterval",
"description": "<p>拉流时的心跳间隔毫秒为单位。如果心跳间隔不为0那拉流时会向源地址以该间隔发送OPTION请求用来心跳保活</p>"
}
]
}
},
"success": {
"fields": {
"200": [
{
"group": "200",
"type": "String",
"optional": false,
"field": "ID",
"description": "<p>拉流的ID。后续可以通过该ID来停止拉流</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/streams.go",
"groupTitle": "流管理"
},
{
"type": "get",
"url": "/api/v1/stream/stop",
"title": "停止推流",
"group": "stream",
"name": "StreamStop",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "String",
"optional": false,
"field": "id",
"description": "<p>拉流的ID</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/streams.go",
"groupTitle": "流管理",
"success": {
"examples": [
{
"title": "成功",
"content": "HTTP/1.1 200 OK",
"type": "json"
}
]
}
},
{
"type": "get",
"url": "/api/v1/getserverinfo",
"title": "获取平台运行信息",
"group": "sys",
"name": "GetServerInfo",
@ -282,11 +556,11 @@ define({ "api": [
},
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口"
"groupTitle": "系统"
},
{
"type": "get",
"url": "/login",
"url": "/api/v1/login",
"title": "登录",
"group": "sys",
"name": "Login",
@ -321,17 +595,17 @@ define({ "api": [
},
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口"
"groupTitle": "系统"
},
{
"type": "get",
"url": "/logout",
"url": "/api/v1/logout",
"title": "登出",
"group": "sys",
"name": "Logout",
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口",
"groupTitle": "系统",
"success": {
"examples": [
{
@ -344,13 +618,13 @@ define({ "api": [
},
{
"type": "get",
"url": "/restart",
"url": "/api/v1/restart",
"title": "重启服务",
"group": "sys",
"name": "Restart",
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口",
"groupTitle": "系统",
"success": {
"examples": [
{
@ -363,13 +637,13 @@ define({ "api": [
},
{
"type": "get",
"url": "/userInfo",
"url": "/api/v1/userInfo",
"title": "获取当前登录用户信息",
"group": "sys",
"name": "UserInfo",
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口",
"groupTitle": "系统",
"success": {
"fields": {
"200": [

View File

@ -1,4 +1,189 @@
[
{
"type": "get",
"url": "/api/v1/record/files",
"title": "获取所有录像文件",
"group": "record",
"name": "RecordFiles",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "Number",
"optional": false,
"field": "folder",
"description": "<p>录像文件所在的文件夹</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "start",
"description": "<p>分页开始,从零开始</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "limit",
"description": "<p>分页大小</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "sort",
"description": "<p>排序字段</p>"
},
{
"group": "Parameter",
"type": "String",
"allowedValues": [
"ascending",
"descending"
],
"optional": true,
"field": "order",
"description": "<p>排序顺序</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "q",
"description": "<p>查询参数</p>"
}
]
}
},
"success": {
"fields": {
"200": [
{
"group": "200",
"type": "Number",
"optional": false,
"field": "total",
"description": "<p>总数</p>"
},
{
"group": "200",
"type": "Array",
"optional": false,
"field": "rows",
"description": "<p>文件列表</p>"
},
{
"group": "200",
"type": "String",
"optional": false,
"field": "rows.duration",
"description": "<p>格式化好的录像时长</p>"
},
{
"group": "200",
"type": "Number",
"optional": false,
"field": "rows.durationMillis",
"description": "<p>录像时长,毫秒为单位</p>"
},
{
"group": "200",
"type": "String",
"optional": false,
"field": "rows.path",
"description": "<p>录像文件的相对路径,录像文件为m3u8格式将其放到video标签中便可直接播放。其绝对路径为http[s]://host:port/record/[path]。</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/record.go",
"groupTitle": "录像"
},
{
"type": "get",
"url": "/api/v1/record/folders",
"title": "获取所有录像文件夹",
"group": "record",
"name": "RecordFolders",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "start",
"description": "<p>分页开始,从零开始</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "limit",
"description": "<p>分页大小</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "sort",
"description": "<p>排序字段</p>"
},
{
"group": "Parameter",
"type": "String",
"allowedValues": [
"ascending",
"descending"
],
"optional": true,
"field": "order",
"description": "<p>排序顺序</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "q",
"description": "<p>查询参数</p>"
}
]
}
},
"success": {
"fields": {
"200": [
{
"group": "200",
"type": "Number",
"optional": false,
"field": "total",
"description": "<p>总数</p>"
},
{
"group": "200",
"type": "Array",
"optional": false,
"field": "rows",
"description": "<p>文件夹列表</p>"
},
{
"group": "200",
"type": "String",
"optional": false,
"field": "rows.folder",
"description": "<p>录像文件夹名称</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/record.go",
"groupTitle": "录像"
},
{
"type": "get",
"url": "/api/v1/players",
@ -114,7 +299,7 @@
},
"version": "0.0.0",
"filename": "routers/stats.go",
"groupTitle": "查询接口"
"groupTitle": "统计"
},
{
"type": "get",
@ -238,11 +423,100 @@
},
"version": "0.0.0",
"filename": "routers/stats.go",
"groupTitle": "查询接口"
"groupTitle": "统计"
},
{
"type": "get",
"url": "/getserverinfo",
"url": "/api/v1/stream/start",
"title": "启动拉转推",
"group": "stream",
"name": "StreamStart",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "String",
"optional": false,
"field": "url",
"description": "<p>RTSP源地址</p>"
},
{
"group": "Parameter",
"type": "String",
"optional": true,
"field": "customPath",
"description": "<p>转推时的推送PATH</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "idleTimeout",
"description": "<p>拉流时的超时时间</p>"
},
{
"group": "Parameter",
"type": "Number",
"optional": true,
"field": "heartbeatInterval",
"description": "<p>拉流时的心跳间隔毫秒为单位。如果心跳间隔不为0那拉流时会向源地址以该间隔发送OPTION请求用来心跳保活</p>"
}
]
}
},
"success": {
"fields": {
"200": [
{
"group": "200",
"type": "String",
"optional": false,
"field": "ID",
"description": "<p>拉流的ID。后续可以通过该ID来停止拉流</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/streams.go",
"groupTitle": "流管理"
},
{
"type": "get",
"url": "/api/v1/stream/stop",
"title": "停止推流",
"group": "stream",
"name": "StreamStop",
"parameter": {
"fields": {
"Parameter": [
{
"group": "Parameter",
"type": "String",
"optional": false,
"field": "id",
"description": "<p>拉流的ID</p>"
}
]
}
},
"version": "0.0.0",
"filename": "routers/streams.go",
"groupTitle": "流管理",
"success": {
"examples": [
{
"title": "成功",
"content": "HTTP/1.1 200 OK",
"type": "json"
}
]
}
},
{
"type": "get",
"url": "/api/v1/getserverinfo",
"title": "获取平台运行信息",
"group": "sys",
"name": "GetServerInfo",
@ -282,11 +556,11 @@
},
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口"
"groupTitle": "系统"
},
{
"type": "get",
"url": "/login",
"url": "/api/v1/login",
"title": "登录",
"group": "sys",
"name": "Login",
@ -321,17 +595,17 @@
},
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口"
"groupTitle": "系统"
},
{
"type": "get",
"url": "/logout",
"url": "/api/v1/logout",
"title": "登出",
"group": "sys",
"name": "Logout",
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口",
"groupTitle": "系统",
"success": {
"examples": [
{
@ -344,13 +618,13 @@
},
{
"type": "get",
"url": "/restart",
"url": "/api/v1/restart",
"title": "重启服务",
"group": "sys",
"name": "Restart",
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口",
"groupTitle": "系统",
"success": {
"examples": [
{
@ -363,13 +637,13 @@
},
{
"type": "get",
"url": "/userInfo",
"url": "/api/v1/userInfo",
"title": "获取当前登录用户信息",
"group": "sys",
"name": "UserInfo",
"version": "0.0.0",
"filename": "routers/sys.go",
"groupTitle": "API接口",
"groupTitle": "系统",
"success": {
"fields": {
"200": [

View File

@ -5,6 +5,12 @@ define({
"stats",
"Pushers",
"Players",
"stream",
"StreamStart",
"StreamStop",
"record",
"RecordFolders",
"RecordFiles",
"sys",
"Login",
"Logout",
@ -19,8 +25,8 @@ define({
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-29T14:31:43.382Z",
"time": "2018-12-03T05:50:19.289Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
"version": "0.17.7"
}
});

View File

@ -5,6 +5,12 @@
"stats",
"Pushers",
"Players",
"stream",
"StreamStart",
"StreamStop",
"record",
"RecordFolders",
"RecordFiles",
"sys",
"Login",
"Logout",
@ -19,8 +25,8 @@
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-29T14:31:43.382Z",
"time": "2018-12-03T05:50:19.289Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
"version": "0.17.7"
}
}

25
www/apidoc/locales/cs.js Normal file
View File

@ -0,0 +1,25 @@
define({
cs: {
'Allowed values:' : 'Povolené hodnoty:',
'Compare all with predecessor': 'Porovnat vše s předchozími verzemi',
'compare changes to:' : 'porovnat změny s:',
'compared to' : 'porovnat s',
'Default value:' : 'Výchozí hodnota:',
'Description' : 'Popis',
'Field' : 'Pole',
'General' : 'Obecné',
'Generated with' : 'Vygenerováno pomocí',
'Name' : 'Název',
'No response values.' : 'Nebyly vráceny žádné hodnoty.',
'optional' : 'volitelné',
'Parameter' : 'Parametr',
'Permission:' : 'Oprávnění:',
'Response' : 'Odpověď',
'Send' : 'Odeslat',
'Send a Sample Request' : 'Odeslat ukázkový požadavek',
'show up to version:' : 'zobrazit po verzi:',
'Size range:' : 'Rozsah velikosti:',
'Type' : 'Typ',
'url' : 'url'
}
});

View File

@ -1,5 +1,6 @@
define([
'./locales/ca.js',
'./locales/cs.js',
'./locales/de.js',
'./locales/es.js',
'./locales/fr.js',