Spaces:
Sleeping
Sleeping
| # app.py | |
| import streamlit as st | |
| import requests | |
| from PIL import Image | |
| from io import BytesIO | |
| from colorthief import ColorThief | |
| import random | |
| import os | |
| # -- API Keys from Hugging Face Secrets -- | |
| UNSPLASH_ACCESS_KEY = os.environ.get("UNSPLASH_ACCESS_KEY") | |
| GOOGLE_FONTS_API_KEY = os.environ.get("GOOGLE_FONTS_API_KEY") | |
| st.set_page_config(page_title="Moodboard AI", layout="wide") | |
| # -- Init Session State -- | |
| if 'dark_mode' not in st.session_state: | |
| st.session_state.dark_mode = False | |
| if 'generate' not in st.session_state: | |
| st.session_state.generate = False | |
| if 'remix' not in st.session_state: | |
| st.session_state.remix = random.randint(1, 10000) | |
| # -- Dark Mode Toggle (no forced rerun) -- | |
| dark_mode_toggle = st.toggle("π Toggle Dark Mode", value=st.session_state.dark_mode, key="dark_toggle") | |
| if dark_mode_toggle != st.session_state.dark_mode: | |
| st.session_state.dark_mode = dark_mode_toggle | |
| st.experimental_rerun() | |
| # -- Apply Dark Mode Styling -- | |
| if st.session_state.dark_mode: | |
| st.markdown(""" | |
| <style> | |
| body, .stApp { | |
| background-color: #121212; | |
| color: #f0f0f0; | |
| } | |
| h1, h2, h3, h4, h5, h6 { | |
| color: #ffffff !important; | |
| } | |
| .stTextInput > div > div > input, | |
| .stTextArea > div > textarea { | |
| background-color: #222222; | |
| color: #ffffff; | |
| } | |
| .stButton>button { | |
| background-color: #333333; | |
| color: #ffffff; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| else: | |
| st.markdown(""" | |
| <style> | |
| h1, h2, h3, h4, h5, h6 { | |
| color: #000000 !important; | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # -- Title -- | |
| st.title("π¨ Moodboard AI β Smart Visual Inspiration Generator") | |
| # -- API Status Display -- | |
| if UNSPLASH_ACCESS_KEY: | |
| st.success("β Unsplash API key loaded successfully.") | |
| else: | |
| st.warning("β οΈ Unsplash API key missing or not set in Hugging Face Secrets.") | |
| if GOOGLE_FONTS_API_KEY: | |
| st.success("β Google Fonts API key loaded successfully.") | |
| else: | |
| st.warning("β οΈ Google Fonts API key missing or not set in Hugging Face Secrets.") | |
| # -- Theme Input -- | |
| theme = st.text_input("Enter a theme or keyword (e.g. 'vintage tech', 'coastal minimalism'):", "futuristic tech").lower() | |
| # -- Control Buttons -- | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| if st.button("π Remix Moodboard"): | |
| st.session_state.remix = random.randint(1, 10000) | |
| st.session_state.generate = True | |
| with col2: | |
| if st.button("β¨ Generate Moodboard"): | |
| st.session_state.generate = True | |
| # -- Fetch Images from Unsplash -- | |
| def fetch_images_from_unsplash(query, count=5): | |
| if not UNSPLASH_ACCESS_KEY: | |
| st.error("π« Missing Unsplash API Key. Please add it to Hugging Face Secrets.") | |
| return [] | |
| try: | |
| url = f"https://api.unsplash.com/search/photos?query={query}&per_page={count}&client_id={UNSPLASH_ACCESS_KEY}" | |
| response = requests.get(url) | |
| if response.status_code != 200: | |
| st.error(f"β Unsplash API Error: {response.status_code}") | |
| return [] | |
| data = response.json() | |
| results = data.get('results', []) | |
| if not results: | |
| st.warning("β οΈ Unsplash API returned 0 results. Try a different keyword.") | |
| return [item['urls']['regular'] for item in results] | |
| except Exception as e: | |
| st.error(f"β Error fetching images: {e}") | |
| return [] | |
| # -- Extract Dominant Colors -- | |
| def extract_colors(image_urls): | |
| colors = [] | |
| for url in image_urls[:3]: | |
| try: | |
| response = requests.get(url) | |
| img = BytesIO(response.content) | |
| ct = ColorThief(img) | |
| palette = ct.get_palette(color_count=3) | |
| colors.extend(palette) | |
| except: | |
| continue | |
| return colors[:5] | |
| # -- Fetch Fonts from Google Fonts -- | |
| def fetch_google_fonts(): | |
| if not GOOGLE_FONTS_API_KEY: | |
| st.error("π« Missing Google Fonts API Key. Please add it to Hugging Face Secrets.") | |
| return [] | |
| try: | |
| url = f"https://www.googleapis.com/webfonts/v1/webfonts?sort=popularity&key={GOOGLE_FONTS_API_KEY}" | |
| res = requests.get(url) | |
| data = res.json() | |
| fonts = [item['family'] for item in data.get('items', [])] | |
| return random.sample(fonts, 3) | |
| except Exception as e: | |
| st.error(f"β Error fetching fonts: {e}") | |
| return [] | |
| # -- Show Moodboard -- | |
| if st.session_state.get("generate"): | |
| with st.spinner("Generating your moodboard..."): | |
| query = f"{theme} {st.session_state.remix}" | |
| image_urls = fetch_images_from_unsplash(query) | |
| if not image_urls: | |
| st.warning("No images found. Try another keyword or check your API key.") | |
| else: | |
| st.subheader("πΌοΈ Image Suggestions") | |
| cols = st.columns(len(image_urls)) | |
| for col, url in zip(cols, image_urls): | |
| col.image(url, use_container_width=True) | |
| st.subheader("π¨ Color Palette") | |
| palette = extract_colors(image_urls) | |
| color_cols = st.columns(len(palette)) | |
| for col, rgb in zip(color_cols, palette): | |
| hex_color = '#%02x%02x%02x' % rgb | |
| col.markdown( | |
| f"<div style='background-color:{hex_color};height:50px;width:100%;border-radius:5px'></div>", | |
| unsafe_allow_html=True | |
| ) | |
| col.write(hex_color) | |
| st.subheader("π€ Font Suggestions") | |
| fonts = fetch_google_fonts() | |
| for font in fonts: | |
| st.markdown( | |
| f"<p style='font-family:{font};font-size:24px;color:{'#ffffff' if st.session_state.dark_mode else '#000000'}'>{font}</p>", | |
| unsafe_allow_html=True | |
| ) | |
| st.success("β Moodboard ready! Right-click or screenshot to save.") | |
| st.session_state.generate = False # Reset |