first commit
This commit is contained in:
+153
@@ -0,0 +1,153 @@
|
||||
import streamlit as st
|
||||
import httpx
|
||||
import time
|
||||
from pathlib import Path
|
||||
from utils import get_logger, get_host_ip
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
# Config - Use host IP so the browser can find the API
|
||||
HOST_IP = get_host_ip()
|
||||
API_URL = f"http://{HOST_IP}:8000"
|
||||
|
||||
st.set_page_config(
|
||||
page_title="Boys Streaming 📺",
|
||||
page_icon="📺",
|
||||
layout="wide"
|
||||
)
|
||||
|
||||
# Custom CSS for big buttons and clean look
|
||||
st.markdown("""
|
||||
<style>
|
||||
.stButton>button {
|
||||
width: 100%;
|
||||
height: 5em;
|
||||
font-size: 20px !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
.thumb-container {
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.thumb-selected {
|
||||
border: 5px solid #ff4b4b;
|
||||
}
|
||||
</style>
|
||||
""", unsafe_allow_html=True)
|
||||
|
||||
# Session State Initialization
|
||||
if 'selected_stories' not in st.session_state:
|
||||
st.session_state.selected_stories = []
|
||||
if 'library' not in st.session_state:
|
||||
st.session_state.library = None
|
||||
|
||||
def fetch_library():
|
||||
try:
|
||||
response = httpx.get(f"{API_URL}/library")
|
||||
if response.status_code == 200:
|
||||
st.session_state.library = response.json()
|
||||
else:
|
||||
st.error("Failed to fetch library from API.")
|
||||
except Exception as e:
|
||||
st.error(f"Could not connect to API: {e}")
|
||||
|
||||
def toggle_story(story_path):
|
||||
if story_path in st.session_state.selected_stories:
|
||||
st.session_state.selected_stories.remove(story_path)
|
||||
else:
|
||||
if len(st.session_state.selected_stories) < 3:
|
||||
st.session_state.selected_stories.append(story_path)
|
||||
else:
|
||||
st.warning("Only 3 stories allowed!")
|
||||
|
||||
def start_bedtime():
|
||||
if len(st.session_state.selected_stories) != 3:
|
||||
st.error("Please pick exactly 3 stories first!")
|
||||
return
|
||||
|
||||
# Auto-pick one music track
|
||||
if not st.session_state.library['music']:
|
||||
st.error("No music tracks found in library (> 2h)!")
|
||||
return
|
||||
|
||||
import random
|
||||
music_track = random.choice(st.session_state.library['music'])
|
||||
|
||||
playlist = st.session_state.selected_stories + [music_track['path']]
|
||||
|
||||
try:
|
||||
response = httpx.post(f"{API_URL}/cast", json={"video_paths": playlist})
|
||||
if response.status_code == 200:
|
||||
st.success("Bedtime ritual started! Casting to Chromecast...")
|
||||
else:
|
||||
st.error("Failed to start casting.")
|
||||
except Exception as e:
|
||||
st.error(f"Error starting bedtime: {e}")
|
||||
|
||||
# --- UI Layout ---
|
||||
st.title("📺 Boys Streaming - Bedtime!")
|
||||
|
||||
if st.button("🔄 Refresh Library"):
|
||||
fetch_library()
|
||||
|
||||
if not st.session_state.library:
|
||||
fetch_library()
|
||||
|
||||
if st.session_state.library:
|
||||
stories = st.session_state.library.get('stories', [])
|
||||
|
||||
st.subheader(f"📖 Pick 3 Stories ({len(st.session_state.selected_stories)}/3)")
|
||||
|
||||
# Display stories in a grid
|
||||
cols_per_row = 4
|
||||
for i in range(0, len(stories), cols_per_row):
|
||||
cols = st.columns(cols_per_row)
|
||||
for j, col in enumerate(cols):
|
||||
if i + j < len(stories):
|
||||
story = stories[i + j]
|
||||
is_selected = story['path'] in st.session_state.selected_stories
|
||||
|
||||
with col:
|
||||
# Use a unique key for each button
|
||||
thumb_url = f"{API_URL}/thumbs/{story['thumbnail']}"
|
||||
st.image(thumb_url, use_container_width=True)
|
||||
|
||||
label = f"✅ {story['filename']}" if is_selected else story['filename']
|
||||
if st.button(label, key=f"btn_{story['id']}"):
|
||||
toggle_story(story['path'])
|
||||
st.rerun()
|
||||
|
||||
st.divider()
|
||||
|
||||
# Action area
|
||||
col1, col2 = st.columns(2)
|
||||
with col1:
|
||||
if st.button("🚀 START BEDTIME", type="primary", use_container_width=True):
|
||||
start_bedtime()
|
||||
with col2:
|
||||
if st.button("🛑 STOP CASTING", use_container_width=True):
|
||||
httpx.post(f"{API_URL}/stop")
|
||||
st.warning("Casting stopped.")
|
||||
|
||||
# Status Monitor
|
||||
st.divider()
|
||||
status_placeholder = st.empty()
|
||||
|
||||
# Auto-refresh status
|
||||
try:
|
||||
status_resp = httpx.get(f"{API_URL}/status")
|
||||
if status_resp.status_code == 200:
|
||||
status = status_resp.json()
|
||||
if status.get('is_playing'):
|
||||
status_placeholder.info(f"Currently Playing item index: {status.get('current_index', 0) + 1} of {status.get('queue_len', 0)}")
|
||||
else:
|
||||
status_placeholder.text("Chromecast is idle.")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
st.info("Waiting for library scan...")
|
||||
time.sleep(2)
|
||||
st.rerun()
|
||||
Reference in New Issue
Block a user