Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -3,6 +3,8 @@ import pandas as pd
|
|
| 3 |
from datetime import datetime
|
| 4 |
import os
|
| 5 |
import base64
|
|
|
|
|
|
|
| 6 |
|
| 7 |
# ππ₯ Initialize session state like a galactic DJ spinning tracks!
|
| 8 |
if 'file_history' not in st.session_state:
|
|
@@ -33,79 +35,101 @@ with st.sidebar:
|
|
| 33 |
st.write("π³οΈ Empty Stash!")
|
| 34 |
|
| 35 |
# ππ¨ Main UI kicks off like a cosmic art show!
|
| 36 |
-
st.title("πΈ
|
| 37 |
|
| 38 |
-
# πΈπ· JS camera zone
|
| 39 |
-
st.header("πΈπ₯ Snap Zone")
|
| 40 |
-
|
| 41 |
-
<div
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
<script>
|
| 43 |
-
let
|
| 44 |
-
const
|
| 45 |
-
|
|
|
|
|
|
|
| 46 |
|
| 47 |
// πΉπ Enumerate cameras like a tech detective!
|
| 48 |
-
async function
|
| 49 |
const devices = await navigator.mediaDevices.enumerateDevices();
|
| 50 |
const videoDevices = devices.filter(device => device.kind === 'videoinput');
|
|
|
|
|
|
|
| 51 |
videoDevices.forEach((device, index) => {
|
| 52 |
-
const
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
<canvas id="canvas${index}" style="display:none;"></canvas>
|
| 57 |
-
<button onclick="takeSnapshot(${index})">πΈ Snap Cam ${index}</button>
|
| 58 |
-
`;
|
| 59 |
-
container.appendChild(div);
|
| 60 |
-
startStream(device.deviceId, index);
|
| 61 |
});
|
|
|
|
|
|
|
|
|
|
| 62 |
}
|
| 63 |
|
| 64 |
-
// πΈπ₯
|
| 65 |
-
async function
|
| 66 |
-
const
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
}
|
| 74 |
|
| 75 |
// πΈβοΈ Snap a pic like a stealthy paparazzi!
|
| 76 |
-
function takeSnapshot(
|
| 77 |
-
|
| 78 |
-
const canvas = document.getElementById(`canvas${index}`);
|
| 79 |
canvas.width = video.videoWidth;
|
| 80 |
canvas.height = video.videoHeight;
|
| 81 |
canvas.getContext('2d').drawImage(video, 0, 0);
|
| 82 |
const dataUrl = canvas.toDataURL('image/jpeg');
|
| 83 |
-
window.parent.postMessage({ type: 'snapshot', data: dataUrl
|
| 84 |
}
|
| 85 |
|
| 86 |
-
// βΉοΈπ΄ Stop
|
| 87 |
-
function
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
|
|
|
|
|
|
| 92 |
}
|
| 93 |
|
| 94 |
// π¬ Kick off the camera party!
|
| 95 |
-
|
| 96 |
-
window.onunload = stopStreams;
|
| 97 |
</script>
|
| 98 |
"""
|
| 99 |
-
st.markdown(
|
| 100 |
|
| 101 |
# πΈπ₯ Handle snapshots from JS
|
| 102 |
def handle_snapshot():
|
| 103 |
if "snapshot" in st.session_state:
|
| 104 |
snapshot_data = st.session_state["snapshot"]
|
| 105 |
-
|
| 106 |
-
filename = f"cam{cam_index}_snap_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
| 107 |
save_to_history("πΌοΈ Image", filename, snapshot_data)
|
| 108 |
-
st.image(Image.open(BytesIO(base64.b64decode(snapshot_data.split(',')[1]))), caption=
|
| 109 |
del st.session_state["snapshot"]
|
| 110 |
|
| 111 |
st.components.v1.html(
|
|
@@ -113,7 +137,7 @@ st.components.v1.html(
|
|
| 113 |
<script>
|
| 114 |
window.addEventListener('message', function(event) {
|
| 115 |
if (event.data.type === 'snapshot') {
|
| 116 |
-
window.streamlitAPI.setComponentValue({snapshot: event.data.data
|
| 117 |
}
|
| 118 |
});
|
| 119 |
</script>
|
|
|
|
| 3 |
from datetime import datetime
|
| 4 |
import os
|
| 5 |
import base64
|
| 6 |
+
from io import BytesIO
|
| 7 |
+
from PIL import Image
|
| 8 |
|
| 9 |
# ππ₯ Initialize session state like a galactic DJ spinning tracks!
|
| 10 |
if 'file_history' not in st.session_state:
|
|
|
|
| 35 |
st.write("π³οΈ Empty Stash!")
|
| 36 |
|
| 37 |
# ππ¨ Main UI kicks off like a cosmic art show!
|
| 38 |
+
st.title("πΈ Live Camera Snap Craze")
|
| 39 |
|
| 40 |
+
# πΈπ· JS live camera zone!
|
| 41 |
+
st.header("πΈπ₯ Live Snap Zone")
|
| 42 |
+
live_camera_html = """
|
| 43 |
+
<div>
|
| 44 |
+
<select id="cameraSelect"></select>
|
| 45 |
+
<button id="startBtn" onclick="toggleStream()">π· Start Live</button>
|
| 46 |
+
<button onclick="takeSnapshot()">πΈ Snap Now</button>
|
| 47 |
+
<video id="video" autoplay playsinline style="width:100%;"></video>
|
| 48 |
+
<canvas id="canvas" style="display:none;"></canvas>
|
| 49 |
+
</div>
|
| 50 |
<script>
|
| 51 |
+
let stream = null;
|
| 52 |
+
const video = document.getElementById('video');
|
| 53 |
+
const canvas = document.getElementById('canvas');
|
| 54 |
+
let autoCaptureInterval = null;
|
| 55 |
+
let isStreaming = false;
|
| 56 |
|
| 57 |
// πΉπ Enumerate cameras like a tech detective!
|
| 58 |
+
async function enumerateCameras() {
|
| 59 |
const devices = await navigator.mediaDevices.enumerateDevices();
|
| 60 |
const videoDevices = devices.filter(device => device.kind === 'videoinput');
|
| 61 |
+
const select = document.getElementById('cameraSelect');
|
| 62 |
+
select.innerHTML = '';
|
| 63 |
videoDevices.forEach((device, index) => {
|
| 64 |
+
const option = document.createElement('option');
|
| 65 |
+
option.value = device.deviceId;
|
| 66 |
+
option.text = device.label || `Camera ${index}`;
|
| 67 |
+
select.appendChild(option);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
});
|
| 69 |
+
if (videoDevices.length === 0) {
|
| 70 |
+
select.innerHTML = '<option>No cameras found</option>';
|
| 71 |
+
}
|
| 72 |
}
|
| 73 |
|
| 74 |
+
// πΈπ₯ Toggle live stream like a broadcast pro!
|
| 75 |
+
async function toggleStream() {
|
| 76 |
+
const startBtn = document.getElementById('startBtn');
|
| 77 |
+
if (isStreaming) {
|
| 78 |
+
stopStream();
|
| 79 |
+
startBtn.textContent = 'π· Start Live';
|
| 80 |
+
isStreaming = false;
|
| 81 |
+
} else {
|
| 82 |
+
const cameraId = document.getElementById('cameraSelect').value;
|
| 83 |
+
if (!cameraId) {
|
| 84 |
+
alert('No camera selected or available!');
|
| 85 |
+
return;
|
| 86 |
+
}
|
| 87 |
+
const constraints = { video: { deviceId: { exact: cameraId } } };
|
| 88 |
+
try {
|
| 89 |
+
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
| 90 |
+
video.srcObject = stream;
|
| 91 |
+
startBtn.textContent = 'βΉοΈ Stop Live';
|
| 92 |
+
isStreaming = true;
|
| 93 |
+
autoCaptureInterval = setInterval(takeSnapshot, 10000); // Auto-save every 10s
|
| 94 |
+
} catch (e) {
|
| 95 |
+
alert('Failed to start camera: ' + e.message);
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
}
|
| 99 |
|
| 100 |
// πΈβοΈ Snap a pic like a stealthy paparazzi!
|
| 101 |
+
function takeSnapshot() {
|
| 102 |
+
if (!stream) return;
|
|
|
|
| 103 |
canvas.width = video.videoWidth;
|
| 104 |
canvas.height = video.videoHeight;
|
| 105 |
canvas.getContext('2d').drawImage(video, 0, 0);
|
| 106 |
const dataUrl = canvas.toDataURL('image/jpeg');
|
| 107 |
+
window.parent.postMessage({ type: 'snapshot', data: dataUrl }, '*');
|
| 108 |
}
|
| 109 |
|
| 110 |
+
// βΉοΈπ΄ Stop the stream like a broadcast kill switch!
|
| 111 |
+
function stopStream() {
|
| 112 |
+
if (stream) {
|
| 113 |
+
stream.getTracks().forEach(track => track.stop());
|
| 114 |
+
stream = null;
|
| 115 |
+
video.srcObject = null;
|
| 116 |
+
clearInterval(autoCaptureInterval);
|
| 117 |
+
}
|
| 118 |
}
|
| 119 |
|
| 120 |
// π¬ Kick off the camera party!
|
| 121 |
+
enumerateCameras();
|
|
|
|
| 122 |
</script>
|
| 123 |
"""
|
| 124 |
+
st.markdown(live_camera_html, unsafe_allow_html=True)
|
| 125 |
|
| 126 |
# πΈπ₯ Handle snapshots from JS
|
| 127 |
def handle_snapshot():
|
| 128 |
if "snapshot" in st.session_state:
|
| 129 |
snapshot_data = st.session_state["snapshot"]
|
| 130 |
+
filename = f"snap_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg"
|
|
|
|
| 131 |
save_to_history("πΌοΈ Image", filename, snapshot_data)
|
| 132 |
+
st.image(Image.open(BytesIO(base64.b64decode(snapshot_data.split(',')[1]))), caption="Latest Snap", use_container_width=True)
|
| 133 |
del st.session_state["snapshot"]
|
| 134 |
|
| 135 |
st.components.v1.html(
|
|
|
|
| 137 |
<script>
|
| 138 |
window.addEventListener('message', function(event) {
|
| 139 |
if (event.data.type === 'snapshot') {
|
| 140 |
+
window.streamlitAPI.setComponentValue({snapshot: event.data.data});
|
| 141 |
}
|
| 142 |
});
|
| 143 |
</script>
|