2026-01-06 12:31:58 +00:00
|
|
|
import time
|
|
|
|
|
import requests
|
|
|
|
|
import streamlit as st
|
|
|
|
|
|
|
|
|
|
CAM_URLS = [
|
|
|
|
|
("Celje SD (DARS)", "https://kamere.dars.si/kamere/Baza_Konjice/Celje_SD.jpg"),
|
2026-01-06 18:46:46 +00:00
|
|
|
("Sp. Stranice (DRSC)", "https://www.drsc.si/kamere/kamslike/SpodnjeStranice/Sst1_0001.jpg"),
|
2026-01-06 12:31:58 +00:00
|
|
|
("Rogaška (DRSC)", "https://www.drsc.si/kamere/KamSlike/Rogaska/Slike/Rog1_0001.jpg"),
|
2026-01-06 18:56:24 +00:00
|
|
|
("Šentjupert", "https://www.drsc.si/kamere/kamslike/Sentrupert/slike/Str1_0001.jpg?"),
|
2026-01-06 19:06:12 +00:00
|
|
|
("Zima - smer CE", "https://kamere.dars.si/kamere/Baza_Konjice/Zima_smer_CE.jpg"),
|
|
|
|
|
("Celje-Center", "https://kamere.dars.si/kamere/drsi_cuvpkamere/Celje_Mariborska_priklju%C4%8Dek_na_AC_VN1.jpeg"),
|
2026-01-06 19:32:34 +00:00
|
|
|
("Celje - CityCenter", "https://kamere.dars.si/kamere/drsi_cuvpkamere/Celje_Mariborska_De%C4%8Dkova_Be%C5%BEigrajska_VN1.jpeg"),
|
|
|
|
|
("Vojnik-Arclin", "https://kamere.dars.si/kamere/drsi_cuvpkamere/Vojnik_Arclin_-_VN.jpeg")
|
2026-01-06 12:31:58 +00:00
|
|
|
]
|
|
|
|
|
|
|
|
|
|
st.set_page_config(page_title="Road Cameras", layout="wide")
|
|
|
|
|
st.title("Road Cameras")
|
|
|
|
|
|
|
|
|
|
refresh_s = st.sidebar.number_input(
|
|
|
|
|
"Refresh interval (seconds)",
|
|
|
|
|
min_value=0.5,
|
|
|
|
|
max_value=60.0,
|
|
|
|
|
value=10.0,
|
|
|
|
|
step=0.5
|
|
|
|
|
)
|
|
|
|
|
cols_n = st.sidebar.slider("Columns", 1, 4, 2)
|
|
|
|
|
auto = st.sidebar.toggle("Auto refresh", value=True)
|
|
|
|
|
|
|
|
|
|
def fetch_image_bytes(url: str) -> bytes:
|
|
|
|
|
cb_url = f"{url}?_ts={int(time.time() * 1000)}"
|
|
|
|
|
headers = {
|
|
|
|
|
"User-Agent": "Mozilla/5.0",
|
|
|
|
|
"Accept": "image/jpeg,image/*,*/*;q=0.8",
|
|
|
|
|
"Cache-Control": "no-cache",
|
|
|
|
|
"Pragma": "no-cache",
|
|
|
|
|
}
|
|
|
|
|
r = requests.get(cb_url, timeout=8, headers=headers)
|
|
|
|
|
r.raise_for_status()
|
|
|
|
|
return r.content
|
|
|
|
|
|
|
|
|
|
# Keep last good image in session_state, so 503 doesn't blank the UI
|
|
|
|
|
if "last_images" not in st.session_state:
|
|
|
|
|
st.session_state.last_images = {} # name -> bytes
|
|
|
|
|
|
|
|
|
|
def render_all():
|
|
|
|
|
cols = st.columns(cols_n)
|
|
|
|
|
for i, (name, url) in enumerate(CAM_URLS):
|
|
|
|
|
with cols[i % cols_n]:
|
|
|
|
|
st.subheader(name)
|
|
|
|
|
try:
|
|
|
|
|
img = fetch_image_bytes(url)
|
|
|
|
|
st.session_state.last_images[name] = img
|
|
|
|
|
st.image(img, use_container_width=True)
|
|
|
|
|
st.caption("✅ OK")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# show last good frame if available
|
|
|
|
|
last = st.session_state.last_images.get(name)
|
|
|
|
|
if last:
|
|
|
|
|
st.image(last, use_container_width=True)
|
|
|
|
|
st.caption(f"⚠️ Using last good frame ({e})")
|
|
|
|
|
else:
|
|
|
|
|
st.warning(f"No image yet ({e})")
|
|
|
|
|
|
|
|
|
|
render_all()
|
|
|
|
|
st.caption(f"Last update: {time.strftime('%Y-%m-%d %H:%M:%S')}")
|
|
|
|
|
|
|
|
|
|
if auto:
|
|
|
|
|
time.sleep(float(refresh_s))
|
|
|
|
|
st.rerun()
|