Enhance camera management features: add debug endpoint for camera manager state, implement live camera routes without authentication, and improve logging for camera initialization and status checks. Update Docker configuration to include environment variables for the web app.

This commit is contained in:
salirezav
2025-09-02 15:31:47 -04:00
parent e0fa947da6
commit 0e53034f30
138 changed files with 672 additions and 18 deletions

View File

@@ -0,0 +1,134 @@
import { useState, useEffect, useRef } from 'react'
interface LiveCameraViewProps {
cameraName: string
}
export function LiveCameraView({ cameraName }: LiveCameraViewProps) {
const [isStreaming, setIsStreaming] = useState(false)
const [error, setError] = useState<string | null>(null)
const [loading, setLoading] = useState(true)
const imgRef = useRef<HTMLImageElement>(null)
const API_BASE = import.meta.env.VITE_VISION_API_URL || 'http://localhost:8000'
useEffect(() => {
startStreaming()
return () => stopStreaming()
}, [cameraName])
const startStreaming = async () => {
try {
setLoading(true)
setError(null)
// Start the stream
const response = await fetch(`${API_BASE}/cameras/${cameraName}/start-stream`, {
method: 'POST'
})
if (response.ok) {
setIsStreaming(true)
// Set the stream source with timestamp to prevent caching
if (imgRef.current) {
imgRef.current.src = `${API_BASE}/cameras/${cameraName}/stream?t=${Date.now()}`
}
} else {
throw new Error(`Failed to start stream: ${response.statusText}`)
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to start stream'
setError(errorMessage)
} finally {
setLoading(false)
}
}
const stopStreaming = async () => {
try {
if (isStreaming) {
await fetch(`${API_BASE}/cameras/${cameraName}/stop-stream`, {
method: 'POST'
})
setIsStreaming(false)
}
} catch (err) {
console.error('Error stopping stream:', err)
}
}
const handleImageError = () => {
setError('Failed to load camera stream')
}
const handleImageLoad = () => {
setError(null)
}
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-900">
<div className="text-center text-white">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-white mx-auto"></div>
<p className="mt-4">Starting camera stream...</p>
</div>
</div>
)
}
if (error) {
return (
<div className="min-h-screen flex items-center justify-center bg-gray-900">
<div className="text-center text-white">
<div className="bg-red-600 rounded-full w-16 h-16 flex items-center justify-center mx-auto mb-4">
<svg className="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<h2 className="text-xl font-semibold mb-2">Stream Error</h2>
<p className="text-gray-300 mb-4">{error}</p>
<button
onClick={startStreaming}
className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-md"
>
Retry
</button>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="relative">
{/* Camera Label */}
<div className="absolute top-4 left-4 z-10">
<div className="bg-black bg-opacity-75 text-white px-3 py-1 rounded-md text-sm font-medium">
{cameraName} - Live View
</div>
</div>
{/* Live Stream */}
<img
ref={imgRef}
alt={`Live stream from ${cameraName}`}
className="max-w-full max-h-screen object-contain"
onError={handleImageError}
onLoad={handleImageLoad}
/>
{/* Status Indicator */}
<div className="absolute bottom-4 right-4 z-10">
<div className="flex items-center space-x-2 bg-black bg-opacity-75 text-white px-3 py-1 rounded-md">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<span className="text-sm">LIVE</span>
</div>
</div>
</div>
</div>
)
}