|
import streamlit as st |
|
import pandas as pd |
|
from datetime import datetime |
|
import os |
|
import base64 |
|
from io import BytesIO |
|
from PIL import Image |
|
|
|
|
|
if 'file_history' not in st.session_state: |
|
st.session_state['file_history'] = [] |
|
|
|
|
|
def save_to_history(file_type, file_path, img_data): |
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") |
|
with open(file_path, "wb") as f: |
|
f.write(base64.b64decode(img_data.split(',')[1])) |
|
st.session_state['file_history'].append({ |
|
"Timestamp": timestamp, |
|
"Type": file_type, |
|
"Path": file_path |
|
}) |
|
|
|
|
|
with st.sidebar: |
|
st.header("ποΈπΈ Snap Shack") |
|
st.subheader("π Snap Stash") |
|
if st.session_state['file_history']: |
|
images = [f for f in st.session_state['file_history'] if f['Type'] == "πΌοΈ Image"] |
|
if images: |
|
st.write("πΌοΈ Images") |
|
for img in images: |
|
st.write(f"- {img['Path']} @ {img['Timestamp']}") |
|
else: |
|
st.write("π³οΈ Empty Stash!") |
|
|
|
|
|
st.title("πΈ Live Camera Snap Craze") |
|
|
|
|
|
st.header("πΈπ₯ Live Snap Zone") |
|
live_camera_html = """ |
|
<div> |
|
<select id="cameraSelect"></select> |
|
<button id="startBtn" onclick="toggleStream()">π· Start Live</button> |
|
<button onclick="takeSnapshot()">πΈ Snap Now</button> |
|
<video id="video" autoplay playsinline style="width:100%;"></video> |
|
<canvas id="canvas" style="display:none;"></canvas> |
|
</div> |
|
<script> |
|
let stream = null; |
|
const video = document.getElementById('video'); |
|
const canvas = document.getElementById('canvas'); |
|
let autoCaptureInterval = null; |
|
let isStreaming = false; |
|
|
|
// πΉπ Enumerate cameras like a tech detective! |
|
async function enumerateCameras() { |
|
const devices = await navigator.mediaDevices.enumerateDevices(); |
|
const videoDevices = devices.filter(device => device.kind === 'videoinput'); |
|
const select = document.getElementById('cameraSelect'); |
|
select.innerHTML = ''; |
|
videoDevices.forEach((device, index) => { |
|
const option = document.createElement('option'); |
|
option.value = device.deviceId; |
|
option.text = device.label || `Camera ${index}`; |
|
select.appendChild(option); |
|
}); |
|
if (videoDevices.length === 0) { |
|
select.innerHTML = '<option>No cameras found</option>'; |
|
} |
|
} |
|
|
|
// πΈπ₯ Toggle live stream like a broadcast pro! |
|
async function toggleStream() { |
|
const startBtn = document.getElementById('startBtn'); |
|
if (isStreaming) { |
|
stopStream(); |
|
startBtn.textContent = 'π· Start Live'; |
|
isStreaming = false; |
|
} else { |
|
const cameraId = document.getElementById('cameraSelect').value; |
|
if (!cameraId) { |
|
alert('No camera selected or available!'); |
|
return; |
|
} |
|
const constraints = { video: { deviceId: { exact: cameraId } } }; |
|
try { |
|
stream = await navigator.mediaDevices.getUserMedia(constraints); |
|
video.srcObject = stream; |
|
startBtn.textContent = 'βΉοΈ Stop Live'; |
|
isStreaming = true; |
|
autoCaptureInterval = setInterval(takeSnapshot, 10000); // Auto-save every 10s |
|
} catch (e) { |
|
alert('Failed to start camera: ' + e.message); |
|
} |
|
} |
|
} |
|
|
|
// πΈβοΈ Snap a pic like a stealthy paparazzi! |
|
function takeSnapshot() { |
|
if (!stream) return; |
|
canvas.width = video.videoWidth; |
|
canvas.height = video.videoHeight; |
|
canvas.getContext('2d').drawImage(video, 0, 0); |
|
const dataUrl = canvas.toDataURL('image/jpeg'); |
|
window.parent.postMessage({ type: 'snapshot', data: dataUrl }, '*'); |
|
} |
|
|
|
// βΉοΈπ΄ Stop the stream like a broadcast kill switch! |
|
function stopStream() { |
|
if (stream) { |
|
stream.getTracks().forEach(track => track.stop()); |
|
stream = null; |
|
video.srcObject = null; |
|
clearInterval(autoCaptureInterval); |
|
} |
|
} |
|
|
|
// π¬ Kick off the camera party! |
|
enumerateCameras(); |
|
</script> |
|
""" |
|
st.markdown(live_camera_html, unsafe_allow_html=True) |
|
|
|
|
|
def handle_snapshot(): |
|
if "snapshot" in st.session_state: |
|
snapshot_data = st.session_state["snapshot"] |
|
filename = f"snap_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg" |
|
save_to_history("πΌοΈ Image", filename, snapshot_data) |
|
st.image(Image.open(BytesIO(base64.b64decode(snapshot_data.split(',')[1]))), caption="Latest Snap", use_container_width=True) |
|
del st.session_state["snapshot"] |
|
|
|
st.components.v1.html( |
|
""" |
|
<script> |
|
window.addEventListener('message', function(event) { |
|
if (event.data.type === 'snapshot') { |
|
window.streamlitAPI.setComponentValue({snapshot: event.data.data}); |
|
} |
|
}); |
|
</script> |
|
""", |
|
height=0 |
|
) |
|
handle_snapshot() |
|
|
|
|
|
st.header("π₯π Drop Zone") |
|
uploaded_files = st.file_uploader("πΈ Toss Pics", accept_multiple_files=True, type=['jpg', 'png']) |
|
if uploaded_files: |
|
for uploaded_file in uploaded_files: |
|
file_path = f"uploaded_{uploaded_file.name}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg" |
|
save_to_history("πΌοΈ Image", file_path, base64.b64encode(uploaded_file.getvalue()).decode()) |
|
st.image(Image.open(uploaded_file), caption=uploaded_file.name, use_container_width=True) |
|
|
|
|
|
st.header("πͺ Snap Show") |
|
if st.session_state['file_history']: |
|
images = [f for f in st.session_state['file_history'] if f['Type'] == "πΌοΈ Image"] |
|
if images: |
|
st.subheader("πΌοΈ Pic Parade") |
|
cols = st.columns(3) |
|
for i, img in enumerate(images): |
|
with cols[i % 3]: |
|
if os.path.exists(img['Path']): |
|
st.image(img['Path'], caption=img['Path'], use_container_width=True) |
|
with open(img['Path'], "rb") as f: |
|
img_data = f.read() |
|
st.markdown(f'<a href="data:image/jpeg;base64,{base64.b64encode(img_data).decode()}" download="{os.path.basename(img["Path"])}">π₯ Snag It!</a>', unsafe_allow_html=True) |
|
else: |
|
st.warning(f"π¨ Missing file: {img['Path']}") |
|
else: |
|
st.write("π« No snaps yet!") |
|
|
|
|
|
st.header("β³ Snap Saga") |
|
if st.session_state['file_history']: |
|
df = pd.DataFrame(st.session_state['file_history']) |
|
st.dataframe(df) |
|
else: |
|
st.write("π³οΈ Nothing snapped yet!") |