Skip to content

Commit

Permalink
Update server files and recordings directory
Browse files Browse the repository at this point in the history
  • Loading branch information
infinitel8p committed Nov 12, 2023
1 parent 1ce13b8 commit 9a8fcbc
Show file tree
Hide file tree
Showing 18 changed files with 339 additions and 60 deletions.
2 changes: 1 addition & 1 deletion main.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
if not recording:
logging.info("Device not detected. Start recording.")
camera.start_recording(
f"./recordings/video_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.h264")
f"./server/public/recordings/video_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.h264")
recording = True
time.sleep(1)
except KeyboardInterrupt:
Expand Down
63 changes: 63 additions & 0 deletions main2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import json
import logging
import os
import time
from datetime import datetime
import picamera
import RPi.GPIO as GPIO
from dotenv import load_dotenv
import websocket
import threading
import io
import base64

# ... [Your existing setup code goes here] ...

# Initialize the camera
camera = picamera.PiCamera()
recording = False

# Set up WebSocket
ws = None

def send_frame():
global ws
while True:
if ws:
stream = io.BytesIO()
camera.capture(stream, format='jpeg')
stream.seek(0)
frame = stream.read()
frame_base64 = base64.b64encode(frame).decode('utf-8')
ws.send(frame_base64)
stream.close()
time.sleep(0.1) # Adjust frame rate

def on_open(new_ws):
global ws
ws = new_ws
send_frame_thread = threading.Thread(target=send_frame)
send_frame_thread.start()

def start_websocket():
websocket.enableTrace(True)
ws_app = websocket.WebSocketApp("ws://localhost:3000", on_open=on_open)
ws_app.run_forever()

# Start WebSocket in a separate thread
websocket_thread = threading.Thread(target=start_websocket)
websocket_thread.start()

# Your existing GPIO and recording logic goes here
# ...

try:
# Your existing loop logic goes here
# ...
except KeyboardInterrupt:
# Your existing KeyboardInterrupt handling goes here
# ...
finally:
GPIO.cleanup()
if ws:
ws.close()
File renamed without changes.
6 changes: 6 additions & 0 deletions server/public/css/recordings.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.video-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 10px;
padding: 20px;
}
Empty file added server/public/css/settings.css
Empty file.
6 changes: 5 additions & 1 deletion server/public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ body {
border-radius: 5px;
}

.active {
color: black !important;
}

