92 lines
2.6 KiB
Python
92 lines
2.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.http import HttpResponse, StreamingHttpResponse
|
|
from rest_framework.decorators import api_view
|
|
from rest_framework.response import Response
|
|
|
|
from .services import gps_service, network_service, video_service
|
|
|
|
|
|
@api_view(["GET"])
|
|
def dashboard_snapshot(request):
|
|
return Response(
|
|
{
|
|
"gps": gps_service.get_latest(),
|
|
"network": network_service.get_latest(),
|
|
"video": video_service.get_status(),
|
|
}
|
|
)
|
|
|
|
|
|
@api_view(["GET"])
|
|
def gps_latest(request):
|
|
return Response(gps_service.get_latest())
|
|
|
|
|
|
@api_view(["GET"])
|
|
def network_latest(request):
|
|
return Response(network_service.get_latest())
|
|
|
|
|
|
@api_view(["GET"])
|
|
def video_status(request):
|
|
return Response(video_service.get_status())
|
|
|
|
|
|
def video_frame(request):
|
|
try:
|
|
frame, headers = video_service.get_next_frame_with_headers()
|
|
except (FileNotFoundError, RuntimeError) as error:
|
|
status = video_service.get_status()
|
|
return HttpResponse(
|
|
status.get("source_detail") or str(error),
|
|
status=503,
|
|
content_type="text/plain; charset=utf-8",
|
|
)
|
|
|
|
response = HttpResponse(frame, content_type="image/jpeg")
|
|
response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
|
for key, value in headers.items():
|
|
response[key] = value
|
|
return response
|
|
|
|
|
|
def video_stream(request):
|
|
status = video_service.get_status()
|
|
if not status["available"]:
|
|
return HttpResponse(
|
|
status.get("source_detail") or f"JPEG frame directory not found: {status['frame_dir']}",
|
|
status=503,
|
|
content_type="text/plain; charset=utf-8",
|
|
)
|
|
|
|
try:
|
|
fps = float(request.GET.get("fps", status["fps"]))
|
|
except (TypeError, ValueError):
|
|
fps = float(status["fps"])
|
|
|
|
response = StreamingHttpResponse(
|
|
video_service.iter_mjpeg(fps=fps),
|
|
content_type="multipart/x-mixed-replace; boundary=frame",
|
|
)
|
|
response["Cache-Control"] = "no-store, no-cache, must-revalidate, max-age=0"
|
|
return response
|
|
|
|
|
|
@csrf_exempt
|
|
@api_view(["POST"])
|
|
def video_display_probe(request):
|
|
try:
|
|
payload = json.loads(request.body.decode("utf-8"))
|
|
except (UnicodeDecodeError, json.JSONDecodeError):
|
|
return Response({"detail": "invalid json"}, status=400)
|
|
|
|
if not isinstance(payload, dict):
|
|
return Response({"detail": "expected json object"}, status=400)
|
|
|
|
video_service.record_display_probe(payload)
|
|
return Response({"ok": True})
|