From 0f29869d7652c3a702c1250710b7bb20adebd95e Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Sun, 5 Dec 2021 23:00:04 -0800 Subject: [PATCH] allow cameras case insensitive, fix lint, update deps --- Makefile | 2 +- cameras.go | 11 +++++++++-- cameras_test.go | 11 +++++++++-- events.go | 14 +++++++------- events_types.go | 24 ++++++++++++------------ files.go | 19 +++++++++---------- go.mod | 12 ++++++++---- go.sum | 23 ++++++++++++++++------- ptz.go | 13 ++++++------- securityspy_test.go | 2 +- server/server.go | 6 +++--- 11 files changed, 81 insertions(+), 56 deletions(-) diff --git a/Makefile b/Makefile index c9b8b38..58e132a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -GOLANGCI_ARGS=-D exhaustivestruct +GOLANGCI_ARGS=-D exhaustivestruct,varnamelen,interfacer,maligned,scopelint,golint all: @echo "try: make test" diff --git a/cameras.go b/cameras.go index e353a40..8adb71e 100644 --- a/cameras.go +++ b/cameras.go @@ -39,6 +39,13 @@ func (c *Cameras) ByName(name string) *Camera { } } + // Try again, case-insensitive. + for _, cam := range c.cameras { + if strings.EqualFold(cam.Name, name) { + return cam + } + } + return nil } @@ -76,7 +83,7 @@ func (c *Camera) StreamVideo(ops *VidOps, length time.Duration, maxsize int64) ( // SaveVideo saves a segment of video from a camera to a file using FFMPEG. func (c *Camera) SaveVideo(ops *VidOps, length time.Duration, maxsize int64, outputFile string) error { if _, err := os.Stat(outputFile); !os.IsNotExist(err) { - return ErrorPathExists + return ErrPathExists } f := ffmpeg.Get(&ffmpeg.Config{ @@ -182,7 +189,7 @@ func (c *Camera) GetJPEG(ops *VidOps) (image.Image, error) { // VidOps defines the image size. ops.FPS is ignored. func (c *Camera) SaveJPEG(ops *VidOps, path string) error { if _, err := os.Stat(path); !os.IsNotExist(err) { - return ErrorPathExists + return ErrPathExists } jpgImage, err := c.GetJPEG(ops) diff --git a/cameras_test.go b/cameras_test.go index d6253b5..c19d86d 100644 --- a/cameras_test.go +++ b/cameras_test.go @@ -45,7 +45,7 @@ func TestAll(t *testing.T) { assert.EqualValues(2, len(cams), "the data contains two cameras, two cameras must be returned") } -func TestByNum(t *testing.T) { //nolint:dupl +func TestByNum(t *testing.T) { t.Parallel() assert := assert.New(t) @@ -69,7 +69,7 @@ func TestByNum(t *testing.T) { //nolint:dupl assert.Nil(server.Cameras.ByNum(99), "a non-existent camera must return nil") } -func TestByName(t *testing.T) { //nolint:dupl +func TestByName(t *testing.T) { t.Parallel() assert := assert.New(t) @@ -91,6 +91,13 @@ func TestByName(t *testing.T) { //nolint:dupl cam := server.Cameras.ByName("Porch") assert.EqualValues(1, cam.Number, "camera 1 is Porch in the test data") assert.Nil(server.Cameras.ByName("not here"), "a non-existent camera must return nil") + + cam = server.Cameras.ByName("porch2") + assert.Nil(cam, "there is no camera named porch2") + + cam = server.Cameras.ByName("porch") + assert.EqualValues(1, cam.Number, "camera 1 is Porch in the test data") + assert.Nil(server.Cameras.ByName("not here"), "a non-existent camera must return nil") } /* Having a comment at the end of the file like this allows commenting the whole file easily. */ diff --git a/events.go b/events.go index ed9378c..de109db 100644 --- a/events.go +++ b/events.go @@ -273,7 +273,7 @@ func (e *Events) serverRefresh() { func (e *Events) UnmarshalEvent(text string) Event { // nolint:funlen,cyclop var ( err error - parts = strings.SplitN(text, " ", 4) + parts = strings.SplitN(text, " ", 4) //nolint:gomnd newEvent = Event{Msg: parts[3], ID: -1, Time: time.Now()} // Parse the time stamp; append the Offset from ++systemInfo to get the right time-location. eventTime = fmt.Sprintf("%v%+03.0f", parts[0], e.server.Info.GmtOffset.Hours()) @@ -281,22 +281,22 @@ func (e *Events) UnmarshalEvent(text string) Event { // nolint:funlen,cyclop if newEvent.When, err = time.ParseInLocation(EventTimeFormat+"-07", eventTime, time.Local); err != nil { newEvent.When = time.Now() - newEvent.Errors = append(newEvent.Errors, ErrorDateParseFail) + newEvent.Errors = append(newEvent.Errors, ErrDateParseFail) } // Parse the ID if newEvent.ID, err = strconv.Atoi(parts[1]); err != nil { newEvent.ID = BadID - newEvent.Errors = append(newEvent.Errors, ErrorIDParseFail) + newEvent.Errors = append(newEvent.Errors, ErrIDParseFail) } // Parse the camera number. parts[2] = strings.TrimPrefix(parts[2], "CAM") if parts[2] != "X" { if cameraNum, err := strconv.Atoi(parts[2]); err != nil { - newEvent.Errors = append(newEvent.Errors, ErrorCAMParseFail) + newEvent.Errors = append(newEvent.Errors, ErrCAMParseFail) } else if newEvent.Camera = e.server.Cameras.ByNum(cameraNum); newEvent.Camera == nil { - newEvent.Errors = append(newEvent.Errors, ErrorCAMMissing) + newEvent.Errors = append(newEvent.Errors, ErrCAMMissing) } } @@ -306,7 +306,7 @@ func (e *Events) UnmarshalEvent(text string) Event { // nolint:funlen,cyclop newEvent.Type = EventType(parts[0]) // Check if the type we just converted is a known event. if name := EventName(newEvent.Type); name == "" { - newEvent.Errors = append(newEvent.Errors, ErrorUnknownEvent) + newEvent.Errors = append(newEvent.Errors, ErrUnknownEvent) newEvent.Type = EventUnknownEvent } @@ -370,7 +370,7 @@ func (e *Event) eventChans(chans map[EventType][]chan Event) { // scanLinesCR is a custom bufio.Scanner to read SecuritySpy eventStream. func scanLinesCR(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { - return 0, nil, ErrorDisconnect + return 0, nil, ErrDisconnect } if i := bytes.IndexByte(data, '\r'); i >= 0 { diff --git a/events_types.go b/events_types.go index 6a2a105..6fa1ca3 100644 --- a/events_types.go +++ b/events_types.go @@ -9,22 +9,22 @@ import ( // This is a list of errors returned by the Events methods. var ( - // ErrorUnknownEvent never really returns, but will fire if SecuritySpy + // ErrUnknownEvent never really returns, but will fire if SecuritySpy // adds new events this library doesn't know about. - ErrorUnknownEvent = fmt.Errorf("unknown event") - // ErrorCAMParseFail will return if the camera number in an event stream does not exist. + ErrUnknownEvent = fmt.Errorf("unknown event") + // ErrCAMParseFail will return if the camera number in an event stream does not exist. // If you see this, run Refresh() more often, or fix your flaky camera connection. - ErrorCAMParseFail = fmt.Errorf("CAM parse failed") - // ErrorIDParseFail will return if the camera number provided by the event stream is not a number. + ErrCAMParseFail = fmt.Errorf("CAM parse failed") + // ErrIDParseFail will return if the camera number provided by the event stream is not a number. // This should never happen, but future versions of SecuritySpy could trigger this if formats change. - ErrorIDParseFail = fmt.Errorf("ID parse failed") - // ErrorCAMMissing like the errors above should never return. + ErrIDParseFail = fmt.Errorf("ID parse failed") + // ErrCAMMissing like the errors above should never return. // This is triggered by a corrupted event format. - ErrorCAMMissing = fmt.Errorf("camera number missing") - // ErrorDateParseFail will only trigger if the time stamp format for events changes. - ErrorDateParseFail = fmt.Errorf("timestamp parse failed") - // ErrorDisconnect becomes the msg in a custom event when the SecSpy event stream is disconnected. - ErrorDisconnect = fmt.Errorf("server connection closed") + ErrCAMMissing = fmt.Errorf("camera number missing") + // ErrDateParseFail will only trigger if the time stamp format for events changes. + ErrDateParseFail = fmt.Errorf("timestamp parse failed") + // ErrDisconnect becomes the msg in a custom event when the SecSpy event stream is disconnected. + ErrDisconnect = fmt.Errorf("server connection closed") ) const ( diff --git a/files.go b/files.go index cad4cae..4c6d4e7 100644 --- a/files.go +++ b/files.go @@ -27,11 +27,10 @@ const ( // Errors returned by the Files type methods. var ( - // ErrorPathExists returns when a requested write path already exists. - ErrorPathExists = fmt.Errorf("cannot overwrite existing path") - - // ErrorInvalidName returns when requesting a file download and the filename is invalid. - ErrorInvalidName = fmt.Errorf("invalid file name") + // ErrPathExists returns when a requested write path already exists. + ErrPathExists = fmt.Errorf("cannot overwrite existing path") + // ErrInvalidName returns when requesting a file download and the filename is invalid. + ErrInvalidName = fmt.Errorf("invalid file name") ) // Files powers the Files interface. @@ -110,13 +109,13 @@ func (f *Files) GetFile(name string) (*File, error) { } if fileExtSplit := strings.Split(name, "."); len(fileExtSplit) != fileParts { - return file, ErrorInvalidName + return file, ErrInvalidName } else if nameDateSplit := strings.Split(fileExtSplit[0], " "); len(fileExtSplit) < fileParts { - return file, ErrorInvalidName + return file, ErrInvalidName } else if file.Updated, err = time.Parse(FileDateFormat, nameDateSplit[0]); err != nil { - return file, ErrorInvalidName + return file, ErrInvalidName } else if file.Camera = f.server.Cameras.ByName(nameDateSplit[len(nameDateSplit)-1]); file.Camera == nil { - return file, ErrorCAMMissing + return file, ErrCAMMissing } else if file.Link.Type = "video/quicktime"; fileExtSplit[1] == "jpg" { file.Link.Type = "image/jpeg" } @@ -134,7 +133,7 @@ func (f *Files) GetFile(name string) (*File, error) { // Returns an error if path exists. func (f *File) Save(path string) (int64, error) { if _, err := os.Stat(path); !os.IsNotExist(err) { - return 0, ErrorPathExists + return 0, ErrPathExists } body, err := f.Get(true) diff --git a/go.mod b/go.mod index cc7d82e..68404af 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,15 @@ module golift.io/securityspy -go 1.16 +go 1.17 require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang/mock v1.5.0 + github.com/golang/mock v1.6.0 github.com/stretchr/testify v1.6.2-0.20201103103935-92707c0b2d50 golift.io/ffmpeg v1.0.1 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect +) + +require ( + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index 6fd450b..b0d97a5 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,39 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.2-0.20201103103935-92707c0b2d50 h1:aQdElrdadJZjGar4PipPBSpVh3yyDIuDSaM5PbMn6o8= github.com/stretchr/testify v1.6.2-0.20201103103935-92707c0b2d50/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golift.io/ffmpeg v1.0.1 h1:uZZ/HEH9bydD6vjiGdwso80U8Wd3F6o/s4HN/de+N78= golift.io/ffmpeg v1.0.1/go.mod h1:CtNdNCyVbHuMhA4RPR27UVG5ukLl1vlZjZpgNWhg+VQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ptz.go b/ptz.go index 98bb428..ab8d843 100644 --- a/ptz.go +++ b/ptz.go @@ -8,12 +8,11 @@ import ( ) var ( - // ErrorPTZNotOK is returned for any command that has a successful web request, + // ErrPTZNotOK is returned for any command that has a successful web request, // but the reply does not end with the word OK. - ErrorPTZNotOK = fmt.Errorf("PTZ command not OK") - - // ErrorPTZRange returns when a PTZ preset outside of 1-8 is provided. - ErrorPTZRange = fmt.Errorf("PTZ preset out of range 1-8") + ErrPTZNotOK = fmt.Errorf("PTZ command not OK") + // ErrPTZRange returns when a PTZ preset outside of 1-8 is provided. + ErrPTZRange = fmt.Errorf("PTZ preset out of range 1-8") ) // PTZ are what "things" a camera can do. Use the bound methods to interact @@ -163,7 +162,7 @@ func (z *PTZ) Preset(preset PTZpreset) error { case PTZpreset8: return z.ptzReq(ptzCommandSavePreset8) default: - return ErrorPTZRange + return ErrPTZRange } } @@ -187,7 +186,7 @@ func (z *PTZ) PresetSave(preset PTZpreset) error { case PTZpreset8: return z.ptzReq(ptzCommandPreset8) default: - return ErrorPTZRange + return ErrPTZRange } } diff --git a/securityspy_test.go b/securityspy_test.go index d76f1b5..0794aa0 100644 --- a/securityspy_test.go +++ b/securityspy_test.go @@ -170,7 +170,7 @@ func TestUnmarshalXMLDuration(t *testing.T) { for _, val := range good { assert.Nil(xml.Unmarshal([]byte(""+val+""), &bit), "unmarshalling must not produce an error") assert.Equal(val, bit.Val, "the value was not unmarshalled correctly") - num, err := strconv.ParseFloat(val, 10) + num, err := strconv.ParseFloat(val, 64) assert.Nil(err, "must not be an error parsing test numbers") assert.Equal(num, bit.Seconds(), "the value was not unmarshalled correctly") assert.Equal(val, bit.Val, "the value was not unmarshalled correctly") diff --git a/server/server.go b/server/server.go index 5cc5c0d..148f6ba 100644 --- a/server/server.go +++ b/server/server.go @@ -14,9 +14,9 @@ import ( "time" ) -// ErrorCmdNotOK is returned for any command that has a successful web request, +// ErrCmdNotOK is returned for any command that has a successful web request, // but the reply does not end with the word OK. -var ErrorCmdNotOK = fmt.Errorf("command unsuccessful") +var ErrCmdNotOK = fmt.Errorf("command unsuccessful") // DefaultTimeout it used for almost every request to SecuritySpy. Adjust as needed. const DefaultTimeout = 10 * time.Second @@ -220,7 +220,7 @@ func (s *Config) SimpleReq(apiURI string, params url.Values, cameraNum int) erro body, err := ioutil.ReadAll(resp.Body) if err != nil || !strings.HasSuffix(string(body), "OK") { - return ErrorCmdNotOK + return ErrCmdNotOK } return nil