Timsupport/camera
0
1const express = require('express');2const http = require('http');3const { Server } = require('socket.io');4const path = require('path');5 6const app = express();7const server = http.createServer(app);8const io = new Server(server, {9 cors: { origin: "*", methods: ["GET", "POST"] }10});11 12// Serve public files (disable auto index.html for /)13app.use(express.static(path.join(__dirname, 'public'), { index: false }));14 15// Root → landing page16app.get('/', (req, res) => {17 res.sendFile(path.join(__dirname, 'home.html'));18});19 20// Rooms: roomCode → { broadcasterId, viewers: [adminSocketId], deviceInfo, isStreaming }21const rooms = {};22 23io.on('connection', (socket) => {24 console.log(`[Socket] Connected: ${socket.id}`);25 26 // === CLIENT (Camera Device) ===27 28 // Client joins/creates a room29 socket.on('client-join-room', ({ roomCode, deviceName }) => {30 roomCode = roomCode.toUpperCase();31 32 // Check if room already has broadcaster33 if (rooms[roomCode] && rooms[roomCode].broadcasterId) {34 socket.emit('client-error', { message: `Room ${roomCode} sudah ada broadcaster` });35 return;36 }37 38 rooms[roomCode] = {39 broadcasterId: socket.id,40 viewers: [],41 deviceInfo: { name: deviceName || 'Camera', platform: 'browser' },42 isStreaming: false43 };44 45 socket.roomCode = roomCode;46 console.log(`[Room] Client ${socket.id} created room: ${roomCode} (${deviceName})`);47 48 // Broadcast room list to admins49 broadcastRoomList();50 51 socket.emit('client-room-joined', { roomCode });52 });53 54 // Client ready to stream (camera started)55 socket.on('client-ready', ({ roomCode }) => {56 if (rooms[roomCode]) {57 rooms[roomCode].isStreaming = true;58 broadcastRoomList();59 60 // Notify all admins in room that stream is ready61 rooms[roomCode].viewers.forEach(adminId => {62 io.to(adminId).emit('room-stream-ready', { roomCode });63 });64 }65 });66 67 // Client sends WebRTC offer to specific admin68 socket.on('client-offer', ({ roomCode, adminId, offer }) => {69 io.to(adminId).emit('webrtc-offer', { roomCode, fromClientId: socket.id, offer });70 });71 72 // Client sends ICE candidate to admin73 socket.on('client-ice', ({ roomCode, adminId, candidate }) => {74 io.to(adminId).emit('webrtc-ice-candidate', { roomCode, candidate });75 });76 77 // Client sends screenshot78 socket.on('client-screenshot', ({ roomCode, imageData }) => {79 if (rooms[roomCode]) {80 rooms[roomCode].viewers.forEach(adminId => {81 io.to(adminId).emit('new-screenshot', { roomCode, imageData });82 });83 }84 });85 86 // Client status update87 socket.on('client-status', ({ roomCode, isRecording }) => {88 if (rooms[roomCode]) {89 rooms[roomCode].viewers.forEach(adminId => {90 io.to(adminId).emit('stream-status', { roomCode, isRecording });91 });92 }93 });94 95 // === ADMIN (Monitoring Dashboard) ===96 97 // Admin joins room to monitor98 socket.on('admin-join-room', ({ roomCode }) => {99 roomCode = roomCode.toUpperCase();100 const room = rooms[roomCode];101 102 if (!room || !room.broadcasterId) {103 socket.emit('admin-error', { message: `Room ${roomCode} tidak ditemukan` });104 return;105 }106 107 if (room.viewers.includes(socket.id)) {108 socket.emit('admin-error', { message: 'Sudah ada di room ini' });109 return;110 }111 112 room.viewers.push(socket.id);113 socket.adminRoom = roomCode;114 115 console.log(`[Admin] ${socket.id} joined room ${roomCode}`);116 117 // Notify client that admin is watching118 io.to(room.broadcasterId).emit('admin-watching', { adminId: socket.id, roomCode });119 120 // Tell admin about current room state121 socket.emit('room-joined', {122 roomCode,123 deviceInfo: room.deviceInfo,124 isStreaming: room.isStreaming125 });126 127 broadcastRoomList();128 });129 130 // Admin stops monitoring room131 socket.on('admin-leave-room', ({ roomCode }) => {132 const room = rooms[roomCode];133 if (room) {134 room.viewers = room.viewers.filter(id => id !== socket.id);135 io.to(room.broadcasterId).emit('admin-left', { adminId: socket.id, roomCode });136 broadcastRoomList();137 }138 socket.adminRoom = null;139 });140 141 // Admin sends remote command to client142 socket.on('admin-command', ({ roomCode, command }) => {143 const room = rooms[roomCode];144 if (room && room.broadcasterId) {145 io.to(room.broadcasterId).emit('remote-command', { command });146 }147 });148 149 // Admin sends WebRTC answer to client150 socket.on('admin-answer', ({ roomCode, answer }) => {151 const room = rooms[roomCode];152 if (room && room.broadcasterId) {153 io.to(room.broadcasterId).emit('webrtc-answer', { roomCode, answer, adminId: socket.id });154 }155 });156 157 // Admin sends ICE candidate to client158 socket.on('admin-ice', ({ roomCode, candidate }) => {159 const room = rooms[roomCode];160 if (room && room.broadcasterId) {161 io.to(room.broadcasterId).emit('webrtc-ice-candidate', { roomCode, candidate, adminId: socket.id });162 }163 });164 165 // === Chat Messages ===166 167 // Client sends chat message168 socket.on('client-chat', ({ roomCode, message, deviceName }) => {169 const room = rooms[roomCode];170 if (room) {171 room.viewers.forEach(adminId => {172 io.to(adminId).emit('chat-message', { roomCode, from: deviceName, message, type: 'client', timestamp: Date.now() });173 });174 }175 });176 177 // Admin sends chat message178 socket.on('admin-chat', ({ roomCode, message }) => {179 const room = rooms[roomCode];180 if (room && room.broadcasterId) {181 io.to(room.broadcasterId).emit('chat-message', { roomCode, from: socket.id.slice(0, 8), message, type: 'admin', timestamp: Date.now() });182 }183 });184 185 // Disconnect186 socket.on('disconnect', () => {187 console.log(`[Socket] Disconnected: ${socket.id}`);188 189 // Handle client disconnect190 if (socket.roomCode && rooms[socket.roomCode]) {191 const room = rooms[socket.roomCode];192 // Notify all admins in room193 room.viewers.forEach(adminId => {194 io.to(adminId).emit('broadcaster-disconnected', { roomCode: socket.roomCode });195 });196 delete rooms[socket.roomCode];197 broadcastRoomList();198 }199 200 // Handle admin disconnect201 if (socket.adminRoom && rooms[socket.adminRoom]) {202 const room = rooms[socket.adminRoom];203 room.viewers = room.viewers.filter(id => id !== socket.id);204 if (room.broadcasterId) {205 io.to(room.broadcasterId).emit('admin-left', { adminId: socket.id, roomCode: socket.adminRoom });206 }207 broadcastRoomList();208 }209 });210});211 212function broadcastRoomList() {213 const roomList = Object.entries(rooms).map(([code, room]) => ({214 roomCode: code,215 deviceName: room.deviceInfo.name,216 viewerCount: room.viewers.length,217 isStreaming: room.isStreaming218 }));219 io.emit('room-list-updated', roomList);220}221 222const PORT = process.env.PORT || 3000;223server.listen(PORT, '0.0.0.0', () => {224 console.log(`🎬 Camera Monitoring Server on port ${PORT}`);225 console.log(` 🏠 Home: http://localhost:${PORT}/`);226 console.log(` 📷 Camera Device: http://localhost:${PORT}/index.html`);227 console.log(` 🎬 Admin Monitor: http://localhost:${PORT}/viewer.html`);228});229 