File size: 6,113 Bytes
c001f24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
<div class="card bg-dark text-white mb-3 border-secondary" id="camera-receiver-component" style="display: none;">
    <div class="card-header d-flex justify-content-between align-items-center">
        <h5 class="mb-0"><i class="bi bi-camera-video me-2"></i>Remote Camera</h5>
        <button type="button" class="btn-close btn-close-white" aria-label="Close" onclick="toggleCameraReceiver()"></button>
    </div>
    <div class="card-body text-center">
        <div class="mb-3">
            <div class="d-inline-block p-2 bg-white rounded mb-2">
                <!-- QR Code Placeholder - In real app, generate this dynamically -->
                <img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data={{ request.url_root }}camera_mobile" alt="Scan to connect mobile camera">
            </div>
            <p class="small text-muted mb-0">Scan with your phone or open <code>/camera_mobile</code></p>
        </div>
        
        <div class="position-relative d-inline-block w-100" style="max-width: 640px; min-height: 240px; background: #000; border-radius: 8px; overflow: hidden;">
            <video id="webcamFeed" autoplay playsinline style="width: 100%; height: 100%; object-fit: contain;"></video>
            <div id="cam-status" class="position-absolute top-50 start-50 translate-middle text-white bg-dark bg-opacity-75 p-2 rounded">
                Waiting for connection...
            </div>
        </div>

        <div class="mt-3">
            <button type="button" class="btn btn-success btn-lg" id="captureBtn" disabled>
                <i class="bi bi-camera me-2"></i>Capture & Add
            </button>
        </div>
    </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.2/socket.io.min.js"></script>
<script>
    let socket;
    let peerConnection;
    let receiverStream;
    const videoEl = document.getElementById('webcamFeed');
    const captureBtn = document.getElementById('captureBtn');
    const statusEl = document.getElementById('cam-status');
    const room = 'stream_room';
    const config = { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] };

    function initCameraReceiver() {
        if (socket) return; // Already initialized

        socket = io();

        socket.on('connect', () => {
            console.log("Receiver connected");
            socket.emit('join', { room: room });
            statusEl.textContent = "Waiting for mobile camera...";
        });

        socket.on('offer', async (offer) => {
            console.log("Received offer");
            statusEl.textContent = "Connecting...";
            
            if (peerConnection) peerConnection.close();
            peerConnection = new RTCPeerConnection(config);

            peerConnection.onicecandidate = (event) => {
                if (event.candidate) socket.emit('candidate', { candidate: event.candidate, room: room });
            };

            peerConnection.ontrack = (event) => {
                console.log("Received track");
                receiverStream = event.streams[0];
                videoEl.srcObject = receiverStream;
                statusEl.style.display = 'none';
                captureBtn.disabled = false;
            };

            await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
            const answer = await peerConnection.createAnswer();
            await peerConnection.setLocalDescription(answer);
            socket.emit('answer', { answer: answer, room: room });
        });

        socket.on('candidate', async (candidate) => {
            if (peerConnection) {
                try { await peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); }
                catch (e) { console.error(e); }
            }
        });

        socket.on('trigger_capture', () => {
            console.log("Remote capture triggered");
            if (!captureBtn.disabled) {
                captureBtn.click();
            }
        });
    }

    // Capture Logic
    captureBtn.addEventListener('click', () => {
        if (!receiverStream) return;
        
        const canvas = document.createElement('canvas');
        canvas.width = videoEl.videoWidth;
        canvas.height = videoEl.videoHeight;
        canvas.getContext('2d').drawImage(videoEl, 0, 0);
        
        canvas.toBlob(blob => {
            const file = new File([blob], `capture_${Date.now()}.png`, { type: 'image/png' });
            
            // Add to the main upload form's FileList logic
            // Since we can't programmatically modify file input value directly to add files easily without DataTransfer,
            // we will handle this by creating a "virtual" file list or uploading immediately.
            // Strategy: Upload immediately to a temporary staging area or just use the upload_images route directly for single file?
            // The user wants to "reuse this component".
            // Let's assume we just append it to a global list or upload it.
            
            // We'll hook into the existing file processing logic of the parent page if possible.
            // Or simpler: Add to a DataTransfer object and update the file input.
            
            addFileToInput(file);
            
            // Visual feedback
            const originalText = captureBtn.innerHTML;
            captureBtn.innerHTML = '<i class="bi bi-check-lg"></i> Added';
            setTimeout(() => captureBtn.innerHTML = originalText, 1000);
        }, 'image/png');
    });

    // Helper to add file to the main input
    function addFileToInput(file) {
        const input = document.getElementById('images-upload');
        const dt = new DataTransfer();
        
        // Add existing files
        if (input.files) {
            for (let i = 0; i < input.files.length; i++) {
                dt.items.add(input.files[i]);
            }
        }
        
        // Add new file
        dt.items.add(file);
        input.files = dt.files;
        
        // Trigger change event to update UI
        input.dispatchEvent(new Event('change'));
    }
</script>