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) }