2024-02-04 22:51:51 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"io/fs"
|
|
|
|
"path/filepath"
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/defs"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/playback"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/record"
|
|
|
|
)
|
|
|
|
|
|
|
|
var errFound = errors.New("found")
|
|
|
|
|
|
|
|
func fixedPathHasRecordings(pathConf *conf.Path) bool {
|
|
|
|
recordPath := record.PathAddExtension(
|
|
|
|
strings.ReplaceAll(pathConf.RecordPath, "%path", pathConf.Name),
|
|
|
|
pathConf.RecordFormat,
|
|
|
|
)
|
|
|
|
|
|
|
|
// we have to convert to absolute paths
|
|
|
|
// otherwise, recordPath and fpath inside Walk() won't have common elements
|
|
|
|
recordPath, _ = filepath.Abs(recordPath)
|
|
|
|
|
|
|
|
commonPath := record.CommonPath(recordPath)
|
|
|
|
|
|
|
|
err := filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !info.IsDir() {
|
|
|
|
var pa record.Path
|
|
|
|
ok := pa.Decode(recordPath, fpath)
|
|
|
|
if ok {
|
|
|
|
return errFound
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil && !errors.Is(err, errFound) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Is(err, errFound)
|
|
|
|
}
|
|
|
|
|
|
|
|
func regexpPathGetRecordings(pathConf *conf.Path) []string {
|
|
|
|
recordPath := record.PathAddExtension(
|
|
|
|
pathConf.RecordPath,
|
|
|
|
pathConf.RecordFormat,
|
|
|
|
)
|
|
|
|
|
|
|
|
// we have to convert to absolute paths
|
|
|
|
// otherwise, recordPath and fpath inside Walk() won't have common elements
|
|
|
|
recordPath, _ = filepath.Abs(recordPath)
|
|
|
|
|
|
|
|
commonPath := record.CommonPath(recordPath)
|
|
|
|
|
|
|
|
var ret []string
|
|
|
|
|
|
|
|
filepath.Walk(commonPath, func(fpath string, info fs.FileInfo, err error) error { //nolint:errcheck
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !info.IsDir() {
|
|
|
|
var pa record.Path
|
|
|
|
ok := pa.Decode(recordPath, fpath)
|
|
|
|
if ok && pathConf.Regexp.FindStringSubmatch(pa.Path) != nil {
|
|
|
|
ret = append(ret, pa.Path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func removeDuplicatesAndSort(in []string) []string {
|
|
|
|
ma := make(map[string]struct{}, len(in))
|
|
|
|
for _, i := range in {
|
|
|
|
ma[i] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
out := []string{}
|
|
|
|
|
|
|
|
for k := range ma {
|
|
|
|
out = append(out, k)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(out)
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
func getAllPathsWithRecordings(paths map[string]*conf.Path) []string {
|
|
|
|
pathNames := []string{}
|
|
|
|
|
|
|
|
for _, pathConf := range paths {
|
2024-04-09 08:04:12 +00:00
|
|
|
if pathConf.Regexp == nil {
|
|
|
|
if fixedPathHasRecordings(pathConf) {
|
|
|
|
pathNames = append(pathNames, pathConf.Name)
|
2024-02-04 22:51:51 +00:00
|
|
|
}
|
2024-04-09 08:04:12 +00:00
|
|
|
} else {
|
|
|
|
pathNames = append(pathNames, regexpPathGetRecordings(pathConf)...)
|
2024-02-04 22:51:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return removeDuplicatesAndSort(pathNames)
|
|
|
|
}
|
|
|
|
|
|
|
|
func recordingEntry(
|
|
|
|
pathConf *conf.Path,
|
|
|
|
pathName string,
|
|
|
|
) *defs.APIRecording {
|
|
|
|
ret := &defs.APIRecording{
|
|
|
|
Name: pathName,
|
|
|
|
}
|
|
|
|
|
|
|
|
segments, _ := playback.FindSegments(pathConf, pathName)
|
|
|
|
|
|
|
|
ret.Segments = make([]*defs.APIRecordingSegment, len(segments))
|
|
|
|
|
|
|
|
for i, seg := range segments {
|
|
|
|
ret.Segments[i] = &defs.APIRecordingSegment{
|
|
|
|
Start: seg.Start,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret
|
|
|
|
}
|