Spaces:
Sleeping
Sleeping
File size: 10,436 Bytes
f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 a6912c3 f06f058 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# Main Streamlit application file
import streamlit as st
from dotenv import load_dotenv
import os
import google.generativeai as genai
from PIL import Image
import io
import json
import logging
# Import helper functions and prompts
from utils import (
configure_gemini,
analyze_input_with_gemini,
resize_image,
MAX_IMAGE_DIMENSION,
MAX_IMAGE_MB
)
from prompts import DETAILED_DISCOVER_PROMPT
# Configure logging
logging.basicConfig(level=logging.INFO)
# --- Page Configuration ---
st.set_page_config(
page_title="Google Discover Optimizer",
page_icon="π°",
layout="wide",
)
# --- Load API Key ---
# Load local .env file if it exists
load_dotenv()
# Try to get API key from Streamlit secrets first, then environment variables
# On Hugging Face Spaces, secrets should be set in the Space settings.
api_key = st.secrets.get("GOOGLE_API_KEY") or os.getenv("GOOGLE_API_KEY")
# Configure Gemini API
gemini_model = configure_gemini(api_key)
# --- App Header ---
st.title("π° Google Discover Content Analyzer")
st.caption("Analyze news article screenshots or text to optimize for Google Discover visibility.")
# --- Input Selection --- #
input_type = st.radio(
"Select Input Type:",
('Screenshot Upload', 'Paste Text'),
horizontal=True,
key='input_type'
)
# --- Input Fields --- #
uploaded_file = None
article_text = ""
if input_type == 'Screenshot Upload':
uploaded_file = st.file_uploader(
"Upload Screenshot",
type=["png", "jpg", "jpeg", "webp"],
help=f"Upload a screenshot of the news article. Images will be resized to max {MAX_IMAGE_DIMENSION}px and compressed (target < {MAX_IMAGE_MB}MB)."
)
else:
article_text = st.text_area(
"Paste Article Text",
height=300,
placeholder="Paste the full text of the news article here...",
max_chars=15000, # Consistent with previous limit
help="Maximum ~2000-2500 words (15000 characters)."
)
# --- Analysis Button and Logic --- #
analyze_button = st.button("Analyze Content", type="primary")
# Initialize session state variables if they don't exist
if 'analysis_result' not in st.session_state:
st.session_state.analysis_result = None
if 'error_message' not in st.session_state:
st.session_state.error_message = None
if 'processed_image_bytes' not in st.session_state:
st.session_state.processed_image_bytes = None
if analyze_button:
st.session_state.analysis_result = None # Clear previous results
st.session_state.error_message = None
st.session_state.processed_image_bytes = None
image_bytes_for_analysis = None
input_provided = False
# --- Input Processing --- #
if input_type == 'Screenshot Upload' and uploaded_file is not None:
input_provided = True
try:
with st.spinner(f'Processing uploaded image ({uploaded_file.name})... Compress/Resize...'):
img_bytes = uploaded_file.getvalue()
logging.info(f"Original image size: {len(img_bytes) / (1024 * 1024):.2f} MB")
# Resize and compress the image using the helper function
processed_image_bytes, final_format = resize_image(img_bytes)
image_bytes_for_analysis = processed_image_bytes # Use processed for AI
st.session_state.processed_image_bytes = processed_image_bytes # Store for display
logging.info(f"Processed image size: {len(processed_image_bytes) / (1024 * 1024):.2f} MB, Format: {final_format}")
except Exception as e:
logging.error(f"Error processing image: {e}", exc_info=True)
st.session_state.error_message = f"Error processing image: {e}"
st.error(st.session_state.error_message)
elif input_type == 'Paste Text' and article_text.strip():
input_provided = True
logging.info(f"Processing text input (length: {len(article_text)} chars)")
# Text input doesn't need pre-processing here
else:
st.warning("Please provide input (upload a screenshot or paste text) before analyzing.")
# --- Gemini API Call --- #
if input_provided and not st.session_state.error_message:
if not gemini_model:
st.session_state.error_message = "Gemini API Key not configured. Please set the GOOGLE_API_KEY secret in your Space settings."
st.error(st.session_state.error_message)
else:
with st.spinner('Analyzing content with Gemini AI... This may take a moment...β³'):
try:
analysis_result = analyze_input_with_gemini(
gemini_model=gemini_model,
prompt=DETAILED_DISCOVER_PROMPT,
image_bytes=image_bytes_for_analysis,
text_content=article_text if input_type == 'Paste Text' else None
)
st.session_state.analysis_result = analysis_result
st.success("Analysis complete! β¨")
except Exception as e:
logging.error(f"Error during Gemini analysis: {e}", exc_info=True)
st.session_state.error_message = f"Analysis failed: {e}"
st.error(st.session_state.error_message)
# --- Display Results --- #
if st.session_state.analysis_result:
result = st.session_state.analysis_result
logging.debug(f"Displaying results: {type(result)}")
st.divider()
st.header("π Analysis Results")
# Layout columns for image and score/details
col1, col2 = st.columns([1, 2]) # Adjust ratio as needed
with col1:
# Display image
if st.session_state.processed_image_bytes:
st.image(st.session_state.processed_image_bytes, caption="Processed Screenshot", use_column_width=True)
elif input_type == 'Paste Text':
st.info("Analysis based on text input.")
st.markdown(" ", unsafe_allow_html=True)
with col2:
# --- Display Error if present --- #
# Check if the result dictionary contains our specific error keys
is_error_result = isinstance(result, dict) and ('analysis_error' in result or 'raw_text' in result)
if is_error_result:
st.error(f"**Analysis Error:** {result.get('analysis_error', 'Unknown error')}")
raw_text = result.get('raw_text')
if raw_text:
with st.expander("Show Raw Gemini Output (for debugging)", expanded=False):
st.text(raw_text)
# Stop further rendering of normal results if there was an error
st.stop()
# --- Display Normal Results (if no error detected above) --- #
# Display Score prominently
if isinstance(result, dict) and 'google_discover_score' in result:
score_data = result.get('google_discover_score', {})
score = score_data.get('score')
explanation = score_data.get('explanation')
pos_factors = score_data.get('key_positive_factors', [])
neg_factors = score_data.get('key_negative_factors', [])
if score is not None:
try:
score_float = float(score)
st.metric(label="Estimated Google Discover Score", value=f"{score_float:.2f} / 1.00")
except (ValueError, TypeError):
st.metric(label="Estimated Google Discover Score", value=f"{score}")
logging.warning(f"Could not convert score '{score}' to float for formatting.")
if explanation:
st.caption(explanation)
if pos_factors:
st.success(f"π Key Strengths: {'; '.join(pos_factors)}")
if neg_factors:
st.warning(f"π Key Weaknesses: {'; '.join(neg_factors)}")
else:
st.warning("Score could not be calculated or found in the result.")
# Display Detailed Analysis Section
if isinstance(result, dict):
st.divider()
st.subheader("Detailed Analysis")
tab_keys = [k for k in result.keys() if k not in ['google_discover_score', 'input_type', 'analysis_error', 'raw_text']]
valid_tabs = {key: result.get(key) for key in tab_keys if result.get(key)}
tab_titles = [key.replace('_',' ').title() for key in valid_tabs.keys()]
if tab_titles:
tabs = st.tabs(tab_titles)
for i, key in enumerate(valid_tabs.keys()):
with tabs[i]:
section_data = valid_tabs[key]
if key == 'optimization_recommendations' and isinstance(section_data, list):
st.dataframe(section_data, use_container_width=True)
elif isinstance(section_data, (dict, list)):
st.json(section_data, expanded=True)
else:
st.write(section_data)
else:
st.info("No detailed analysis sections found.")
# Download Button
st.divider()
try:
# Make sure to dump the *original* result, not one potentially modified by error display
json_string = json.dumps(st.session_state.analysis_result, indent=2, ensure_ascii=False)
st.download_button(
label="Download Full Report (JSON)",
data=json_string,
file_name="discover_analysis_report.json",
mime="application/json",
)
except Exception as e:
st.warning(f"Could not generate JSON download: {e}")
# This case is now handled by the error check at the start of col2
# elif result:
# st.warning("Analysis did not return structured data. Displaying raw output:")
# st.text(str(result))
# Handle case where analysis button wasn't clicked, but an error exists from previous run
elif st.session_state.error_message:
st.error(st.session_state.error_message)
# Display initial warning if API key is missing
if not api_key:
st.warning("β οΈ Google API Key not found. Please set the `GOOGLE_API_KEY` secret in your Hugging Face Space settings for the analysis to work.", icon="π¨") |