|
import os |
|
import time |
|
|
|
import streamlit as st |
|
from google import genai |
|
from google.genai.types import Tool, GenerateContentConfig, GoogleSearch |
|
import google.api_core |
|
|
|
|
|
st.set_page_config( |
|
page_title="AI Niche Content Idea Generator", |
|
page_icon="π‘", |
|
layout="centered", |
|
) |
|
|
|
|
|
PATREON_URL = "https://www.patreon.com/cineAI" |
|
|
|
|
|
FALLBACK_MODELS = [ |
|
"gemini-2.0-flash-thinking-exp-01-21", |
|
"gemini-2.5-flash-preview-04-17", |
|
"gemini-1.5-flash", |
|
"gemini-1.5-pro", |
|
"gemini-2.0-flash", |
|
"gemini-2.0-flash-lite", |
|
"gemini-2.5-pro-preview-03-25", |
|
] |
|
|
|
|
|
api_key = "" |
|
try: |
|
api_key = st.secrets["GOOGLE_API_KEY"] |
|
api_key_configured = True |
|
except (KeyError, FileNotFoundError): |
|
st.error("π¨ Google API Key not found! Please go to the settings, then find the `Variables and secrets` item and click `New secret`, insert your Google API key.") |
|
api_key_configured = False |
|
st.stop() |
|
|
|
|
|
def generate_content_ideas(niche, audience, content_format, num_ideas=4, advanced_mode=False): |
|
"""Generates content ideas using Google Generative AI, with fallback and optional search.""" |
|
if not api_key_configured: |
|
return "API Key not configured. Cannot proceed." |
|
|
|
|
|
base_prompt = f""" |
|
Act as an expert content strategist specializing in the niche: '{niche}'. |
|
Your task is to generate {num_ideas} fresh, specific, and engaging content ideas tailored for the following target audience: '{audience}'. |
|
The desired content format is: '{content_format}'. |
|
Focus on providing unique angles, addressing potential pain points or interests of the audience, and suggesting topics that are relevant within the niche. Avoid generic ideas. |
|
""" |
|
|
|
|
|
if advanced_mode: |
|
prompt = base_prompt + f""" |
|
**IMPORTANT: Leverage current information from the web using the available search tool** to identify *trending topics*, *recent developments*, or *very specific audience questions* within the '{niche}' niche related to the '{audience}' audience. Ground your ideas in potentially searchable, up-to-date information. |
|
""" |
|
else: |
|
prompt = base_prompt + """ |
|
Generate the ideas based on your general knowledge of the niche and audience. |
|
""" |
|
|
|
|
|
prompt += """ |
|
Please format the output as a numbered list. Each idea should be concise but descriptive. |
|
Example for niche 'Sustainable Gardening', audience 'Urban Beginners', format 'Blog Post Titles': |
|
1. 5 Easy-to-Grow Vegetables for Your Tiny Balcony Garden |
|
2. Composting in Apartments: A No-Smell Guide for City Dwellers |
|
3. Choosing the Right Recycled Pots for Your Indoor Plants |
|
4. How to Attract Pollinators to Your Urban Oasis |
|
5. Watering Wisely: Saving Water in Your Container Garden |
|
Now, generate the ideas for the requested niche, audience, and format: |
|
""" |
|
|
|
last_error = None |
|
last_error_message = "" |
|
|
|
|
|
if advanced_mode: |
|
google_search_tool = Tool(google_search=GoogleSearch()) |
|
config = GenerateContentConfig( |
|
tools=[google_search_tool], |
|
response_modalities=['TEXT'], |
|
|
|
max_output_tokens=4100, |
|
) |
|
else: |
|
config = None |
|
|
|
tool_status_msg = " (Advanced Mode with Google Search π)" if advanced_mode else "" |
|
|
|
for model_name in FALLBACK_MODELS: |
|
st.info(f"π§ Attempting generation with model: `{model_name}`{tool_status_msg}...") |
|
try: |
|
model = genai.Client(api_key=api_key) |
|
response = model.models.generate_content( |
|
model=model_name, |
|
contents=prompt, |
|
config=config, |
|
) |
|
|
|
if hasattr(response, 'text') and response.text: |
|
st.success(f"β
Successfully generated ideas using `{model_name}`!") |
|
return response.text |
|
elif hasattr(response, 'prompt_feedback') and response.prompt_feedback.block_reason: |
|
error_message = f"β οΈ Generation blocked by `{model_name}`. Reason: {response.prompt_feedback.block_reason.name}. Trying next model..." |
|
st.warning(error_message) |
|
last_error = BlockingIOError(f"{model_name}: Blocked - {response.prompt_feedback.block_reason.name}") |
|
last_error_message = error_message |
|
time.sleep(0.5) |
|
continue |
|
else: |
|
error_message = f"β οΈ Generation failed with `{model_name}` (empty or invalid response structure). Trying next model..." |
|
st.warning(error_message) |
|
last_error = ValueError(f"{model_name}: Empty/Invalid response") |
|
last_error_message = error_message |
|
time.sleep(1) |
|
continue |
|
|
|
|
|
except google.api_core.exceptions.ResourceExhausted as e: |
|
error_message = f"β³ Model `{model_name}` likely rate limited: {e}. Trying next model..." |
|
st.warning(error_message) |
|
last_error = e |
|
last_error_message = error_message |
|
time.sleep(3) |
|
continue |
|
except google.api_core.exceptions.ServiceUnavailable as e: |
|
error_message = f"βοΈ Model `{model_name}` unavailable: {e}. Trying next model..." |
|
st.warning(error_message) |
|
last_error = e |
|
last_error_message = error_message |
|
time.sleep(3) |
|
continue |
|
except (google.api_core.exceptions.InvalidArgument, ValueError) as e: |
|
err_str = str(e).lower() |
|
if "tool" in err_str or "function" in err_str or "retriever" in err_str: |
|
error_message = f"π§ Model `{model_name}` might not support the requested tool or configuration: {e}. Trying next model..." |
|
st.warning(error_message) |
|
else: |
|
error_message = f"π« Invalid argument for `{model_name}`: {e}. Trying next model..." |
|
st.warning(error_message) |
|
last_error = e |
|
last_error_message = error_message |
|
continue |
|
except google.api_core.exceptions.GoogleAPIError as e: |
|
error_message = f"π« API Error with `{model_name}`: {e}. Trying next model..." |
|
st.warning(error_message) |
|
last_error = e |
|
last_error_message = error_message |
|
continue |
|
|
|
except Exception as e: |
|
error_message = f"β Unexpected error with `{model_name}`: {type(e).__name__} - {e}. Trying next model..." |
|
st.warning(error_message) |
|
last_error = e |
|
last_error_message = error_message |
|
continue |
|
|
|
st.error("π₯ All attempts failed.") |
|
final_error_message = "β οΈ Failed to generate ideas after trying all configured models." |
|
if last_error: |
|
final_error_message += f"\nLast known issue: {last_error_message}" |
|
|
|
return final_error_message |
|
|
|
|
|
st.sidebar.title("Support This Tool") |
|
st.sidebar.markdown(f""" |
|
If you find this AI Content Idea Generator useful, please consider supporting its ongoing development and hosting costs. |
|
|
|
[**π Support on Patreon**]({PATREON_URL}) |
|
|
|
Your support helps keep this tool running and allows for future improvements! |
|
""") |
|
|
|
st.title("π AI Niche Content Idea Generator") |
|
st.markdown("Define your niche, audience, and format. Use **Advanced Mode** for potentially more current ideas!") |
|
st.divider() |
|
|
|
|
|
with st.form("content_idea_form"): |
|
st.subheader("Tell us about your content needs:") |
|
niche = st.text_input("π― Enter your Niche:", placeholder="e.g., Sustainable Home Decor, Retro PC Gaming") |
|
audience = st.text_area("π₯ Describe your Target Audience:", placeholder="e.g., Eco-conscious millennials, 90s Gamers", height=100) |
|
content_format_options = ["Blog Post Titles", "Social Media Captions", "YouTube Video Ideas", "Podcast Topics", "Newsletter Subjects", "TikTok Video Concepts", "LinkedIn Article Headlines"] |
|
content_format = st.selectbox("βοΈ Select Desired Content Format:", options=content_format_options) |
|
num_ideas_slider = st.slider("π’ Number of ideas:", min_value=3, max_value=15, value=4) |
|
advanced_mode_toggle = st.toggle("π Advanced Mode (Use Google Search)", value=False, help="May use Google Search for more current ideas. Works best with Gemini models.") |
|
submitted = st.form_submit_button("β¨ Generate Ideas!") |
|
|
|
|
|
if submitted: |
|
if not niche or not audience or not content_format: |
|
st.warning("β οΈ Please fill in all the fields.") |
|
elif api_key_configured: |
|
mode_msg = " (Advanced Mode)" if advanced_mode_toggle else "" |
|
with st.spinner(f"π§ Thinking up brilliant ideas{mode_msg}..."): |
|
generated_ideas = generate_content_ideas( |
|
niche, audience, content_format, num_ideas_slider, advanced_mode=advanced_mode_toggle |
|
) |
|
|
|
st.divider() |
|
st.subheader("π‘ Here are your content ideas:") |
|
if generated_ideas and not generated_ideas.startswith(("β οΈ", "π₯")): |
|
st.markdown(generated_ideas) |
|
st.success("Ideas generated successfully!") |
|
st.markdown("---") |
|
st.markdown(f"**Hope these ideas help!** If this tool saved you time or sparked creativity, consider [supporting the creator on Patreon]({PATREON_URL}).") |
|
elif generated_ideas: |
|
st.error(generated_ideas) |
|
else: |
|
st.error("An unexpected issue occurred. No ideas were generated or returned.") |
|
else: |
|
st.error("API Key is not configured.") |