From 47bd7352f0883b6749c4fe44b08059fd92c2a357 Mon Sep 17 00:00:00 2001 From: Neeraj Kanhere Date: Sat, 2 Dec 2023 08:36:13 -0500 Subject: [PATCH] support unix timestamp in filenames of recorded segments (#2774) * added support for %s to use unix timestamp in filenames * fix lint errors * add tests * update documentation --------- Co-authored-by: aler9 <46489434+aler9@users.noreply.github.com> --- README.md | 2 +- internal/record/record_path.go | 14 +++++++- internal/record/record_path_test.go | 51 +++++++++++++++++++++++++++++ mediamtx.yml | 2 +- 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 internal/record/record_path_test.go diff --git a/README.md b/README.md index c407ced8..9e975393 100644 --- a/README.md +++ b/README.md @@ -1153,7 +1153,7 @@ pathDefaults: record: yes # Path of recording segments. # Extension is added automatically. - # Available variables are %path (path name), %Y %m %d %H %M %S %f (time in strftime format) + # Available variables are %path (path name), %Y %m %d %H %M %S %f %s (time in strftime format) recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f ``` diff --git a/internal/record/record_path.go b/internal/record/record_path.go index 84c16683..98f4f4c4 100644 --- a/internal/record/record_path.go +++ b/internal/record/record_path.go @@ -56,6 +56,7 @@ func decodeRecordPath(format string, v string) *recordPathParams { re = strings.ReplaceAll(re, "%M", "([0-9]{2})") re = strings.ReplaceAll(re, "%S", "([0-9]{2})") re = strings.ReplaceAll(re, "%f", "([0-9]{6})") + re = strings.ReplaceAll(re, "%s", "([0-9]{10})") r := regexp.MustCompile(re) var groupMapping []string @@ -77,6 +78,7 @@ func decodeRecordPath(format string, v string) *recordPathParams { "%M", "%S", "%f", + "%s", } { if strings.HasPrefix(cur, va) { groupMapping = append(groupMapping, va) @@ -104,6 +106,7 @@ func decodeRecordPath(format string, v string) *recordPathParams { var minute int var second int var micros int + var unixSec int64 = -1 for k, v := range values { switch k { @@ -134,10 +137,18 @@ func decodeRecordPath(format string, v string) *recordPathParams { case "%f": tmp, _ := strconv.ParseInt(v, 10, 64) micros = int(tmp) + + case "%s": + unixSec, _ = strconv.ParseInt(v, 10, 64) } } - t := time.Date(year, month, day, hour, minute, second, micros*1000, time.Local) + var t time.Time + if unixSec > 0 { + t = time.Unix(unixSec, 0) + } else { + t = time.Date(year, month, day, hour, minute, second, micros*1000, time.Local) + } return &recordPathParams{ path: values["%path"], @@ -153,5 +164,6 @@ func encodeRecordPath(params *recordPathParams, v string) string { v = strings.ReplaceAll(v, "%M", leadingZeros(params.time.Minute(), 2)) v = strings.ReplaceAll(v, "%S", leadingZeros(params.time.Second(), 2)) v = strings.ReplaceAll(v, "%f", leadingZeros(params.time.Nanosecond()/1000, 6)) + v = strings.ReplaceAll(v, "%s", strconv.FormatInt(params.time.Unix(), 10)) return v } diff --git a/internal/record/record_path_test.go b/internal/record/record_path_test.go new file mode 100644 index 00000000..8009b79a --- /dev/null +++ b/internal/record/record_path_test.go @@ -0,0 +1,51 @@ +package record + +import ( + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +var recordPathCases = []struct { + name string + format string + dec *recordPathParams + enc string +}{ + { + "standard", + "%path/%Y-%m-%d_%H-%M-%S-%f.mp4", + &recordPathParams{ + path: "mypath", + time: time.Date(2008, 11, 0o7, 11, 22, 4, 123456000, time.Local), + }, + "mypath/2008-11-07_11-22-04-123456.mp4", + }, + { + "unix seconds", + "%path/%s.mp4", + &recordPathParams{ + path: "mypath", + time: time.Date(2021, 12, 2, 12, 15, 23, 0, time.UTC).Local(), + }, + "mypath/1638447323.mp4", + }, +} + +func TestRecordPathDecode(t *testing.T) { + for _, ca := range recordPathCases { + t.Run(ca.name, func(t *testing.T) { + require.Equal(t, ca.dec, decodeRecordPath(ca.format, ca.enc)) + }) + } +} + +func TestRecordPathEncode(t *testing.T) { + for _, ca := range recordPathCases { + t.Run(ca.name, func(t *testing.T) { + require.Equal(t, ca.enc, strings.ReplaceAll(encodeRecordPath(ca.dec, ca.format), "%path", "mypath")) + }) + } +} diff --git a/mediamtx.yml b/mediamtx.yml index 5cac5cbd..896b737b 100644 --- a/mediamtx.yml +++ b/mediamtx.yml @@ -296,7 +296,7 @@ pathDefaults: record: no # Path of recording segments. # Extension is added automatically. - # Available variables are %path (path name), %Y %m %d %H %M %S %f (time in strftime format) + # Available variables are %path (path name), %Y %m %d %H %M %S %f %s (time in strftime format) recordPath: ./recordings/%path/%Y-%m-%d_%H-%M-%S-%f # Format of recorded segments. # Available formats are "fmp4" (fragmented MP4) and "mpegts" (MPEG-TS).