mirror of https://github.com/EasyDarwin/EasyDarwin
189 lines
5.5 KiB
Go
189 lines
5.5 KiB
Go
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(strings.ToLower(info.Name()), ".m3u8") && !strings.HasSuffix(strings.ToLower(info.Name()), ".ts") {
|
||
return nil
|
||
}
|
||
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)
|
||
}
|