diff options
Diffstat (limited to 'templates/client.html')
| -rw-r--r-- | templates/client.html | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/templates/client.html b/templates/client.html new file mode 100644 index 0000000..785fbcf --- /dev/null +++ b/templates/client.html @@ -0,0 +1,173 @@ +<!DOCTYPE html> +<html> +<head> + <title>Client {{ client_id }} - Camera Streams</title> + <link rel="stylesheet" href="{{ url_for('static', path='css/main.css') }}"> + <style> + .camera-stream { display: inline-block; margin: 10px; } + video { border: 1px solid #333; } + </style> +</head> +<body> + <h1>Camera Streams for client: {{ client_id }}</h1> + <div class="streams-container"> + {% for camera_id in camera_ids %} + <div class="camera-stream"> + <h2>Camera: {{ camera_id }}</h2> + <img + id="video-{{ camera_id }}" + width="640" + height="480" /> + <div> + <h3>Modify Camera Name</h3> + <input + type="text" + id="new-name-{{ camera_id }}" + placeholder="New Name" + required pattern= "^\S+$" + title="No whitespace allowed."> + <button onclick="sendConfig('{{ camera_id }}', 'modify_camera_name')">Send</button> + </div> + <div> + <h3>Modify Camera Threshold</h3> + <input + type="number" + id="threshold-value-{{ camera_id }}" + placeholder="Threshold" + required step="1" + min="0" + inputmode="numeric" + title="Enter a non-negative integer."> + <button onclick="sendConfig('{{ camera_id }}', 'modify_camera_threshold')">Send</button> + </div> + <div> + <h3>Modify Camera Grace Period</h3> + <input + type="number" + id="grace-value-{{ camera_id }}" + placeholder="Grace Period" + required step="1" + min="0" + inputmode="numeric" + title="Enter a non-negative integer."> + <button onclick="sendConfig('{{ camera_id }}', 'modify_camera_grace_pd')">Send</button> + </div> + <div> + <h3>Recorded Videos for {{ camera_id }}</h3> + <ul id="video-list-{{ camera_id }}" class="scroll-box"> + {% for filename, video_url, timestamp in client_videos[camera_id] %} + <li><a href="{{ video_url }}">{{ filename }}</a> ({{ timestamp }})</li> + <!-- <h3>video file: {{ filename }}</h3> + <video src="{{ video_url }}" type="video/ogg" width="320" height="240" controls></video> + <hr4>creation: {{ timestamp }}</h3> + --> + {% endfor %} + </ul> + </div> + </div> + {% endfor %} +<!-- + <div> + <h3>Add Camera</h3> + <input type="text" id="add-name" placeholder="Camera Name"> + <input type="text" id="add-address" placeholder="Address"> + <button onclick="sendConfig('add_camera')">Send</button> + </div> +--> + <div> + <h3>Remove Camera</h3> + <input + type="text" + id="remove-name" + placeholder="Camera Name" + required pattern="^\S+$" + title="No whitespace allowed."> + <button onclick="sendConfig('remove_camera')">Send</button> + </div> + </div> + <script> + const wsMap = {}; + {% for camera_id in camera_ids %} + // For each camera, open a WebSocket and update the corresponding <img> + (function() { + const cameraId = '{{ camera_id }}'; + const ws = new WebSocket('ws://' + window.location.host + '/ws/{{ client_id }}/' + cameraId); + let currentUrl = null; + ws.onmessage = function(event) { + let image = document.getElementById('video-' + cameraId); + if (currentUrl) { + URL.revokeObjectURL(currentUrl); + } + currentUrl = URL.createObjectURL(event.data); + image.src = currentUrl; + }; + ws.onclose = function(event) { + console.log('WebSocket closed for camera ' + cameraId + ':', event); + }; + ws.onerror = function(event) { + console.log('WebSocket error for camera ' + cameraId + ':', event); + }; + window.addEventListener('beforeunload', function() { + ws.close(); + }); + wsMap[cameraId] = ws; + })(); + // FIXME: Move to a separate function + // For each camera, open a WebSocket to receive notificationf about new videos + // (function() { + // const cameraId = '{{ camera_id }}'; + // const wsVideos = new WebSocket('ws://' + window.location.host + '/ws/{{ client_id }}/' + cameraId + '/videos'); + // wsVideos.onmessage = function(event) { + // let videoFilename = event.data; + // console.log('New video available for camera ' + cameraId + ': ' + videoFilename); + // // On new video notification, + // }; + // })(); + {% endfor %} + + function sendConfig(cameraId, type) { + let msg = {}; + switch(type) { + case 'modify_camera_name': + msg[type] = [ + document.getElementById('new-name-' + cameraId).value + ]; + break; + case 'modify_camera_threshold': + msg[type] = [ + document.getElementById('threshold-value-' + cameraId).value + ]; + break; + case 'modify_camera_grace_pd': + msg[type] = [ + parseInt(document.getElementById('grace-value-' + cameraId).value) + ]; + break; + case 'modify_camera_address': + msg[type] = [ + document.getElementById('address-value-' + cameraId).value + ]; + break; + // case 'add_camera': + // msg[type] = [ + // document.getElementById('add-name').value, + // document.getElementById('add-address').value + // ]; + // break; + case 'remove_camera': + msg[type] = [ + document.getElementById('remove-name').value + ]; + break; + } + const ws = wsMap[cameraId] && wsMap[cameraId] ? wsMap[cameraId] : Object.values(wsMap)[0]; + if (ws && ws.readyState === WebSocket.OPEN) { + console.log("Sending message:", msg, "on ws:", ws); + ws.send(JSON.stringify(msg)); + } else { + alert('WebSocket is not open for camera ' + cameraId); + } + } + </script> +</body> +</html> |
