Skip to content

Commit

Permalink
Update Djangolang, bit of tidying up
Browse files Browse the repository at this point in the history
  • Loading branch information
initialed85 committed Jan 5, 2025
1 parent 6ae63e1 commit 34e9cd9
Show file tree
Hide file tree
Showing 60 changed files with 7,746 additions and 1,213 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ tmp/
**/__pycache__
**/.openapi-generator
**/api/docs/
cmd/api/api
33 changes: 16 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
# camry

# status: not yet working
# status: in production at my house for ages now

Intended as the spiritual sucessor to [initialed85/cameranator](https://github.com/initialed85/cameranator), built on [initialed85/djangolang](https://github.com/initialed85/djangolang)
Intended as the spiritual sucessor to [initialed85/cameranator](https://github.com/initialed85/cameranator), built on
[initialed85/djangolang](https://github.com/initialed85/djangolang)

## Tasks

- [TODO] Come up with a more efficient way to use the detections data, probably some sort of aggregation
## Features

## Usage
- Lots of nice caching etc thanks to [initialed85/djangolang](https://github.com/initialed85/djangolang)
- Frontend is a PWA for a good mobile / offline experience
- Uses YOLO for object detection
- See
[initialed85/home-ops/tree/master/applications/camry](https://github.com/initialed85/home-ops/tree/master/applications/camry)
for some inspiration on how to deploy

### Testing
## Tasks

```shell
./run-env.sh full
```
## Usage

### Development

Expand All @@ -32,19 +34,16 @@ REDIS_URL=redis://localhost:6379 DJANGOLANG_API_ROOT=/api POSTGRES_DB=camry POST
websocat -B 1048576 ws://localhost:7070/api/__stream | jq

# shell 5
curl -X POST http://localhost:7070/api/cameras -d '[{"name": "Driveway", "stream_url": "rtsp://192.168.137.31:554/Streaming/Channels/101"}, {"name": "Front door", "stream_url": "rtsp://192.168.137.32:554/Streaming/Channels/101"}, {"name": "Side gate", "stream_url": "rtsp://192.168.137.33:554/Streaming/Channels/101"}]' | jq
devserver --address 0.0.0.0:6060

# shell 6
DJANGOLANG_API_ROOT=/api DESTINATION_PATH=media ENABLE_PASSTHROUGH=1 CAMERA_NAME='Driveway' NET_CAM_URL=rtsp://192.168.137.31:554/Streaming/Channels/101 POSTGRES_DB=camry POSTGRES_PASSWORD=NoNVR\!11 go run ./cmd segment_producer
curl -X POST http://localhost:7070/api/cameras -d '[{"name": "Driveway", "stream_url": "rtsp://192.168.137.31:554/Streaming/Channels/101"}, {"name": "Front door", "stream_url": "rtsp://192.168.137.32:554/Streaming/Channels/101"}, {"name": "Side gate", "stream_url": "rtsp://192.168.137.33:554/Streaming/Channels/101"}]' | jq

# shell 7
DJANGOLANG_API_ROOT=/api DESTINATION_PATH=media ENABLE_PASSTHROUGH=1 CAMERA_NAME='Front door' NET_CAM_URL=rtsp://192.168.137.32:554/Streaming/Channels/101 POSTGRES_DB=camry POSTGRES_PASSWORD=NoNVR\!11 go run ./cmd segment_producer
DJANGOLANG_API_ROOT=/api DESTINATION_PATH=media ENABLE_PASSTHROUGH=1 POSTGRES_DB=camry POSTGRES_PASSWORD=NoNVR\!11 go run ./cmd segment_producer

# shell 8
DJANGOLANG_API_ROOT=/api DESTINATION_PATH=media ENABLE_PASSTHROUGH=1 CAMERA_NAME='Side gate' NET_CAM_URL=rtsp://192.168.137.33:554/Streaming/Channels/101 POSTGRES_DB=camry POSTGRES_PASSWORD=NoNVR\!11 go run ./cmd segment_producer

# shell 9

DEBUG=1 API_URL=http://localhost:7070 SOURCE_PATH=media ~/.venv/camry/bin/python3 -m object_detector
```

### Scratch
Expand Down
Binary file removed cmd/api/api
Binary file not shown.
146 changes: 6 additions & 140 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
@@ -1,168 +1,34 @@
package main

import (
"context"
"fmt"
"log"
"net/http"
"os"
"strings"
"time"

"github.com/go-chi/chi/v5"
"github.com/initialed85/camry/internal"
"github.com/initialed85/camry/pkg/api"
"github.com/initialed85/djangolang/pkg/config"
"github.com/initialed85/djangolang/pkg/server"
)

type ClaimRequest struct {
ClaimDurationSeconds float64 `json:"claim_duration_seconds"`
}

func RunServeWithEnvironment(
httpMiddlewares []server.HTTPMiddleware,
objectMiddlewares []server.ObjectMiddleware,
addCustomHandlers func(chi.Router) error,
) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

port := config.Port()

db, err := config.GetDBFromEnvironment(ctx)
if err != nil {
log.Fatalf("%v", err)
}
defer func() {
db.Close()
}()

redisPool, err := config.GetRedisFromEnvironment()
if err != nil {
log.Fatalf("%v", err)
}
defer func() {
_ = redisPool.Close()
}()

actualAddCustomHandlers := func(r chi.Router) error {
claimVideoForObjectDetectorHandler, err := api.GetHTTPHandler(
http.MethodPatch,
"/custom/claim-video-for-object-detector",
http.StatusOK,
func(
ctx context.Context,
pathParams server.EmptyPathParams,
queryParams server.EmptyQueryParams,
req ClaimRequest,
rawReq any,
) (*api.Video, error) {
now := time.Now().UTC()

claimUntil := now.Add(time.Second * time.Duration(req.ClaimDurationSeconds))

if claimUntil.Sub(now) <= 0 {
return nil, fmt.Errorf("claim_duration_seconds too short; must result in a claim that expires in the future")
}

tx, err := db.Begin(ctx)
if err != nil {
return nil, err
}

defer func() {
_ = tx.Rollback(ctx)
}()

video := &api.Video{}

// TODO: this is hacky; wait for as long as other processes are _probably_ claiming for plus some fudge (but not a certainty of course)
err = video.AdvisoryLockWithRetries(ctx, tx, 3, time.Duration(req.ClaimDurationSeconds)+(time.Second*2), time.Second*1)
if err != nil {
return nil, err
}

videos, _, _, _, _, err := api.SelectVideos(
ctx,
tx,
fmt.Sprintf(
"%v = 'needs detection' AND %v < now()",
api.VideoTableStatusColumn,
api.VideoTableObjectDetectorClaimedUntilColumn,
),
internal.Ptr(fmt.Sprintf(
"%v DESC",
api.VideoTableStartedAtColumn,
)),
internal.Ptr(1),
nil,
)
if err != nil {
return nil, err
}

if len(videos) == 0 {
return nil, nil
}

if len(videos) > 1 {
return nil, fmt.Errorf("wanted exactly 1 unclaimed video, got %d", len(videos))
}

video = videos[0]

video.ObjectDetectorClaimedUntil = claimUntil
video.ObjectTrackerClaimedUntil = time.Time{} // zero to ensure we don't wipe out an existing value

err = video.Update(ctx, tx, false)
if err != nil {
return nil, err
}

err = tx.Commit(ctx)
if err != nil {
return nil, err
}

return video, nil
},
)
if err != nil {
return err
}

r.Patch("/claim-video-for-object-detector", claimVideoForObjectDetectorHandler.ServeHTTP)

if addCustomHandlers != nil {
err = addCustomHandlers(r)
if err != nil {
return err
}
}

return nil
}

api.RunServeWithArguments(ctx, cancel, port, db, redisPool, nil, nil, actualAddCustomHandlers)
}
var log = api.ThisLogger()

func main() {
if len(os.Args) < 2 {
log.Fatal("first argument must be command (one of 'serve', 'dump-openapi-json', 'dump-openapi-yaml')")
log.Fatal("first argument must be command (one of 'dump-config', 'dump-openapi-json', 'dump-openapi-yaml' or 'serve')")
}

command := strings.TrimSpace(strings.ToLower(os.Args[1]))

switch command {

case "dump-config":
config.DumpConfig()

case "dump-openapi-json":
api.RunDumpOpenAPIJSON()

case "dump-openapi-yaml":
api.RunDumpOpenAPIYAML()

case "serve":
RunServeWithEnvironment(nil, nil, nil)
api.RunServeWithEnvironment(nil, nil, nil)
}
}
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ services:
image: swaggerapi/swagger-ui:v5.4.2
environment:
- "BASE_URL=/"
- "SWAGGER_JSON_URL=http://host.docker.internal:7070/api/openapi.json"
- "SWAGGER_JSON_URL=http://0.0.0.0:7070/api/openapi.json"
ports:
- "7071:8080/tcp"

Expand Down
Loading

0 comments on commit 34e9cd9

Please sign in to comment.