config.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. package record
  2. import (
  3. "bufio"
  4. "io"
  5. "net/http"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "strings"
  10. "sync"
  11. "time"
  12. "m7s.live/engine/v4/config"
  13. "m7s.live/engine/v4/util"
  14. )
  15. type FileWr interface {
  16. io.Reader
  17. io.Writer
  18. io.Seeker
  19. io.Closer
  20. }
  21. var WritingFiles sync.Map
  22. type FileWriter struct {
  23. filePath string
  24. io.Reader
  25. io.Writer
  26. io.Seeker
  27. io.Closer
  28. bufw *bufio.Writer
  29. }
  30. func (f *FileWriter) Seek(offset int64, whence int) (int64, error) {
  31. if f.bufw != nil {
  32. f.bufw.Flush()
  33. }
  34. return f.Seeker.Seek(offset, whence)
  35. }
  36. func (f *FileWriter) Close() error {
  37. WritingFiles.Delete(f.filePath)
  38. return f.Closer.Close()
  39. }
  40. type VideoFileInfo struct {
  41. Path string
  42. Size int64
  43. Duration uint32
  44. }
  45. type Record struct {
  46. Ext string `desc:"文件扩展名"` //文件扩展名
  47. Path string `desc:"存储文件的目录"` //存储文件的目录
  48. AutoRecord bool `desc:"是否自动录制"` //是否自动录制
  49. Filter config.Regexp `desc:"录制过滤器"` //录制过滤器
  50. Fragment time.Duration `desc:"分片大小,0表示不分片"` //分片大小,0表示不分片
  51. http.Handler `json:"-" yaml:"-"`
  52. CreateFileFn func(filename string, append bool) (FileWr, error) `json:"-" yaml:"-"`
  53. GetDurationFn func(file io.ReadSeeker) uint32 `json:"-" yaml:"-"`
  54. }
  55. func (r *Record) NeedRecord(streamPath string) bool {
  56. return r.AutoRecord && (!r.Filter.Valid() || r.Filter.MatchString(streamPath))
  57. }
  58. func (r *Record) Init() {
  59. os.MkdirAll(r.Path, 0766)
  60. r.Handler = http.FileServer(http.Dir(r.Path))
  61. r.CreateFileFn = func(filename string, append bool) (file FileWr, err error) {
  62. filePath := filepath.Join(r.Path, filename)
  63. if err = os.MkdirAll(filepath.Dir(filePath), 0766); err != nil {
  64. return file, err
  65. }
  66. fw := &FileWriter{filePath: filePath}
  67. if !append {
  68. if _, loaded := WritingFiles.LoadOrStore(filePath, fw); loaded {
  69. return file, ErrRecordExist
  70. }
  71. }
  72. file, err = os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|util.Conditoinal(append, os.O_APPEND, os.O_TRUNC), 0666)
  73. if err == nil && !append {
  74. fw.Reader = file
  75. fw.Writer = file
  76. fw.Seeker = file
  77. fw.Closer = file
  78. return fw, nil
  79. }
  80. return
  81. }
  82. }
  83. func (r *Record) Tree(dstPath string, level int) (files []*VideoFileInfo, err error) {
  84. var dstF *os.File
  85. dstF, err = os.Open(dstPath)
  86. if err != nil {
  87. return
  88. }
  89. defer dstF.Close()
  90. fileInfo, err := dstF.Stat()
  91. if err != nil {
  92. return
  93. }
  94. if !fileInfo.IsDir() { //如果dstF是文件
  95. if r.Ext == "." || path.Ext(fileInfo.Name()) == r.Ext {
  96. //p := strings.TrimPrefix(dstPath, r.Path)
  97. p := strings.ReplaceAll(dstPath, "\\", "/")
  98. var duration uint32
  99. if r.GetDurationFn != nil {
  100. duration = r.GetDurationFn(dstF)
  101. }
  102. files = append(files, &VideoFileInfo{
  103. Path: strings.TrimPrefix(p, "/"),
  104. Size: fileInfo.Size(),
  105. Duration: duration,
  106. })
  107. }
  108. return
  109. } else { //如果dstF是文件夹
  110. var dir []os.FileInfo
  111. dir, err = dstF.Readdir(0) //获取文件夹下各个文件或文件夹的fileInfo
  112. if err != nil {
  113. return
  114. }
  115. for _, fileInfo = range dir {
  116. var _files []*VideoFileInfo
  117. _files, err = r.Tree(filepath.Join(dstPath, fileInfo.Name()), level+1)
  118. if err != nil {
  119. return
  120. }
  121. files = append(files, _files...)
  122. }
  123. return
  124. }
  125. }