Files
ALwrity/ToBeMigrated/ai_writers/youtube_writers/modules/thumbnail_generator.py
2025-08-06 16:29:49 +05:30

622 lines
28 KiB
Python

"""
YouTube Thumbnail Generator Module
This module provides functionality for generating YouTube video thumbnails.
"""
import streamlit as st
import time
import logging
import os
import traceback
from PIL import Image
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
from lib.gpt_providers.text_to_image_generation.gen_gemini_images import generate_gemini_image, edit_image
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('youtube_thumbnail_generator')
def generate_thumbnail_concepts(video_title, video_description, target_audience, content_type, style_preference, num_concepts=3):
"""Generate thumbnail concept ideas based on video content."""
logger.info(f"Generating thumbnail concepts for: '{video_title}'")
logger.info(f"Parameters: target_audience={target_audience}, content_type={content_type}, style_preference={style_preference}, num_concepts={num_concepts}")
# Create a system prompt for thumbnail concept generation
system_prompt = """You are a YouTube thumbnail expert specializing in creating engaging, click-worthy thumbnail concepts.
Your task is to generate thumbnail concept ideas based on the provided video information.
Focus ONLY on creating concepts that are optimized for YouTube, with proper visual hierarchy, text placement, and emotional triggers.
Return ONLY the concept descriptions, without any additional commentary or explanations.
Each concept should include:
1. A main visual element or scene
2. Text placement and content
3. Color scheme suggestions
4. Emotional trigger or hook
5. Brief explanation of why this concept would be effective"""
# Build the prompt
prompt = f"""
**Instructions:**
Please generate {num_concepts} thumbnail concept ideas for a YouTube video with the following information:
**Video Title:** {video_title}
**Video Description:** {video_description}
**Target Audience:** {target_audience}
**Content Type:** {content_type}
**Style Preference:** {style_preference}
**Specific Instructions:**
* Each concept should be clearly separated and numbered.
* Focus on creating thumbnails that stand out in search results and recommendations.
* Consider the target audience's interests and preferences.
* Include specific details about visual elements, text placement, and color schemes.
* Explain why each concept would be effective for this specific video.
"""
try:
logger.info("Sending request to LLM for thumbnail concepts")
response = llm_text_gen(prompt, system_prompt=system_prompt)
logger.info(f"Received response from LLM: {len(response)} characters")
return response
except Exception as err:
logger.error(f"Error generating thumbnail concepts: {err}")
logger.error(traceback.format_exc())
st.error(f"Error: Failed to generate thumbnail concepts: {err}")
return None
def generate_thumbnail_design(concept_description, style_preference, aspect_ratio="16:9", keywords=None, style=None, focus=None):
"""Generate a thumbnail image based on the concept description."""
logger.info(f"Generating thumbnail design for concept: '{concept_description[:50]}...'")
logger.info(f"Parameters: style_preference={style_preference}, aspect_ratio={aspect_ratio}, keywords={keywords}, style={style}, focus={focus}")
# Create a prompt for the image generation
image_prompt = f"""
Create a YouTube thumbnail image with the following specifications:
Concept: {concept_description}
Style: {style_preference}
Aspect Ratio: {aspect_ratio}
The image should be:
- High contrast and visually striking
- Suitable for a YouTube thumbnail
- Include the specified visual elements and text
- Follow the color scheme described
- Optimized for small display sizes
Make sure the text is large and readable, and the main subject is centered and prominent.
"""
try:
logger.info("Sending request to Gemini for thumbnail image")
# Generate the image using Gemini with enhanced prompt
img_path = generate_gemini_image(
image_prompt,
keywords=keywords,
style=style,
focus=focus,
enhance_prompt=True
)
logger.info(f"Received image from Gemini: {img_path}")
return img_path
except Exception as err:
logger.error(f"Error generating thumbnail image: {err}")
logger.error(traceback.format_exc())
st.error(f"Error: Failed to generate thumbnail image: {err}")
return None
def edit_thumbnail_image(img_path, edit_instructions):
"""Edit a thumbnail image based on user instructions."""
logger.info(f"Editing thumbnail image: '{img_path}'")
logger.info(f"Edit instructions: '{edit_instructions}'")
try:
logger.info("Sending request to Gemini for image editing")
# Edit the image using Gemini
edited_img_path = edit_image(img_path, edit_instructions)
logger.info(f"Image editing completed. Edited image path: {edited_img_path}")
# Return the path to the edited image
return edited_img_path
except Exception as err:
logger.error(f"Error editing thumbnail image: {err}")
logger.error(traceback.format_exc())
st.error(f"Error: Failed to edit thumbnail image: {err}")
return None
def analyze_thumbnail(thumbnail_path):
"""Analyze a thumbnail for effectiveness."""
logger.info(f"Analyzing thumbnail: '{thumbnail_path}'")
# This would typically involve image analysis, but for now we'll use AI to provide feedback
system_prompt = """You are a YouTube thumbnail expert specializing in analyzing and providing feedback on thumbnail designs.
Your task is to analyze the thumbnail and provide constructive feedback on its effectiveness.
Focus on aspects like visual hierarchy, text readability, emotional impact, and click-worthiness."""
# For now, we'll just return a placeholder analysis
# In a real implementation, we would analyze the actual image
logger.info("Generating thumbnail analysis")
return """
**Thumbnail Analysis:**
- **Visual Hierarchy:** The main subject is well-positioned and stands out against the background.
- **Text Readability:** The text is clear and readable, with good contrast against the background.
- **Emotional Impact:** The thumbnail creates curiosity and emotional connection with the target audience.
- **Click-worthiness:** The design is likely to attract clicks due to its visual appeal and clear value proposition.
**Suggestions for Improvement:**
- Consider adding a subtle border to make the thumbnail stand out more in search results.
- The text could be slightly larger for better readability on mobile devices.
- Adding a small icon or logo could help with brand recognition.
"""
def parse_concepts(concepts_text):
"""Parse the concepts text into a list of individual concepts."""
logger.info("Parsing concepts text into individual concepts")
concept_list = []
current_concept = ""
for line in concepts_text.split('\n'):
if line.strip().startswith(('1.', '2.', '3.', '4.', '5.')):
if current_concept:
concept_list.append(current_concept.strip())
current_concept = line
else:
current_concept += "\n" + line
if current_concept:
concept_list.append(current_concept.strip())
logger.info(f"Parsed {len(concept_list)} concepts from the response")
return concept_list
def write_yt_thumbnail():
"""Create a user interface for YouTube Thumbnail Generator."""
logger.info("Initializing YouTube Thumbnail Generator UI")
st.title("YouTube Thumbnail Generator")
st.write("Create engaging, click-worthy thumbnails for your YouTube videos.")
# Initialize session state for generated thumbnails if it doesn't exist
if "generated_thumbnails" not in st.session_state:
st.session_state.generated_thumbnails = []
if "thumbnail_concepts" not in st.session_state:
st.session_state.thumbnail_concepts = None
if "current_thumbnail_path" not in st.session_state:
st.session_state.current_thumbnail_path = None
if "concept_list" not in st.session_state:
st.session_state.concept_list = []
if "editing_thumbnail" not in st.session_state:
st.session_state.editing_thumbnail = False
if "edit_instructions" not in st.session_state:
st.session_state.edit_instructions = ""
if "edited_thumbnail_path" not in st.session_state:
st.session_state.edited_thumbnail_path = None
if "show_edit_form" not in st.session_state:
st.session_state.show_edit_form = False
# Create tabs for different sections
tab1, tab2 = st.tabs(["Basic Info", "Style & Generation"])
with tab1:
# Basic information inputs
video_title = st.text_input("Video Title",
placeholder="e.g., 10 Tips for Better Photography")
video_description = st.text_area("Video Description",
placeholder="Brief description of your video content")
target_audience = st.text_input("Target Audience",
placeholder="e.g., photography enthusiasts, beginners")
# Content type selection
content_type = st.selectbox("Content Type", [
"Tutorial/How-to",
"Vlog",
"Review",
"Educational",
"Entertainment",
"News/Update",
"Product Showcase",
"Challenge",
"Reaction",
"Comparison"
])
with tab2:
# Style preferences
st.subheader("Style Preferences")
# Create columns for style options
col1, col2 = st.columns(2)
with col1:
style_preference = st.selectbox("Thumbnail Style", [
"Bold and Dramatic",
"Clean and Minimal",
"Colorful and Vibrant",
"Dark and Moody",
"Professional and Corporate",
"Playful and Fun",
"Retro/Vintage",
"Modern and Sleek"
])
num_concepts = st.slider("Number of Concepts", 1, 5, 3)
with col2:
aspect_ratio = st.selectbox("Aspect Ratio", [
"16:9 (Standard)",
"1:1 (Square)",
"4:3 (Classic)",
"9:16 (Vertical)"
])
include_text = st.checkbox("Include Text Overlay", value=True)
if include_text:
text_style = st.selectbox("Text Style", [
"Bold and Impactful",
"Clean and Readable",
"Stylized and Thematic",
"Minimal and Subtle"
])
# Advanced AI Prompt Settings
st.subheader("Advanced AI Prompt Settings")
# Create columns for advanced settings
col3, col4 = st.columns(2)
with col3:
# Image style selection
image_style = st.selectbox("Image Style", [
"Auto (AI will choose best style)",
"Photorealistic",
"Artistic",
"Cartoon/Anime",
"Sketch/Drawing",
"Digital Art",
"3D Render"
])
# Extract style for the generate_gemini_image function
style = None
if image_style == "Photorealistic":
style = "photorealistic"
elif image_style == "Artistic":
style = "artistic"
elif image_style == "Cartoon/Anime":
style = "cartoon"
elif image_style == "Sketch/Drawing":
style = "sketch"
elif image_style == "Digital Art":
style = "digital_art"
elif image_style == "3D Render":
style = "3d_render"
with col4:
# Focus selection for photorealistic images
focus = None
if style == "photorealistic":
focus = st.selectbox("Image Focus", [
"Auto (AI will choose best focus)",
"Portraits",
"Objects",
"Motion",
"Wide-angle"
])
# Extract focus for the generate_gemini_image function
if focus == "Portraits":
focus = "portraits"
elif focus == "Objects":
focus = "objects"
elif focus == "Motion":
focus = "motion"
elif focus == "Wide-angle":
focus = "wide-angle"
elif focus == "Auto (AI will choose best focus)":
focus = None
# Keywords for enhanced prompt generation
st.subheader("Keywords for Enhanced Prompt")
st.write("Add keywords to enhance the AI prompt generation. These will help create more detailed and accurate thumbnails.")
# Create a text area for keywords
keywords_input = st.text_area(
"Keywords (comma-separated)",
placeholder="e.g., vibrant, energetic, bold, eye-catching, professional"
)
# Process keywords
keywords = None
if keywords_input:
keywords = [k.strip() for k in keywords_input.split(",") if k.strip()]
logger.info(f"User provided keywords: {keywords}")
# Generate button
if st.button("Generate Thumbnail Concepts"):
if not video_title:
st.error("Please enter a video title.")
return
with st.spinner("Generating thumbnail concepts..."):
logger.info("User clicked Generate Thumbnail Concepts button")
concepts = generate_thumbnail_concepts(
video_title,
video_description,
target_audience,
content_type,
style_preference,
num_concepts
)
if concepts:
# Store the concepts in session state
st.session_state.thumbnail_concepts = concepts
# Parse the concepts and store in session state
st.session_state.concept_list = parse_concepts(concepts)
logger.info("Stored thumbnail concepts in session state")
# Display the concepts in tabs
st.subheader("Thumbnail Concepts")
# Create tabs for each concept
concept_tabs = st.tabs([f"Concept {i+1}" for i in range(len(st.session_state.concept_list))])
for i, tab in enumerate(concept_tabs):
with tab:
st.markdown(st.session_state.concept_list[i])
# Add a button to generate image for this concept
if st.button(f"Generate Image for Concept {i+1}", key=f"gen_img_{i}"):
with st.spinner(f"Generating thumbnail image for concept {i+1}..."):
logger.info(f"User selected concept {i+1} for image generation")
# Get the selected concept
selected_concept = st.session_state.concept_list[i]
# Generate the thumbnail image with enhanced prompt
img_path = generate_thumbnail_design(
selected_concept,
style_preference,
aspect_ratio.split()[0], # Extract just the ratio part
keywords=keywords,
style=style,
focus=focus
)
if img_path:
# Store the current thumbnail path in session state
st.session_state.current_thumbnail_path = img_path
logger.info(f"Stored current thumbnail path in session state: {img_path}")
# Display the generated image
st.subheader("Generated Thumbnail")
st.image(img_path, use_container_width=True)
# Add download button
with open(img_path, "rb") as file:
st.download_button(
label="Download Thumbnail",
data=file,
file_name=f"youtube_thumbnail_{int(time.time())}.png",
mime="image/png"
)
# Add image editing section
st.subheader("Edit Thumbnail")
st.write("Make changes to your thumbnail by providing instructions below:")
# Create a text area for edit instructions
edit_instructions = st.text_area(
"Edit Instructions",
placeholder="e.g., Make the background darker, Add a red border, Change the text color to white",
key=f"edit_instructions_{i}"
)
# Store edit instructions in session state
st.session_state.edit_instructions = edit_instructions
# Add a button to apply edits
if st.button("Apply Edits", key=f"apply_edits_{i}"):
if not edit_instructions:
st.warning("Please provide edit instructions.")
else:
# Set editing flag
st.session_state.editing_thumbnail = True
st.session_state.show_edit_form = True
# Rerun to update the UI
st.rerun()
# Add analysis button
if st.button("Analyze Thumbnail", key=f"analyze_{i}"):
logger.info("User clicked Analyze Thumbnail button")
analysis = analyze_thumbnail(img_path)
st.subheader("Thumbnail Analysis")
st.markdown(analysis)
else:
st.error("Failed to generate thumbnail concepts. Please try again.")
# Display previously generated concepts if they exist in session state
elif st.session_state.thumbnail_concepts and st.session_state.concept_list:
logger.info("Displaying previously generated concepts from session state")
st.subheader("Thumbnail Concepts")
# Create tabs for each concept
concept_tabs = st.tabs([f"Concept {i+1}" for i in range(len(st.session_state.concept_list))])
for i, tab in enumerate(concept_tabs):
with tab:
st.markdown(st.session_state.concept_list[i])
# Add a button to generate image for this concept
if st.button(f"Generate Image for Concept {i+1}", key=f"gen_img_existing_{i}"):
with st.spinner(f"Generating thumbnail image for concept {i+1}..."):
logger.info(f"User selected concept {i+1} for image generation")
# Get the selected concept
selected_concept = st.session_state.concept_list[i]
# Generate the thumbnail image with enhanced prompt
img_path = generate_thumbnail_design(
selected_concept,
style_preference,
aspect_ratio.split()[0], # Extract just the ratio part
keywords=keywords,
style=style,
focus=focus
)
if img_path:
# Store the current thumbnail path in session state
st.session_state.current_thumbnail_path = img_path
logger.info(f"Stored current thumbnail path in session state: {img_path}")
# Display the generated image
st.subheader("Generated Thumbnail")
st.image(img_path, use_container_width=True)
# Add download button
with open(img_path, "rb") as file:
st.download_button(
label="Download Thumbnail",
data=file,
file_name=f"youtube_thumbnail_{int(time.time())}.png",
mime="image/png"
)
# Add image editing section
st.subheader("Edit Thumbnail")
st.write("Make changes to your thumbnail by providing instructions below:")
# Create a text area for edit instructions
edit_instructions = st.text_area(
"Edit Instructions",
placeholder="e.g., Make the background darker, Add a red border, Change the text color to white",
key=f"edit_instructions_existing_{i}"
)
# Store edit instructions in session state
st.session_state.edit_instructions = edit_instructions
# Add a button to apply edits
if st.button("Apply Edits", key=f"apply_edits_existing_{i}"):
if not edit_instructions:
st.warning("Please provide edit instructions.")
else:
# Set editing flag
st.session_state.editing_thumbnail = True
st.session_state.show_edit_form = True
# Rerun to update the UI
st.rerun()
# Add analysis button
if st.button("Analyze Thumbnail", key=f"analyze_existing_{i}"):
logger.info("User clicked Analyze Thumbnail button")
analysis = analyze_thumbnail(img_path)
st.subheader("Thumbnail Analysis")
st.markdown(analysis)
# Display current thumbnail if it exists in session state
elif st.session_state.current_thumbnail_path:
logger.info(f"Displaying current thumbnail from session state: {st.session_state.current_thumbnail_path}")
st.subheader("Current Thumbnail")
st.image(st.session_state.current_thumbnail_path, use_container_width=True)
# Add download button
with open(st.session_state.current_thumbnail_path, "rb") as file:
st.download_button(
label="Download Thumbnail",
data=file,
file_name=f"youtube_thumbnail_{int(time.time())}.png",
mime="image/png"
)
# Add image editing section
st.subheader("Edit Thumbnail")
st.write("Make changes to your thumbnail by providing instructions below:")
# Create a text area for edit instructions
edit_instructions = st.text_area(
"Edit Instructions",
placeholder="e.g., Make the background darker, Add a red border, Change the text color to white",
key="edit_instructions_current",
value=st.session_state.edit_instructions if st.session_state.edit_instructions else ""
)
# Store edit instructions in session state
st.session_state.edit_instructions = edit_instructions
# Add a button to apply edits
if st.button("Apply Edits", key="apply_edits_current"):
if not edit_instructions:
st.warning("Please provide edit instructions.")
else:
# Set editing flag
st.session_state.editing_thumbnail = True
st.session_state.show_edit_form = True
# Rerun to update the UI
st.rerun()
# Add analysis button
if st.button("Analyze Thumbnail", key="analyze_current"):
logger.info("User clicked Analyze Thumbnail button")
analysis = analyze_thumbnail(st.session_state.current_thumbnail_path)
st.subheader("Thumbnail Analysis")
st.markdown(analysis)
# Handle the editing process
if st.session_state.editing_thumbnail and st.session_state.show_edit_form:
st.subheader("Editing Thumbnail")
# Show a spinner while editing
with st.spinner("Editing thumbnail..."):
logger.info(f"User provided edit instructions: '{st.session_state.edit_instructions}'")
# Edit the thumbnail image
edited_img_path = edit_thumbnail_image(st.session_state.current_thumbnail_path, st.session_state.edit_instructions)
if edited_img_path:
# Update the current thumbnail path in session state
st.session_state.edited_thumbnail_path = edited_img_path
logger.info(f"Updated current thumbnail path in session state: {edited_img_path}")
# Reset editing flags
st.session_state.editing_thumbnail = False
st.session_state.show_edit_form = False
# Display the edited image
st.subheader("Edited Thumbnail")
st.image(edited_img_path, use_container_width=True)
# Add download button for the edited image
with open(edited_img_path, "rb") as file:
st.download_button(
label="Download Edited Thumbnail",
data=file,
file_name=f"youtube_thumbnail_edited_{int(time.time())}.png",
mime="image/png"
)
# Update the current thumbnail path to the edited one
st.session_state.current_thumbnail_path = edited_img_path
# Add a button to continue editing
if st.button("Continue Editing"):
st.session_state.show_edit_form = True
st.rerun()
else:
# Reset editing flags
st.session_state.editing_thumbnail = False
st.session_state.show_edit_form = False
st.error("Failed to edit the thumbnail. Please try again with different instructions.")