.main-content {
padding: 20px;
text-align: center;
Expand All @@ -53,7 +57,7 @@ body {

#videoCanvas {
max-width: 100%;
height: auto;
height: 400px;
border: 1px solid #ddd;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
Expand Down
12 changes: 7 additions & 5 deletions server/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,23 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Security Cam Webserver</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/index.css">
<script src="./js/script.js"></script>
</head>

<body>
<nav class="navbar">
<ul class="navbar-nav">
<li class="nav-item"><a href="/">Home</a></li>
<li class="nav-item"><a href="/recordings">Recordings</a></li>
<li class="nav-item"><a href="/settings">Settings</a></li>
<li class="nav-item"><a class="nav-link" href="./index.html">Home</a></li>
<li class="nav-item"><a class="nav-link" href="./recordings.html">Recordings</a></li>
<li class="nav-item"><a class="nav-link" href="./settings.html">Settings</a></li>
</ul>
</nav>
<div class="main-content">
<h1>Security Cam Webserver</h1>
<canvas id="videoCanvas"></canvas>
<canvas id="videoCanvas" width="1920" height="1080"></canvas>
</div>
<script src="js/script.js"></script>
<script src="./js/stream.js"></script>
</body>

</html>
21 changes: 21 additions & 0 deletions server/public/js/playback.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
document.addEventListener('DOMContentLoaded', () => {
fetch('/video-list')
.then(response => response.json())
.then(videos => {
const container = document.querySelector('.video-grid');
videos.forEach(video => {
const videoElement = document.createElement('video');
videoElement.width = 320; // set your dimensions
videoElement.height = 240;
videoElement.controls = true;

const sourceElement = document.createElement('source');
sourceElement.src = `recordings/${video}`;
sourceElement.type = 'video/mp4';

videoElement.appendChild(sourceElement);
container.appendChild(videoElement);
});
})
.catch(error => console.error('Error fetching video list:', error));
});
28 changes: 13 additions & 15 deletions server/public/js/script.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
const canvas = document.getElementById('videoCanvas');
const context = canvas.getContext('2d');
document.addEventListener('DOMContentLoaded', () => {
const navItems = document.querySelectorAll('.nav-item');

const ws = new WebSocket('ws://localhost:3000');
ws.binaryType = 'blob';
navItems.forEach(item => {
const navLink = item.querySelector('.nav-link');
let currentHref = window.location.href;

ws.onmessage = function (event) {
const blob = new Blob([event.data], { type: 'image/jpeg' });
if (currentHref.endsWith('/')) {
currentHref += 'index.html';
}

const url = URL.createObjectURL(blob);

const image = new Image();
image.onload = function () {
context.drawImage(this, 0, 0, canvas.width, canvas.height);
URL.revokeObjectURL(url);
};
image.src = url;
};
if (navLink.href === currentHref) {
navLink.classList.add('active');
}
});
});
48 changes: 48 additions & 0 deletions server/public/js/stream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const canvas = document.getElementById('videoCanvas');
const context = canvas.getContext('2d');

const ws = new WebSocket('ws://localhost:3000');
ws.binaryType = 'blob';

let python_connected = false;

ws.onopen = function () {
const clientInfo = {
type: 'Browser',
userAgent: navigator.userAgent
};
ws.send(JSON.stringify(clientInfo));
showMessageOnCanvas("Searching for stream...");
};

ws.onmessage = function (event) {
if (event.data === 'PYTHON_CONNECTED') {
console.log('Python script connected');
python_connected = true;
} else if (event.data === 'PYTHON_DISCONNECTED') {
console.log('Python script disconnected');
python_connected = false;
setTimeout(() => showMessageOnCanvas("Stream disconnected. Waiting for stream..."), 2000);
} else if (python_connected) {
const blob = new Blob([event.data], { type: 'image/jpeg' });
const url = URL.createObjectURL(blob);
const image = new Image();
image.onload = function () {
context.drawImage(this, 0, 0, canvas.width, canvas.height);
URL.revokeObjectURL(url);
};
image.src = url;
}
};

ws.onclose = function () {
console.log('WebSocket connection closed');
}

function showMessageOnCanvas(message) {
context.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
context.font = '40px Arial'; // Set font size and style
context.fillStyle = 'black'; // Set font color
context.textAlign = 'center'; // Align text to center
context.fillText(message, canvas.width / 2, canvas.height / 2); // Draw text on canvas
}
30 changes: 30 additions & 0 deletions server/public/recordings.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Security Cam Webserver</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/recordings.css">
<script src="./js/script.js"></script>
</head>

<body>
<nav class="navbar">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="./index.html">Home</a></li>
<li class="nav-item"><a class="nav-link" href="./recordings.html">Recordings</a></li>
<li class="nav-item"><a class="nav-link" href="./settings.html">Settings</a></li>

</ul>
</nav>

<div class="main-content">
<h1>Recordings</h1>
<div class="video-grid"></div>
</div>
<script src="./js/playback.js"></script>
</body>

</html>
Empty file.
Binary file not shown.
Binary file not shown.
Binary file not shown.
27 changes: 27 additions & 0 deletions server/public/settings.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Security Cam Webserver</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/settings.css">
<script src="./js/script.js"></script>
</head>

<body>
<nav class="navbar">
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" href="./index.html">Home</a></li>
<li class="nav-item"><a class="nav-link" href="./recordings.html">Recordings</a></li>
<li class="nav-item"><a class="nav-link" href="./settings.html">Settings</a></li>
</ul>
</nav>

<div class="main-content">
<h1>Settings</h1>
</div>
</body>

</html>
74 changes: 56 additions & 18 deletions server/server.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,80 @@
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const fs = require('fs');
const app = express();
const port = 3000;

// Serve static files from the 'public' directory
app.use(express.static('public'));

// Create an HTTP server
const server = http.createServer(app);

// Create a WebSocket server
const wss = new WebSocket.Server({ server });

let connectedClients = 0;
let clients = {};

// Handle WebSocket connection
wss.on('connection', function connection(ws) {
connectedClients++;
console.log('A new client connected! Total clients:', connectedClients);
let clientId;

ws.on('message', function incoming(data) {
if (connectedClients > 1) {
// Broadcast the received data (video frame) to all connected clients
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
if (!clientId) {
clientId = data.toString();
clients[clientId] = ws;
updateClients();
return;
}

// Regular message handling
broadcastData(data, ws);
});

ws.on('close', () => {
connectedClients--;
console.log('Client disconnected. Total clients:', connectedClients);
delete clients[clientId];
updateClients();
});
});

// Notify all clients about Python script connection status
function updateClients() {
const isPythonConnected = !!clients['Python Script'];
const isBrowserConnected = Object.keys(clients).some(id => id !== 'Python Script');

if (clients['Python Script']) {
// Tell the Python script to start/stop sending frames based on browser connection
const message = isBrowserConnected ? 'START' : 'STOP';
clients['Python Script'].send(message);
}

// Notify all browsers about Python script connection status
Object.keys(clients).forEach(clientId => {
if (clientId !== 'Python Script') {
const message = isPythonConnected ? 'PYTHON_CONNECTED' : 'PYTHON_DISCONNECTED';
clients[clientId].send(message);
}
});
}

// Broadcast data to all clients except sender
function broadcastData(data, senderWs) {
Object.values(clients).forEach(client => {
if (client !== senderWs && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
}

// endpoint to get list of video files on server
app.get('/video-list', (req, res) => {
const recordingsPath = './public/recordings'
fs.readdir(recordingsPath, (err, files) => {
if (err) {
res.status(500).send('Error reading video files');
return;
}
res.json(files.filter(file => file.endsWith('.mp4') || file.endsWith('.h264')));
});
});

// Start the server
// start server
server.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
Loading

0 comments on commit 9a8fcbc

Please sign in to comment.