mediamtx/internal/record/cleaner.go

144 lines
2.3 KiB
Go

package record
import (
"context"
"io/fs"
"os"
"path/filepath"
"strings"
"time"
"github.com/bluenviron/mediamtx/internal/logger"
)
func commonPath(v string) string {
common := ""
remaining := v
for {
i := strings.IndexAny(remaining, "\\/")
if i < 0 {
break
}
var part string
part, remaining = remaining[:i+1], remaining[i+1:]
if strings.Contains(part, "%") {
break
}
common += part
}
if len(common) > 0 {
common = common[:len(common)-1]
}
return common
}
// Cleaner removes expired recordings from disk.
type Cleaner struct {
ctx context.Context
ctxCancel func()
path string
deleteAfter time.Duration
parent logger.Writer
done chan struct{}
}
// NewCleaner allocates a Cleaner.
func NewCleaner(
recordPath string,
deleteAfter time.Duration,
parent logger.Writer,
) *Cleaner {
recordPath += ".mp4"
ctx, ctxCancel := context.WithCancel(context.Background())
c := &Cleaner{
ctx: ctx,
ctxCancel: ctxCancel,
path: recordPath,
deleteAfter: deleteAfter,
parent: parent,
done: make(chan struct{}),
}
go c.run()
return c
}
// Close closes the Cleaner.
func (c *Cleaner) Close() {
c.ctxCancel()
<-c.done
}
// Log is the main logging function.
func (c *Cleaner) Log(level logger.Level, format string, args ...interface{}) {
c.parent.Log(level, "[record cleaner]"+format, args...)
}
func (c *Cleaner) run() {
defer close(c.done)
interval := 30 * 60 * time.Second
if interval > (c.deleteAfter / 2) {
interval = c.deleteAfter / 2
}
c.doRun() //nolint:errcheck
for {
select {
case <-time.After(interval):
c.doRun() //nolint:errcheck
case <-c.ctx.Done():
return
}
}
}
func (c *Cleaner) doRun() error {
commonPath := commonPath(c.path)
now := timeNow()
filepath.Walk(commonPath, func(path string, info fs.FileInfo, err error) error { //nolint:errcheck
if err != nil {
return err
}
if !info.IsDir() {
params := decodeRecordPath(c.path, path)
if params != nil {
if now.Sub(params.time) > c.deleteAfter {
c.Log(logger.Debug, "removing %s", path)
os.Remove(path)
}
}
}
return nil
})
filepath.Walk(commonPath, func(path string, info fs.FileInfo, err error) error { //nolint:errcheck
if err != nil {
return err
}
if info.IsDir() {
os.Remove(path)
}
return nil
})
return nil
}