diff --git a/frontend/src/components/VideoTable.tsx b/frontend/src/components/VideoTable.tsx index 5cdd8a1..9c19b97 100644 --- a/frontend/src/components/VideoTable.tsx +++ b/frontend/src/components/VideoTable.tsx @@ -75,7 +75,7 @@ export function VideoTable(props: VideoTableProps) { const startedAt = new Date(video.started_at); const endedAt = video.ended_at && new Date(video.ended_at); - const available = !!endedAt; + const available = video?.status !== "recording"; const minutes = Math.floor( (video?.duration || 0) / (1_000_000_000 * 60), diff --git a/go.mod b/go.mod index 4495efd..a2ffbb4 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/go-chi/chi/v5 v5.1.0 github.com/gomodule/redigo v1.9.2 github.com/google/uuid v1.6.0 - github.com/initialed85/djangolang v0.0.25 + github.com/initialed85/djangolang v0.0.27 github.com/jackc/pgtype v1.14.3 github.com/jackc/pgx/v5 v5.6.0 github.com/jmoiron/sqlx v1.4.0 diff --git a/go.sum b/go.sum index 19439fc..24fcc00 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/initialed85/djangolang v0.0.25 h1:5vb/R9KpOKZ8sasD2MFFlh250ciABqqYGhEdsw4DV6w= -github.com/initialed85/djangolang v0.0.25/go.mod h1:3mqaFDBq9zfX/18jyDCHs5UpH2r5XGgBj/SlRcgfY64= +github.com/initialed85/djangolang v0.0.27 h1:y/+0WwvKmJ0eyJIq/fWGqVFjyNHRgclkZiSV7kf/r7w= +github.com/initialed85/djangolang v0.0.27/go.mod h1:3mqaFDBq9zfX/18jyDCHs5UpH2r5XGgBj/SlRcgfY64= github.com/initialed85/structmeta v0.0.0-20240802152142-39f398ef1ab7 h1:G9Z1k4TyxQ/9Kk4ZSuw82WZCxJayZf12Aos2MorzKRg= github.com/initialed85/structmeta v0.0.0-20240802152142-39f398ef1ab7/go.mod h1:hTGWTsfgy6Um+L8e3Qcj8/pBkHGcIGxEpZAKziWhQfc= github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso= diff --git a/pkg/api/bin/api b/pkg/api/bin/api index dd3a474..f30e0fd 100755 Binary files a/pkg/api/bin/api and b/pkg/api/bin/api differ diff --git a/pkg/api/camera.go b/pkg/api/camera.go index 253d1e1..8e644e7 100755 --- a/pkg/api/camera.go +++ b/pkg/api/camera.go @@ -642,10 +642,10 @@ func SelectCameras( err = object.FromItem(item) if err != nil { - return nil, fmt.Errorf("failed to call Camera.FromItem; err: %v", err) + return nil, err } - func() { + err = func() error { possibleRootTableName := ctx.Value(_rootTableNameContextKey) rootTableName, _ := possibleRootTableName.(string) if rootTableName == "" { @@ -653,7 +653,7 @@ func SelectCameras( } if rootTableName != CameraTable { - object.ReferencedByVideoCameraIDObjects, _ = SelectVideos( + object.ReferencedByVideoCameraIDObjects, err = SelectVideos( ctx, tx, fmt.Sprintf("%v = $1", VideoTableCameraIDColumn), @@ -662,10 +662,18 @@ func SelectCameras( nil, object.ID, ) + if err != nil { + return err + } } + + return nil }() + if err != nil { + return nil, err + } - func() { + err = func() error { possibleRootTableName := ctx.Value(_rootTableNameContextKey) rootTableName, _ := possibleRootTableName.(string) if rootTableName == "" { @@ -673,7 +681,7 @@ func SelectCameras( } if rootTableName != CameraTable { - object.ReferencedByDetectionCameraIDObjects, _ = SelectDetections( + object.ReferencedByDetectionCameraIDObjects, err = SelectDetections( ctx, tx, fmt.Sprintf("%v = $1", DetectionTableCameraIDColumn), @@ -682,8 +690,16 @@ func SelectCameras( nil, object.ID, ) + if err != nil { + return err + } } + + return nil }() + if err != nil { + return nil, err + } objects = append(objects, object) } diff --git a/pkg/api/detection.go b/pkg/api/detection.go index 6f61771..2ebba07 100755 --- a/pkg/api/detection.go +++ b/pkg/api/detection.go @@ -832,16 +832,19 @@ func SelectDetections( err = object.FromItem(item) if err != nil { - return nil, fmt.Errorf("failed to call Detection.FromItem; err: %v", err) + return nil, err } if !types.IsZeroUUID(object.CameraID) { - object.CameraIDObject, _ = SelectCamera( + object.CameraIDObject, err = SelectCamera( ctx, tx, fmt.Sprintf("%v = $1", CameraTablePrimaryKeyColumn), object.CameraID, ) + if err != nil { + return nil, err + } } objects = append(objects, object) diff --git a/pkg/api/video.go b/pkg/api/video.go index e4a8b6e..d7b1328 100755 --- a/pkg/api/video.go +++ b/pkg/api/video.go @@ -880,16 +880,19 @@ func SelectVideos( err = object.FromItem(item) if err != nil { - return nil, fmt.Errorf("failed to call Video.FromItem; err: %v", err) + return nil, err } if !types.IsZeroUUID(object.CameraID) { - object.CameraIDObject, _ = SelectCamera( + object.CameraIDObject, err = SelectCamera( ctx, tx, fmt.Sprintf("%v = $1", CameraTablePrimaryKeyColumn), object.CameraID, ) + if err != nil { + return nil, err + } } objects = append(objects, object) diff --git a/pkg/segment_producer/helpers.go b/pkg/segment_producer/helpers.go index 7e6a84c..994cacd 100644 --- a/pkg/segment_producer/helpers.go +++ b/pkg/segment_producer/helpers.go @@ -16,7 +16,7 @@ func GenerateThumbnail(videoPath, imagePath string) error { videoPath, "-ss", "00:00:00.000", - "-vframes", + "-frames:v", "1", imagePath, } @@ -34,7 +34,7 @@ func GenerateThumbnail(videoPath, imagePath string) error { err := cmd.Run() if err != nil { - return fmt.Errorf("failed to generate thumbnail: %v; stdout=%#+v, stderr=%#+v", err, stdout, stderr) + return fmt.Errorf("failed to generate thumbnail: %v; stdout=%#+v, stderr=%#+v", err, stdout.String(), stderr.String()) } return nil diff --git a/pkg/segment_producer/segment_producer.go b/pkg/segment_producer/segment_producer.go index a0b09ea..6b241c6 100644 --- a/pkg/segment_producer/segment_producer.go +++ b/pkg/segment_producer/segment_producer.go @@ -412,7 +412,7 @@ func Run() error { return err } - func() error { + err = func() error { tx, err := db.BeginTxx(ctx, nil) if err != nil { return err @@ -440,11 +440,24 @@ func Run() error { return err } - for _, orphanedVideo := range orphanedVideos { - log.Printf("marking orphaned video %s as failed", orphanedVideo.ID) + for _, video := range orphanedVideos { + log.Printf("marking orphaned video %s as failed", video.ID) + + filePath := filepath.Join(destinationPath, video.FileName) + + _, fileName := filepath.Split(filePath) + video.FileName = fileName - orphanedVideo.Status = helpers.Ptr("failed") - err = orphanedVideo.Update(ctx, tx, false) + ext := filepath.Ext(fileName) + thumbnailPath := fmt.Sprintf("%v.jpg", filePath[:len(filePath)-len(ext)]) + err = GenerateThumbnail(filePath, thumbnailPath) + if err == nil { + _, thumbnailName := filepath.Split(thumbnailPath) + video.ThumbnailName = &thumbnailName + } + + video.Status = helpers.Ptr("failed") + err = video.Update(ctx, tx, false) if err != nil { return err } @@ -461,7 +474,7 @@ func Run() error { return fmt.Errorf("failed to handle orphaned videos: %v", err) } - log.Printf("api confirmed %#+v", camera) + log.Printf("api confirmed camera %s | %s | %s", camera.ID, camera.StreamURL, camera.Name) mu := new(sync.Mutex) var video *api.Video @@ -482,6 +495,19 @@ func Run() error { if video != nil { log.Printf("warning: there was a Video in-flight that should have been closed out- marking %v as failed", video.ID) + filePath := filepath.Join(destinationPath, video.FileName) + + _, fileName := filepath.Split(filePath) + video.FileName = fileName + + ext := filepath.Ext(fileName) + thumbnailPath := fmt.Sprintf("%v.jpg", filePath[:len(filePath)-len(ext)]) + err = GenerateThumbnail(filePath, thumbnailPath) + if err == nil { + _, thumbnailName := filepath.Split(thumbnailPath) + video.ThumbnailName = &thumbnailName + } + video.Status = helpers.Ptr("failed") err = video.Update(ctx, tx, false) if err != nil {