diff --git a/.gitignore b/.gitignore index 3bf5853f..042d3254 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /EasyDarwin.exe /EasyDarwin.exe~ /logs +/*.log node_modules *.db *.db.bak diff --git a/README.md b/README.md index 8fcc9ac9..5888edd8 100644 --- a/README.md +++ b/README.md @@ -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 + ### 编译命令 diff --git a/easydarwin.ini b/easydarwin.ini index a8839edf..8ed02119 100644 --- a/easydarwin.ini +++ b/easydarwin.ini @@ -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文件,单位秒 \ No newline at end of file + +; 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 \ No newline at end of file diff --git a/package.json b/package.json index 7b63b942..8c5a59fd 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/routers/record.go b/routers/record.go new file mode 100644 index 00000000..21b240dd --- /dev/null +++ b/routers/record.go @@ -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 录像文件的相对路径,录像文件为m3u8格式,将其放到video标签中便可直接播放。其绝对路径为: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) +} diff --git a/routers/stats.go b/routers/stats.go index a9189ff0..cd0173e2 100644 --- a/routers/stats.go +++ b/routers/stats.go @@ -10,7 +10,7 @@ import ( ) /** - * @apiDefine stats 查询接口 + * @apiDefine stats 统计 */ /** diff --git a/routers/streams.go b/routers/streams.go index 1c4c072e..358e04d1 100644 --- a/routers/streams.go +++ b/routers/streams.go @@ -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] 拉流时的心跳间隔,毫秒为单位。如果心跳间隔不为0,那拉流时会向源地址以该间隔发送OPTION请求用来心跳保活 + * @apiParam {Number} [idleTimeout] 拉流时的超时时间 + * @apiParam {Number} [heartbeatInterval] 拉流时的心跳间隔,毫秒为单位。如果心跳间隔不为0,那拉流时会向源地址以该间隔发送OPTION请求用来心跳保活 * @apiSuccess (200) {String} ID 拉流的ID。后续可以通过该ID来停止拉流 */ 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 录像文件的相对路径,录像文件为m3u8格式,将其放到video标签中便可直接播放。其绝对路径为: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) -} diff --git a/routers/sys.go b/routers/sys.go index d627eff8..2a81f6da 100644 --- a/routers/sys.go +++ b/routers/sys.go @@ -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 diff --git a/routers/types.go b/routers/types.go index ba0d456f..bb2d2e38 100644 --- a/routers/types.go +++ b/routers/types.go @@ -3,7 +3,7 @@ package routers import "github.com/penggy/EasyGoLib/utils" const ( - BuildVersion = "v8.0" + BuildVersion = "v8.1" ) type PercentData struct { diff --git a/vendor/github.com/penggy/EasyGoLib/buildtime/buildtime.go b/vendor/github.com/penggy/EasyGoLib/buildtime/buildtime.go deleted file mode 100644 index 34b17fcf..00000000 --- a/vendor/github.com/penggy/EasyGoLib/buildtime/buildtime.go +++ /dev/null @@ -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 -} diff --git a/vendor/vendor.json b/vendor/vendor.json index 99e84455..0cffde30 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -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", diff --git a/www/apidoc/api_data.js b/www/apidoc/api_data.js index 704b330c..3c35010a 100644 --- a/www/apidoc/api_data.js +++ b/www/apidoc/api_data.js @@ -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": "
录像文件所在的文件夹
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "start", + "description": "分页开始,从零开始
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "limit", + "description": "分页大小
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "sort", + "description": "排序字段
" + }, + { + "group": "Parameter", + "type": "String", + "allowedValues": [ + "ascending", + "descending" + ], + "optional": true, + "field": "order", + "description": "排序顺序
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "q", + "description": "查询参数
" + } + ] + } + }, + "success": { + "fields": { + "200": [ + { + "group": "200", + "type": "Number", + "optional": false, + "field": "total", + "description": "总数
" + }, + { + "group": "200", + "type": "Array", + "optional": false, + "field": "rows", + "description": "文件列表
" + }, + { + "group": "200", + "type": "String", + "optional": false, + "field": "rows.duration", + "description": "格式化好的录像时长
" + }, + { + "group": "200", + "type": "Number", + "optional": false, + "field": "rows.durationMillis", + "description": "录像时长,毫秒为单位
" + }, + { + "group": "200", + "type": "String", + "optional": false, + "field": "rows.path", + "description": "录像文件的相对路径,录像文件为m3u8格式,将其放到video标签中便可直接播放。其绝对路径为:http[s]://host:port/record/[path]。
" + } + ] + } + }, + "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": "分页开始,从零开始
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "limit", + "description": "分页大小
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "sort", + "description": "排序字段
" + }, + { + "group": "Parameter", + "type": "String", + "allowedValues": [ + "ascending", + "descending" + ], + "optional": true, + "field": "order", + "description": "排序顺序
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "q", + "description": "查询参数
" + } + ] + } + }, + "success": { + "fields": { + "200": [ + { + "group": "200", + "type": "Number", + "optional": false, + "field": "total", + "description": "总数
" + }, + { + "group": "200", + "type": "Array", + "optional": false, + "field": "rows", + "description": "文件夹列表
" + }, + { + "group": "200", + "type": "String", + "optional": false, + "field": "rows.folder", + "description": "录像文件夹名称
" + } + ] + } + }, + "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": "RTSP源地址
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "customPath", + "description": "转推时的推送PATH
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "idleTimeout", + "description": "拉流时的超时时间
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "heartbeatInterval", + "description": "拉流时的心跳间隔,毫秒为单位。如果心跳间隔不为0,那拉流时会向源地址以该间隔发送OPTION请求用来心跳保活
" + } + ] + } + }, + "success": { + "fields": { + "200": [ + { + "group": "200", + "type": "String", + "optional": false, + "field": "ID", + "description": "拉流的ID。后续可以通过该ID来停止拉流
" + } + ] + } + }, + "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": "拉流的ID
" + } + ] + } + }, + "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": [ diff --git a/www/apidoc/api_data.json b/www/apidoc/api_data.json index 0643806b..4adbe795 100644 --- a/www/apidoc/api_data.json +++ b/www/apidoc/api_data.json @@ -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": "录像文件所在的文件夹
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "start", + "description": "分页开始,从零开始
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "limit", + "description": "分页大小
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "sort", + "description": "排序字段
" + }, + { + "group": "Parameter", + "type": "String", + "allowedValues": [ + "ascending", + "descending" + ], + "optional": true, + "field": "order", + "description": "排序顺序
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "q", + "description": "查询参数
" + } + ] + } + }, + "success": { + "fields": { + "200": [ + { + "group": "200", + "type": "Number", + "optional": false, + "field": "total", + "description": "总数
" + }, + { + "group": "200", + "type": "Array", + "optional": false, + "field": "rows", + "description": "文件列表
" + }, + { + "group": "200", + "type": "String", + "optional": false, + "field": "rows.duration", + "description": "格式化好的录像时长
" + }, + { + "group": "200", + "type": "Number", + "optional": false, + "field": "rows.durationMillis", + "description": "录像时长,毫秒为单位
" + }, + { + "group": "200", + "type": "String", + "optional": false, + "field": "rows.path", + "description": "录像文件的相对路径,录像文件为m3u8格式,将其放到video标签中便可直接播放。其绝对路径为:http[s]://host:port/record/[path]。
" + } + ] + } + }, + "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": "分页开始,从零开始
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "limit", + "description": "分页大小
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "sort", + "description": "排序字段
" + }, + { + "group": "Parameter", + "type": "String", + "allowedValues": [ + "ascending", + "descending" + ], + "optional": true, + "field": "order", + "description": "排序顺序
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "q", + "description": "查询参数
" + } + ] + } + }, + "success": { + "fields": { + "200": [ + { + "group": "200", + "type": "Number", + "optional": false, + "field": "total", + "description": "总数
" + }, + { + "group": "200", + "type": "Array", + "optional": false, + "field": "rows", + "description": "文件夹列表
" + }, + { + "group": "200", + "type": "String", + "optional": false, + "field": "rows.folder", + "description": "录像文件夹名称
" + } + ] + } + }, + "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": "RTSP源地址
" + }, + { + "group": "Parameter", + "type": "String", + "optional": true, + "field": "customPath", + "description": "转推时的推送PATH
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "idleTimeout", + "description": "拉流时的超时时间
" + }, + { + "group": "Parameter", + "type": "Number", + "optional": true, + "field": "heartbeatInterval", + "description": "拉流时的心跳间隔,毫秒为单位。如果心跳间隔不为0,那拉流时会向源地址以该间隔发送OPTION请求用来心跳保活
" + } + ] + } + }, + "success": { + "fields": { + "200": [ + { + "group": "200", + "type": "String", + "optional": false, + "field": "ID", + "description": "拉流的ID。后续可以通过该ID来停止拉流
" + } + ] + } + }, + "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": "拉流的ID
" + } + ] + } + }, + "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": [ diff --git a/www/apidoc/api_project.js b/www/apidoc/api_project.js index 2297e957..9c2c3e0c 100644 --- a/www/apidoc/api_project.js +++ b/www/apidoc/api_project.js @@ -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" } }); diff --git a/www/apidoc/api_project.json b/www/apidoc/api_project.json index 383001bf..a0427aa4 100644 --- a/www/apidoc/api_project.json +++ b/www/apidoc/api_project.json @@ -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" } } diff --git a/www/apidoc/locales/cs.js b/www/apidoc/locales/cs.js new file mode 100644 index 00000000..b7796d8f --- /dev/null +++ b/www/apidoc/locales/cs.js @@ -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' + } +}); diff --git a/www/apidoc/locales/locale.js b/www/apidoc/locales/locale.js index ba82385a..3530705e 100644 --- a/www/apidoc/locales/locale.js +++ b/www/apidoc/locales/locale.js @@ -1,5 +1,6 @@ define([ './locales/ca.js', + './locales/cs.js', './locales/de.js', './locales/es.js', './locales/fr.js',