mediamtx/internal/record/cleaner.go

125 lines
2.3 KiB
Go

package record
import (
"context"
"io/fs"
"os"
"path/filepath"
"time"
"github.com/bluenviron/mediamtx/internal/conf"
"github.com/bluenviron/mediamtx/internal/logger"
)
var timeNow = time.Now
// CleanerEntry is a cleaner entry.
type CleanerEntry struct {
Path string
Format conf.RecordFormat
DeleteAfter time.Duration
}
// Cleaner removes expired recording segments from disk.
type Cleaner struct {
Entries []CleanerEntry
Parent logger.Writer
ctx context.Context
ctxCancel func()
done chan struct{}
}
// Initialize initializes a Cleaner.
func (c *Cleaner) Initialize() {
c.ctx, c.ctxCancel = context.WithCancel(context.Background())
c.done = make(chan struct{})
go c.run()
}
// Close closes the Cleaner.
func (c *Cleaner) Close() {
c.ctxCancel()
<-c.done
}
// Log implements logger.Writer.
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
for _, e := range c.Entries {
if interval > (e.DeleteAfter / 2) {
interval = e.DeleteAfter / 2
}
}
c.doRun() //nolint:errcheck
for {
select {
case <-time.After(interval):
c.doRun()
case <-c.ctx.Done():
return
}
}
}
func (c *Cleaner) doRun() {
for _, e := range c.Entries {
c.doRunEntry(&e) //nolint:errcheck
}
}
func (c *Cleaner) doRunEntry(e *CleanerEntry) error {
entryPath := PathAddExtension(e.Path, e.Format)
// we have to convert to absolute paths
// otherwise, entryPath and fpath inside Walk() won't have common elements
entryPath, _ = filepath.Abs(entryPath)
commonPath := CommonPath(entryPath)
now := timeNow()
filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error { //nolint:errcheck
if err != nil {
return err
}
if !info.IsDir() {
var pa Path
ok := pa.Decode(entryPath, fpath)
if ok {
if now.Sub(pa.Start) > e.DeleteAfter {
c.Log(logger.Debug, "removing %s", fpath)
os.Remove(fpath)
}
}
}
return nil
})
filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error { //nolint:errcheck
if err != nil {
return err
}
if info.IsDir() {
os.Remove(fpath)
}
return nil
})
return nil
}