config.go 3.4 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. //fmt.Println("NEED RECORD:", r.AutoRecord && (!r.Filter.Valid() || r.Filter.MatchString(streamPath)))
  57. return r.AutoRecord && (!r.Filter.Valid() || r.Filter.MatchString(streamPath))
  58. }
  59. func (r *Record) Init() {
  60. os.MkdirAll(r.Path, 0766)
  61. r.Handler = http.FileServer(http.Dir(r.Path))
  62. r.CreateFileFn = func(filename string, append bool) (file FileWr, err error) {
  63. filePath := filepath.Join(r.Path, filename)
  64. if err = os.MkdirAll(filepath.Dir(filePath), 0766); err != nil {
  65. return file, err
  66. }
  67. fw := &FileWriter{filePath: filePath}
  68. if !append {
  69. if _, loaded := WritingFiles.LoadOrStore(filePath, fw); loaded {
  70. return file, ErrRecordExist
  71. }
  72. }
  73. file, err = os.OpenFile(filePath, os.O_CREATE|os.O_RDWR|util.Conditoinal(append, os.O_APPEND, os.O_TRUNC), 0666)
  74. if err == nil && !append {
  75. fw.Reader = file
  76. fw.Writer = file
  77. fw.Seeker = file
  78. fw.Closer = file
  79. return fw, nil
  80. }
  81. return
  82. }
  83. }
  84. func (r *Record) Tree(dstPath string, level int) (files []*VideoFileInfo, err error) {
  85. var dstF *os.File
  86. dstF, err = os.Open(dstPath)
  87. if err != nil {
  88. return
  89. }
  90. defer dstF.Close()
  91. fileInfo, err := dstF.Stat()
  92. if err != nil {
  93. return
  94. }
  95. if !fileInfo.IsDir() { //如果dstF是文件
  96. if r.Ext == "." || path.Ext(fileInfo.Name()) == r.Ext {
  97. p := strings.TrimPrefix(dstPath, r.Path)
  98. p = strings.ReplaceAll(p, "\\", "/")
  99. var duration uint32
  100. if r.GetDurationFn != nil {
  101. duration = r.GetDurationFn(dstF)
  102. }
  103. files = append(files, &VideoFileInfo{
  104. Path: strings.TrimPrefix(p, "/"),
  105. Size: fileInfo.Size(),
  106. Duration: duration,
  107. })
  108. }
  109. return
  110. } else { //如果dstF是文件夹
  111. var dir []os.FileInfo
  112. dir, err = dstF.Readdir(0) //获取文件夹下各个文件或文件夹的fileInfo
  113. if err != nil {
  114. return
  115. }
  116. for _, fileInfo = range dir {
  117. var _files []*VideoFileInfo
  118. _files, err = r.Tree(filepath.Join(dstPath, fileInfo.Name()), level+1)
  119. if err != nil {
  120. return
  121. }
  122. files = append(files, _files...)
  123. }
  124. return
  125. }
  126. }