Youtube AI Writer Tools

This commit is contained in:
ajaysi
2025-04-08 18:37:35 +05:30
parent 940a4ad1fa
commit 8312dbaaac
18 changed files with 3979 additions and 274 deletions

View File

@@ -67,7 +67,6 @@ logging.basicConfig(
)
logger = logging.getLogger(__name__)
from lib.utils.ui_setup import setup_ui
from lib.utils.api_key_manager.api_key_manager import APIKeyManager, render
from lib.utils.api_key_manager.validation import check_all_api_keys
from dotenv import load_dotenv

View File

@@ -0,0 +1,141 @@
# AI Copywriting Tools
A comprehensive collection of AI-powered copywriting tools designed to help create compelling, conversion-focused content using various proven frameworks and approaches.
## Available Copywriting Tools
### 1. AIDA Copywriter
The AIDA (Attention-Interest-Desire-Action) framework is a classic copywriting approach that guides your audience through a complete journey:
- **Attention**: Captures attention with compelling headlines
- **Interest**: Generates interest through benefits and pain points
- **Desire**: Creates desire by showcasing solutions
- **Action**: Prompts specific actions with strong CTAs
Best for: Landing pages, sales pages, email campaigns, and direct response advertising.
### 2. 4C Copywriter
The 4C framework ensures your message is effective and persuasive through:
- **Clear**: Easy to understand messaging
- **Concise**: Brief and to-the-point content
- **Credible**: Evidence-backed claims
- **Compelling**: Interesting and persuasive messaging
Best for: Email marketing, landing pages, social media, and product descriptions.
### 3. 4R Copywriter
The 4R framework focuses on building relationships with your audience through:
- **Relevance**: Content that matters to your audience
- **Receptivity**: Timing and context optimization
- **Response**: Clear calls to action
- **Return**: Value-driven content
Best for: Content marketing, blog posts, and relationship-building campaigns.
### 4. PAS Copywriter
The PAS (Problem-Agitation-Solution) framework addresses customer pain points:
- **Problem**: Identifies the customer's issue
- **Agitation**: Amplifies the problem's impact
- **Solution**: Presents your offering as the answer
Best for: Problem-solving content, product launches, and service offerings.
### 5. FAB Copywriter
The FAB (Features-Advantages-Benefits) framework focuses on product value:
- **Features**: Product characteristics
- **Advantages**: How features stand out
- **Benefits**: Customer value proposition
Best for: Product descriptions, sales pages, and feature highlights.
### 6. QUEST Copywriter
The QUEST framework creates engaging storytelling:
- **Qualify**: Identify the right audience
- **Understand**: Show empathy
- **Educate**: Provide value
- **Stimulate**: Create desire
- **Transition**: Guide to action
Best for: Story-based marketing, brand storytelling, and content marketing.
### 7. STAR Copywriter
The STAR framework focuses on social proof and testimonials:
- **Situation**: Context of the problem
- **Task**: Challenge faced
- **Action**: Solution implemented
- **Result**: Outcome achieved
Best for: Case studies, testimonials, and success stories.
### 8. OATH Copywriter
The OATH framework addresses customer objections:
- **Objection**: Identify common concerns
- **Acknowledge**: Show understanding
- **Transform**: Turn negatives to positives
- **Handle**: Provide solutions
Best for: Sales pages, product launches, and objection handling.
### 9. AIDPPC Copywriter
The AIDPPC framework extends AIDA with additional elements:
- **Attention**: Initial hook
- **Interest**: Generate curiosity
- **Desire**: Create want
- **Proof**: Provide evidence
- **Push**: Create urgency
- **Close**: Final call to action
Best for: Long-form sales pages and comprehensive marketing materials.
### 10. Emotional Copywriter
Focuses on creating emotional connections through:
- Emotional triggers (FOMO, trust, joy, urgency)
- Personal connections
- Pain point addressing
- Trust building
- Community creation
Best for: Brand storytelling, emotional marketing, and relationship building.
## Features
All copywriting tools include:
- User-friendly interface with Streamlit
- Educational content about each framework
- Customizable input parameters
- Multiple language support
- Tone and style options
- Target audience customization
- Brand-specific content generation
- Retry mechanism for reliable API calls
## Usage
1. Select your desired copywriting framework
2. Fill in the required information:
- Brand/Company details
- Target audience
- Unique selling points
- Desired tone and style
- Platform-specific requirements
3. Generate your copy
4. Review and refine the output
## Best Practices
1. **Know Your Audience**: Always provide detailed target audience information
2. **Be Specific**: Include clear unique selling points and value propositions
3. **Choose the Right Framework**: Match the framework to your content goals
4. **Maintain Consistency**: Keep brand voice and messaging consistent
5. **Test and Optimize**: Use different frameworks for A/B testing
6. **Review and Edit**: Always review AI-generated content for accuracy and tone
## Technical Requirements
- Python 3.7+
- Streamlit
- GPT API access
- Required Python packages (see requirements.txt)
## Support
For technical support or questions about specific frameworks, please refer to the documentation or contact the development team.

View File

@@ -24,8 +24,8 @@ copywriter_modules = [
"aida_copywriter",
"pas_copywriter",
"fab_copywriter",
"four_c_copywriter",
"four_r_copywriter"
"4c_copywriter",
"4r_copywriter"
]
# Dynamically import all copywriter modules

View File

@@ -1,257 +0,0 @@
import time #Iwish
import os
import json
import streamlit as st
def write_yt_description():
st.title("📽️ YT Description Writer")
col1, col2 = st.columns([1, 1])
with col1:
keywords = st.text_input('**Describe Your YT video Keywords (comma-separated)**',
help="Enter keywords separated by commas.").split(',')
target_audience = st.multiselect('**Select Your Target Audience**',
['Beginners', 'Marketers', 'Gamers', 'Foodies', 'Entrepreneurs', 'Students', 'Parents',
'Tech Enthusiasts', 'General Audience', 'News Readers', 'Finance Enthusiasts'],
help="Select the target audience for your video.")
with col2:
tone_style = st.selectbox('**Select Tone and Style of YT Description**',
['Casual', 'Professional', 'Humorous', 'Formal', 'Informal', 'Inspirational'],
help="Select the tone and style of your video.")
language = st.selectbox('**Select YT description Language**',
['English', 'Spanish', 'Chinese', 'Hindi', 'Arabic'],
help="Select the language for the video description.")
if st.button('**Generate YT Description**'):
with st.spinner():
if not keywords:
st.error("🚫 Please provide all required inputs.")
else:
response = generate_youtube_description(keywords, target_audience, tone_style, language)
if response:
st.subheader(f'**🧕👩: Your Final youtube Description !**')
st.write(response)
st.write("\n\n\n\n\n\n")
else:
st.error("💥**Failed to write YT Description. Please try again!**")
def generate_youtube_description(keywords, target_audience, tone_style, language):
""" Generate youtube script generator """
prompt = f"""
Please write a descriptive YouTube description in {language} for a video about {keywords} based on the following information:
Keywords: {', '.join(keywords)}
Target Audience: {', '.join(target_audience)}
Language for description: {', '.join(language)}
Tone and Style: {tone_style}
Specific Instructions:
- Include Primary Keywords Early: Place the most important keywords at the beginning to enhance SEO.
- Write a Compelling Hook: Start with an engaging sentence to grab attention and entice viewers to watch the video.
- Provide a Brief Overview: Summarize the video's content and what viewers can expect to learn or experience.
- Use Relevant Keywords: Integrate additional keywords naturally to improve searchability.
- Add Timestamps: Include timestamps for different sections of the video, if applicable.
- Include Links: Add links to related videos, playlists, or external resources.
- Encourage Engagement: Ask viewers to like, comment, and subscribe, and include a clear call to action.
- Provide Contact Information: Include relevant social media handles, website links, or contact information.
- Use Clear and Concise Language: Avoid jargon and keep sentences straightforward and easy to understand.
- Include Hashtags: Use relevant hashtags to increase discoverability, placing them at the end of the description.
- Tailor the Language and Tone: Adjust to suit the target audience.
- Engage and Describe: Use descriptive language to make the video sound interesting.
- Be Concise but Informative: Provide enough context about the video.
- Highlight Unique Details: Mention any important details or highlights that make the video unique.
- Ensure Proper Grammar and Spelling: Maintain a high standard of writing.
Generate a detailed YouTube description that adheres to the above guidelines and includes a compelling hook, a brief overview, relevant keywords, a call to action, hashtags, and any other relevant information. Ensure proper formatting and a clear structure.
"""
try:
response = generate_text_with_exception_handling(prompt)
return response
except Exception as err:
st.error(f"Exit: Failed to get response from LLM: {err}")
exit(1)
def write_yt_title():
""" Generat YT Titles UI """
st.title("🎬 Write YT Video Titles")
with st.expander("**PRO-TIP** - Read the instructions below.", expanded=True):
col1, col2 = st.columns([5, 5])
with col1:
main_points = st.text_area('**What is your video about ?**',
placeholder='Write few words on your video for title ? (e.g., "New trek, Latest in news, Finance, Tech...")')
tone_style = st.selectbox('**Select Tone & Style**', ['Casual', 'Professional', 'Humorous', 'Formal', 'Informal', 'Inspirational'])
with col2:
target_audience = st.multiselect('**Select Video Target Audience(One Or Multiple)**', [
'Beginners',
'Marketers',
'Gamers',
'Foodies',
'Entrepreneurs',
'Students',
'Parents',
'Tech Enthusiasts',
'General Audience',
'News article',
'Finance Article'])
use_case = st.selectbox('**Youtube Title Use Case**', [
'Tutorials',
'Product Reviews',
'Explainer Videos',
'Vlogs',
'Motivational Speeches',
'Comedy Skits',
'Educational Content'
])
if st.button('**Write YT Titles**'):
with st.status("Assigning AI professional to write your YT Titles..", expanded=True) as status:
if not main_points:
st.error("🚫 Please provide all required inputs.")
else:
response = generate_youtube_title(target_audience, main_points, tone_style, use_case)
if response:
st.subheader(f'**🧕👩: Your Final youtube Titles !**')
st.markdown(response)
st.write("\n\n\n")
else:
st.error("💥**Failed to write Letter. Please try again!**")
def generate_youtube_title(target_audience, main_points, tone_style, use_case):
""" Generate youtube script generator """
prompt = f"""
**Instructions:**
Please generate 5 YouTube title options for a video about **{main_points}** based on the following information:
**Target Audience:** {target_audience}
**Tone and Style:** {tone_style}
**Use Case:** {use_case}
**Specific Instructions:**
* Make the titles catchy and attention-grabbing.
* Use relevant keywords to improve SEO.
* Tailor the language and tone to the target audience.
* Ensure the title reflects the content and use case of the video.
"""
try:
response = generate_text_with_exception_handling(prompt)
return response
except Exception as err:
st.error(f"Exit: Failed to get response from LLM: {err}")
exit(1)
def write_yt_script():
""" Generate youtube scripts """
with st.expander("**PRO-TIP** - Read the instructions below.", expanded=True):
col1, col2 = st.columns([5, 5])
with col1:
main_points = st.text_area('**What is your video about ?**',
placeholder='Write few lines on Video idea for transcript ? (e.g., "New trek, Latest in news, Finance, Tech...")')
tone_style = st.selectbox('**Select Tone & Style**', ['Casual', 'Professional', 'Humorous', 'Formal', 'Informal', 'Inspirational'])
target_audience = st.multiselect('**Select Video Target Audience(One Or Multiple)**', [
'Beginners',
'Marketers',
'Gamers',
'Foodies',
'Entrepreneurs',
'Students',
'Parents',
'Tech Enthusiasts',
'General Audience',
'News article',
'Finance Article'
])
with col2:
# Selectbox for Video Length
video_length = st.selectbox('**Select Video Length**', [
'Short (1-3 minutes)',
'Medium (3-5 minutes)',
'Long (5-10 minutes)',
'Very Long (10+ minutes)'
])
# Selectbox for Script Structure
script_structure = st.selectbox('**Script Structure**', [
'Linear',
'Storytelling',
'Q&A'
])
use_case = st.selectbox('**Youtube Script Use Case**', [
'Tutorials',
'Product Reviews',
'Explainer Videos',
'Vlogs',
'Motivational Speeches',
'Comedy Skits',
'Educational Content'
])
if st.button('**Write YT Script**'):
with st.status("Assigning AI professional to write your YT script..", expanded=True) as status:
if not main_points:
st.error("🚫 Please provide all required inputs.")
else:
response = generate_youtube_script(target_audience, main_points, tone_style, video_length, use_case, script_structure)
if response:
st.subheader(f'**🧕👩: Your Final youtube script!**')
st.write(response)
st.write("\n\n\n\n\n\n")
else:
st.error("💥**Failed to write Letter. Please try again!**")
def generate_youtube_script(target_audience, main_points, tone_style, video_length, use_case, script_structure):
""" Generate youtube script generator """
prompt = f"""
Please write a YouTube script for a video about {main_points} based on the following information:
Target Audience: {', '.join(target_audience)}
Main Points: {', '.join(main_points)}
Tone and Style: {tone_style}
Video Length: {video_length}
Script Structure: {script_structure}
Specific Instructions:
* Include a strong hook to grab attention at the start.
* Structure the script with clear sections and headings.
* Provide engaging introductions and conclusions for each section.
* Use clear and concise language, avoiding jargon or overly technical terms.
* Tailor the language and tone to the target audience.
* Include relevant examples, anecdotes, and stories to make the video more engaging.
* Add questions to encourage viewer interaction and participation.
* End the script with a strong call to action, encouraging viewers to subscribe, like the video, or visit your website.
Use Case: {use_case}
Output Format:
Please provide the script in a clear and easy-to-read format.
Include clear headings for each section and ensure that all instructions are followed.
"""
try:
response = generate_text_with_exception_handling(prompt)
return response
except Exception as err:
st.error(f"Exit: Failed to get response from LLM: {err}")
exit(1)

View File

@@ -0,0 +1,225 @@
YouTube Description Generator with SEO optimization features. Here's a summary of the improvements I've made:
1. Added SEO Optimization Features
Primary and Secondary Keywords:
Renamed the original keywords field to "Primary Keywords" for clarity
Added a new field for "Secondary Keywords" in the SEO Optimization tab
Updated the prompt generation to include both primary and secondary keywords
Keyword Density Checker:
Added a new calculate_keyword_density function that:
Counts occurrences of each keyword in the text
Calculates the density as a percentage of total words
Returns a formatted string with density for each keyword
Character Counter and SEO Score:
Added a character counter that displays the total length of the description
Created a comprehensive calculate_seo_score function that evaluates:
Text length (optimal is between 200-5000 characters)
Keyword placement in the first 100 characters
Keyword density (optimal is between 0.5-2.5%)
Presence of call-to-action phrases
Inclusion of hashtags
Presence of links
Returns a percentage score based on these factors
Improved User Interface
Tabbed Interface:
Organized the interface into three tabs: "Basic Info", "SEO Optimization", and "Advanced Options"
This makes the interface cleaner and more focused
Enhanced Input Fields:
Added more descriptive help text for each field
Improved field organization and grouping
Preview Options:
Added tabs for different views of the generated description:
"Formatted" - Shows the description with proper formatting
"Plain Text" - Shows the raw text for copying
"SEO Analysis" - Shows the SEO metrics and score
Download Option:
Added a download button to save the description as a text file
Improved Prompt Generation
Dynamic Prompt Building:
Restructured the prompt generation to be more dynamic
Only includes sections that are relevant based on user input
Provides more specific instructions when additional information is available
Template Support:
Added support for different description templates
Includes a custom template option for advanced users
These enhancements make the YouTube Description Generator much more useful for content creators by providing:
Better SEO optimization
More detailed analysis of the generated content
A more organized and user-friendly interface
More customization options
The tool now helps creators not only generate descriptions but also evaluate and optimize them for better performance on YouTube.
YouTube Title Generator with the following features:
Character Counter:
Tracks the length of each generated title
Indicates if the title is within the optimal length range (50-60 characters)
Provides visual feedback with success/warning messages
Clickbait Detector:
Contains a comprehensive list of clickbait phrases
Calculates a clickbait score based on the presence of these phrases
Provides clear visual feedback about clickbait detection
SEO Score:
Calculates a score out of 10 based on various SEO elements
Considers title length, numbers, question marks, colons, and brackets
Provides visual feedback about the SEO score
User Interface Improvements:
Displays each title in an expandable section
Shows detailed analysis for each title
Includes a copy button for easy title copying
Provides visual indicators (✅, ⚠️, ❌) for quick assessment
Script Structure Templates
I've expanded the script structure options from just 3 to 14 different formats:
Problem-Solution: Identifies a problem and presents your solution
Before-After-Bridge: Shows the problem, solution, and transformation
Hook-Problem-Solution-Call to Action: Attention-grabbing format with clear problem, solution, and call to action
Compare and Contrast: Compares different options or approaches
Step-by-Step Tutorial: Detailed instructions broken down into clear steps
Case Study: Examines a specific example or scenario in detail
Interview Format: Structured as an interview with questions and answers
Review Format: Evaluates a product, service, or topic with pros and cons
Vlog Format: Personal, conversational style documenting experiences
Educational Format: Focused on teaching a specific concept or skill
Entertainment Format: Engaging, fun-focused content with humor or excitement
Additional Improvements
Structure Descriptions: Added helpful descriptions for each script structure to help users understand which format best suits their content.
Advanced Options: Added an expandable section with customizable options:
Attention-grabbing hooks
Call-to-action elements
Viewer engagement prompts
Suggested timestamps
Visual cues/transitions with different style options
Enhanced Script Generation:
Structure-specific instructions for each template
Visual cue instructions for better video production
Improved prompt engineering for more natural, conversational scripts
Better User Experience:
Progress bar during generation
Tabbed preview with formatted and plain text views
Download button for saving scripts
Improved error handling
More Use Cases: Added additional use cases like News Coverage, How-To Guides, Product Demonstrations, Travel Videos, Cooking/Recipe Videos, Gaming Content, and Tech Reviews.
These enhancements make the YouTube Script Generator much more powerful and flexible, allowing content creators to generate scripts tailored to their specific needs and content types. The structure-specific instructions ensure that each script follows best practices for its format, resulting in more professional and engaging content.
1. Enhanced Engagement Hooks
I've added a variety of engagement hook options that users can select to include in their scripts:
Question Hook: Start with a thought-provoking question
Story Hook: Begin with a brief, relevant story or anecdote
Statistic Hook: Open with an interesting statistic or fact
Controversy Hook: Present a controversial statement to spark interest
Promise Hook: Make a promise about what viewers will learn
Scenario Hook: Describe a relatable scenario
Mystery Hook: Create a sense of mystery or intrigue
Quote Hook: Start with a relevant quote from an expert
2. Community Interaction Points
I've added several options for community interaction that can be included in the script:
Comment Prompt: Ask viewers to share experiences in comments
Poll Suggestion: Suggest creating a poll for viewers
Question for Comments: Pose a specific question for comments
Challenge: Challenge viewers to try something and report back
Tag Friends: Encourage tagging friends who might benefit
Share Request: Ask viewers to share the video
Community Post: Mention creating a community post
Live Stream Teaser: Tease an upcoming live stream
3. Script Export Options
I've implemented a comprehensive export system with multiple format options:
Text (.txt): Simple text format
Markdown (.md): For platforms that support markdown
HTML (.html): Web-friendly format
JSON (.json): Structured data format
Subtitles (SRT): Basic subtitle format for video editing
Additional export features include:
Custom filename option
Copy to clipboard functionality
Formatted and plain text views of the script
Download button with the selected format
UI Improvements
Added a new "Engagement & Export" tab to organize the new features
Improved script display with tabs for formatted and plain text views
Added a subheader for export options
Included additional export options that can be expanded
These enhancements make the YouTube Script Generator more powerful and user-friendly, providing creators with more tools to engage their audience and export their content in various formats.
1. YouTube Thumbnail Generator
Added a dedicated tab with a "Coming Soon" notice
Included a comprehensive description of the tool's features:
Thumbnail concept generation based on video content
Color scheme suggestions aligned with brand
Layout recommendations for maximum click-through rate
Best practices for thumbnail design
Text placement suggestions for readability
Added a placeholder image to visually represent the upcoming feature
2. YouTube Tags Generator
Created a tab with a "Coming Soon" notice
Provided a detailed description of the tool's capabilities:
Relevant tag generation based on video content
Trending tag suggestions to increase visibility
Tag combination recommendations
Tag research tools for finding popular keywords
Recommendations for tag placement and usage
Added a placeholder image for visual appeal
3. YouTube End Screen Generator
Added a tab with a "Coming Soon" notice
Included a description of the tool's features:
End screen template generation based on video type
Strategic CTA placement recommendations
Video playlist promotion suggestions
Best practices for end screen design
Cross-promotion opportunity recommendations
Added a placeholder image to represent the upcoming feature
4. YouTube Playlist Description Generator
Created a tab with a "Coming Soon" notice
Provided a description of the tool's capabilities:
Engaging playlist description generation
SEO optimization recommendations for playlists
Playlist organization suggestions
Best practices for playlist metadata
Recommendations for playlist thumbnails and titles
Added a placeholder image for visual appeal
5. Additional "More Tools" Tab
Added an extra tab for future tools
Included a list of potential future features:
YouTube Analytics Insights
Channel Trailer Generator
Video Series Planner
YouTube Shorts Script Generator
Community Post Generator
Added a call for user suggestions for new tools
Included a placeholder image for visual appeal
Each tool tab follows a consistent format with:
A clear title with an emoji for visual identification
A "Coming Soon" notice using Streamlit's info component
A detailed description of the tool's features
A placeholder image to represent the upcoming feature
This implementation provides users with a clear roadmap of upcoming features while maintaining the existing functionality of the YouTube AI Writer. The "coming soon" state allows you to gauge user interest in these features before fully implementing them.
TBD:
Allow alwrity end users to connect their youtube accounts to fetch their youtube data for analytics and then generate YT related content based on their data and needs:
1). https://developers.google.com/youtube/reporting/v1/code_samples/python
2). https://github.com/youtube/api-samples/blob/master/python/yt_analytics_report.py
3). https://developers.google.com/youtube/reporting/guides/authorization/server-side-web-apps#python

View File

@@ -0,0 +1,96 @@
# YouTube Thumbnail Generator
A powerful AI-powered tool for creating engaging, click-worthy thumbnails for your YouTube videos.
## Overview
The YouTube Thumbnail Generator is a specialized module within the AI Writer suite that helps content creators design eye-catching thumbnails optimized for YouTube. Using advanced AI image generation technology, this tool creates custom thumbnails based on your video content, target audience, and style preferences.
## Features
### 1. AI-Powered Thumbnail Generation
- **Concept Generation**: Automatically generates multiple thumbnail concept ideas based on your video title, description, and target audience
- **Visual Design**: Creates high-quality thumbnail images using state-of-the-art AI image generation
- **Style Customization**: Choose from various style preferences including bold, clean, colorful, dark, professional, playful, retro, and modern
### 2. Advanced Customization Options
- **Aspect Ratio Selection**: Choose from standard YouTube ratios (16:9, 1:1, 4:3, 9:16)
- **Text Overlay Options**: Add and customize text overlays with different styles
- **Image Style Selection**: Choose from photorealistic, artistic, cartoon/anime, sketch/drawing, digital art, or 3D render
- **Focus Selection**: For photorealistic images, specify focus areas like portraits, objects, motion, or wide-angle
### 3. Thumbnail Editing
- **AI-Powered Editing**: Make changes to your generated thumbnails using natural language instructions
- **Iterative Refinement**: Continue editing until you're satisfied with the result
- **Preserve Original**: Keep both original and edited versions of your thumbnails
### 4. Thumbnail Analysis
- **AI Analysis**: Get feedback on your thumbnail's effectiveness
- **Improvement Suggestions**: Receive specific recommendations to enhance your thumbnail's impact
- **Best Practices**: Learn about visual hierarchy, text readability, emotional impact, and click-worthiness
### 5. User-Friendly Interface
- **Tabbed Interface**: Organize your workflow with intuitive tabs for basic info and style settings
- **Concept Tabs**: View and select from multiple thumbnail concepts
- **Real-time Preview**: See your generated thumbnails immediately
- **Download Options**: Easily download your thumbnails in high resolution
## How to Use
### Step 1: Enter Basic Information
- Provide your video title and description
- Specify your target audience
- Select your content type (tutorial, vlog, review, etc.)
### Step 2: Customize Style Preferences
- Choose your preferred thumbnail style
- Select the number of concepts to generate
- Pick your desired aspect ratio
- Configure text overlay options
### Step 3: Generate Thumbnail Concepts
- Click "Generate Thumbnail Concepts" to create multiple thumbnail ideas
- Review each concept in the provided tabs
- Select the concept you'd like to develop further
### Step 4: Generate and Customize Your Thumbnail
- Click "Generate Image" for your selected concept
- Use the editing tools to refine your thumbnail
- Apply changes using natural language instructions
- Download your final thumbnail when satisfied
### Step 5: Analyze Your Thumbnail
- Use the "Analyze Thumbnail" feature to get AI feedback
- Review suggestions for improvement
- Make additional edits based on the analysis
## Technical Details
The Thumbnail Generator uses:
- **Gemini AI**: For high-quality image generation and editing
- **Advanced Prompt Engineering**: To ensure consistent and relevant results
- **Retry Mechanism**: Handles service overloads with exponential backoff
- **Session State Management**: Preserves your work across page refreshes
## Tips for Best Results
1. **Be Specific**: Provide detailed video descriptions to help the AI understand your content
2. **Target Your Audience**: Specify your audience demographics and interests
3. **Choose Appropriate Style**: Select a style that matches your channel's branding
4. **Use Keywords**: Add relevant keywords to enhance the AI's understanding
5. **Iterate**: Don't hesitate to generate multiple concepts and make edits
6. **Analyze**: Use the analysis feature to get objective feedback on your thumbnails
## Requirements
- Internet connection for AI services
- Modern web browser
- No additional software installation required
## Support
For technical issues or feature requests, please contact our support team or submit an issue on our GitHub repository.
---
*The YouTube Thumbnail Generator is part of the AI Writer suite, designed to help content creators streamline their workflow and produce high-quality content.*

View File

@@ -0,0 +1,108 @@
End Screen Generator feature for YouTube videos.
## Step 1: Understanding End Screens
YouTube end screens are the final elements shown at the end of a video that encourage viewers to take action, such as subscribing, watching another video, or visiting a website. They typically include:
1. Call-to-action elements (subscribe button, playlists, other videos)
2. Visual elements (background image, branding)
3. Text overlays (promotional messages, channel name)
4. Layout options (different templates for different purposes)
## Step 2: Required User Inputs
Based on the thumbnail generator and YouTube end screen requirements, we'll need these inputs:
1. **Basic Video Information**:
- Video title
- Video description
- Target audience
- Content type (tutorial, vlog, review, etc.)
2. **End Screen Purpose**:
- Primary goal (drive subscriptions, promote playlist, promote next video, etc.)
- Secondary goal (if applicable)
3. **Visual Style Preferences**:
- Color scheme
- Style (minimal, bold, branded, etc.)
- Brand elements to include (logo, channel name, etc.)
4. **Content Elements**:
- Number of elements to include (1-4)
- Types of elements (subscribe button, playlist, video, website)
- Text for each element
5. **Advanced Settings**:
- Background style (solid color, gradient, image, etc.)
- Animation preferences
- Custom branding elements
## Step 3: Implementation Plan
Let's create a new module called `end_screen_generator.py` in the same directory as the thumbnail generator. Here's how we'll structure it:
1. **Functions**:
- `generate_end_screen_concepts`: Generate end screen design concepts
- `generate_end_screen_design`: Create visual end screen designs
- `analyze_end_screen`: Provide feedback on end screen effectiveness
- `write_yt_end_screen`: Main UI function
2. **User Interface**:
- Tabs for different sections (Basic Info, Style & Elements, Preview)
- Input fields for all required information
- Preview section to show generated end screens
- Download options for the end screen designs
### End Screen Generator Features
1. **Comprehensive User Inputs**:
- Basic video information (title, description, target audience)
- End screen purpose (subscribe, next video, playlist, website, social media)
- Visual style preferences (modern, minimalist, bold, playful, elegant)
- Content elements (text, CTAs, visual elements)
- Advanced settings (image style, focus, keywords)
2. **AI-Powered Generation**:
- Concept generation with detailed descriptions
- Image generation with style customization
- Thumbnail analysis for effectiveness
- Image editing capabilities
3. **User Interface**:
- Tabbed interface for multiple end screen concepts
- Visual preview of generated end screens
- Download options for all generated images
- Edit functionality for refining designs
4. **Integration with Existing Tools**:
- Reuses the image generation and editing functions from the thumbnail generator
- Consistent UI/UX with other YouTube tools
- Proper error handling and logging
### How to Use the End Screen Generator
1. **Access the Tool**:
- Select "End Screen Generator" from the YouTube tools menu
- The tool is now active and ready to use
2. **Generate End Screens**:
- Enter your video details (title, description, target audience)
- Select the primary purpose of your end screen
- Choose your preferred visual style
- Select content elements to include
- Optionally customize advanced settings
- Click "Generate End Screen Concepts"
3. **Review and Customize**:
- Browse through the generated concepts in tabs
- Generate images for concepts you like
- Edit the generated images with specific instructions
- Download your final end screen designs
4. **Analyze Effectiveness**:
- Get AI-powered analysis of your end screen designs
- Receive feedback on visual hierarchy, text readability, and more
The End Screen Generator is now fully integrated into the YouTube AI Writer and ready to use. Would you like me to make any adjustments or enhancements to the implementation?

View File

@@ -0,0 +1,5 @@
"""
YouTube AI Writer Modules
This package contains modular components for the YouTube AI Writer functionality.
"""

View File

@@ -0,0 +1,404 @@
"""
YouTube Description Generator Module
This module provides functionality for generating YouTube video descriptions.
"""
import streamlit as st
import time
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
def calculate_keyword_density(text, keywords):
"""Calculate the density of keywords in the text."""
if not text or not keywords:
return 0
text = text.lower()
keywords = [k.lower() for k in keywords]
total_words = len(text.split())
keyword_count = sum(text.count(k) for k in keywords)
return (keyword_count / total_words) * 100 if total_words > 0 else 0
def calculate_seo_score(text, keywords):
"""Calculate the SEO score of the description."""
score = 0
# Text length (optimal: 250-300 words)
word_count = len(text.split())
if 250 <= word_count <= 300:
score += 3
elif 200 <= word_count <= 350:
score += 2
elif 150 <= word_count <= 400:
score += 1
# Keyword presence
text_lower = text.lower()
keywords_lower = [k.lower() for k in keywords]
keyword_count = sum(text_lower.count(k) for k in keywords_lower)
if keyword_count >= 3:
score += 3
elif keyword_count >= 2:
score += 2
elif keyword_count >= 1:
score += 1
# Call to action phrases
cta_phrases = ["subscribe", "like", "comment", "share", "follow", "check out", "visit", "learn more"]
cta_count = sum(text_lower.count(phrase) for phrase in cta_phrases)
if cta_count >= 2:
score += 2
elif cta_count >= 1:
score += 1
# Hashtags
hashtag_count = text.count("#")
if 3 <= hashtag_count <= 5:
score += 2
elif 1 <= hashtag_count <= 8:
score += 1
# Links
link_count = text.count("http")
if 1 <= link_count <= 3:
score += 2
elif link_count > 3:
score += 1
return min(score, 10) # Cap at 10
def generate_youtube_description(target_audience, main_points, tone_style, use_case, primary_keywords,
secondary_keywords, language, seo_goals, include_timestamps=False,
include_hashtags=False, include_social_handles=False):
"""Generate a YouTube description based on the provided parameters."""
# Create a custom system prompt for YouTube description generation
system_prompt = """You are a YouTube description expert specializing in creating engaging, SEO-optimized video descriptions.
Your task is to generate YouTube video descriptions based on the provided information.
Focus ONLY on creating descriptions that are optimized for YouTube, with proper formatting, keywords, and calls to action.
Return ONLY the description text, without any additional commentary or explanations."""
# Build the prompt
prompt = f"""
**Instructions:**
Please generate a YouTube description for a video about **{main_points}** based on the following information:
**Target Audience:** {target_audience}
**Tone and Style:** {tone_style}
**Use Case:** {use_case}
**Language:** {language}
**Primary Keywords:** {primary_keywords}
**Secondary Keywords:** {secondary_keywords}
**SEO Goals:** {seo_goals}
**Additional Elements:**
{"- Include timestamps for key sections." if include_timestamps else ""}
{"- Include relevant hashtags." if include_hashtags else ""}
{"- Include social media handles." if include_social_handles else ""}
**Specific Instructions:**
* Keep the description informative and engaging.
* Use a conversational tone that matches the target audience.
* Include relevant keywords naturally.
* Add a call to action.
* Keep the length between 250-300 words for optimal SEO.
"""
try:
response = llm_text_gen(prompt, system_prompt=system_prompt)
return response
except Exception as err:
st.error(f"Error: Failed to get response from LLM: {err}")
return None
def write_yt_description():
"""Create a user interface for YouTube Description Generator."""
st.write("Generate SEO-optimized YouTube video descriptions that drive engagement.")
# Initialize session state for generated description if it doesn't exist
if "generated_description" not in st.session_state:
st.session_state.generated_description = None
# Create tabs for different sections
tab1, tab2, tab3 = st.tabs(["Basic Info", "SEO Optimization", "Advanced Options"])
with tab1:
# Basic information inputs
main_points = st.text_area("Main Points/Keywords (comma-separated)",
placeholder="e.g., cooking tips, healthy recipes, quick meals")
# Create columns for the other inputs
col1, col2, col3, col4 = st.columns(4)
with col1:
tone_style = st.selectbox("Tone/Style",
["Professional", "Casual", "Humorous", "Educational", "Entertaining", "Inspirational"])
with col2:
target_audience = st.text_input("Target Audience",
placeholder="e.g., beginners, professionals, parents")
with col3:
use_case = st.selectbox("Use Case",
["How-to/Tutorial", "Vlog", "Review", "Educational", "Entertainment", "News"])
with col4:
language = st.selectbox("Language", ["English", "Spanish", "French", "German", "Italian", "Portuguese"])
with tab2:
# SEO optimization inputs
primary_keywords = st.text_input("Primary Keywords (comma-separated)",
placeholder="e.g., cooking, recipes, healthy food")
secondary_keywords = st.text_input("Secondary Keywords (comma-separated)",
placeholder="e.g., quick meals, budget cooking")
seo_goals = st.multiselect("SEO Goals",
["Increase Views", "Drive Engagement", "Build Subscribers", "Promote Products/Services"])
with tab3:
# Advanced options
st.subheader("Additional Elements")
include_timestamps = st.checkbox("Include Timestamps", value=True)
include_hashtags = st.checkbox("Include Hashtags", value=True)
include_social_handles = st.checkbox("Include Social Media Handles", value=True)
if st.button("Generate Description"):
if not main_points:
st.error("Please enter main points/keywords.")
return
with st.spinner("Generating description..."):
description = generate_youtube_description(
target_audience, main_points, tone_style, use_case, primary_keywords,
secondary_keywords, language, seo_goals, include_timestamps,
include_hashtags, include_social_handles
)
if description:
# Store the description in session state
st.session_state.generated_description = description
# Store other parameters in session state for regeneration
st.session_state.description_params = {
"target_audience": target_audience,
"main_points": main_points,
"tone_style": tone_style,
"use_case": use_case,
"primary_keywords": primary_keywords,
"secondary_keywords": secondary_keywords,
"language": language,
"seo_goals": seo_goals,
"include_timestamps": include_timestamps,
"include_hashtags": include_hashtags,
"include_social_handles": include_social_handles
}
st.subheader("Generated Description")
# Display description with analysis
st.text_area("Description", description, height=200)
# Calculate and display metrics
all_keywords = primary_keywords.split(",") + secondary_keywords.split(",")
keyword_density = calculate_keyword_density(description, all_keywords)
seo_score = calculate_seo_score(description, all_keywords)
col1, col2 = st.columns(2)
with col1:
st.metric("Keyword Density", f"{keyword_density:.1f}%")
with col2:
st.metric("SEO Score", f"{seo_score}/10")
# Create columns for the buttons
btn_col1, btn_col2 = st.columns(2)
with btn_col1:
# Download button
st.download_button(
label="Download Description",
data=description,
file_name="youtube_description.txt",
mime="text/plain"
)
with btn_col2:
# Regenerate button
if st.button("Regenerate"):
st.session_state.show_regenerate_popover = True
# Regenerate popover
if st.session_state.get("show_regenerate_popover", False):
with st.form("regenerate_form"):
st.subheader("Regenerate Description")
st.write("Specify changes you'd like to make to the description:")
changes = st.text_area("Changes to make",
placeholder="e.g., Make it more casual, add more call-to-actions, focus on product benefits")
submitted = st.form_submit_button("Regenerate with Changes")
if submitted and changes:
with st.spinner("Regenerating description..."):
# Get the stored parameters
params = st.session_state.description_params
# Add the changes to the prompt
params["changes"] = changes
# Generate a new description with the changes
new_description = generate_youtube_description_with_changes(
params["target_audience"],
params["main_points"],
params["tone_style"],
params["use_case"],
params["primary_keywords"],
params["secondary_keywords"],
params["language"],
params["seo_goals"],
params["include_timestamps"],
params["include_hashtags"],
params["include_social_handles"],
changes
)
if new_description:
# Update the stored description
st.session_state.generated_description = new_description
st.session_state.show_regenerate_popover = False
st.rerun()
else:
st.error("Failed to regenerate description. Please try again.")
else:
st.error("Failed to generate description. Please try again.")
# Display previously generated description if it exists in session state
elif st.session_state.generated_description:
description = st.session_state.generated_description
params = st.session_state.description_params
st.subheader("Generated Description")
# Display description with analysis
st.text_area("Description", description, height=200)
# Calculate and display metrics
all_keywords = params["primary_keywords"].split(",") + params["secondary_keywords"].split(",")
keyword_density = calculate_keyword_density(description, all_keywords)
seo_score = calculate_seo_score(description, all_keywords)
col1, col2 = st.columns(2)
with col1:
st.metric("Keyword Density", f"{keyword_density:.1f}%")
with col2:
st.metric("SEO Score", f"{seo_score}/10")
# Create columns for the buttons
btn_col1, btn_col2 = st.columns(2)
with btn_col1:
# Download button
st.download_button(
label="Download Description",
data=description,
file_name="youtube_description.txt",
mime="text/plain"
)
with btn_col2:
# Regenerate button
if st.button("Regenerate"):
st.session_state.show_regenerate_popover = True
# Regenerate popover
if st.session_state.get("show_regenerate_popover", False):
with st.form("regenerate_form"):
st.subheader("Regenerate Description")
st.write("Specify changes you'd like to make to the description:")
changes = st.text_area("Changes to make",
placeholder="e.g., Make it more casual, add more call-to-actions, focus on product benefits")
submitted = st.form_submit_button("Regenerate with Changes")
if submitted and changes:
with st.spinner("Regenerating description..."):
# Add the changes to the prompt
params["changes"] = changes
# Generate a new description with the changes
new_description = generate_youtube_description_with_changes(
params["target_audience"],
params["main_points"],
params["tone_style"],
params["use_case"],
params["primary_keywords"],
params["secondary_keywords"],
params["language"],
params["seo_goals"],
params["include_timestamps"],
params["include_hashtags"],
params["include_social_handles"],
changes
)
if new_description:
# Update the stored description
st.session_state.generated_description = new_description
st.session_state.show_regenerate_popover = False
st.rerun()
else:
st.error("Failed to regenerate description. Please try again.")
def generate_youtube_description_with_changes(target_audience, main_points, tone_style, use_case, primary_keywords,
secondary_keywords, language, seo_goals, include_timestamps=False,
include_hashtags=False, include_social_handles=False, changes=""):
"""Generate a YouTube description based on the provided parameters and requested changes."""
# Create a custom system prompt for YouTube description generation
system_prompt = """You are a YouTube description expert specializing in creating engaging, SEO-optimized video descriptions.
Your task is to generate YouTube video descriptions based on the provided information.
Focus ONLY on creating descriptions that are optimized for YouTube, with proper formatting, keywords, and calls to action.
Return ONLY the description text, without any additional commentary or explanations."""
# Build the prompt
prompt = f"""
**Instructions:**
Please generate a YouTube description for a video about **{main_points}** based on the following information:
**Target Audience:** {target_audience}
**Tone and Style:** {tone_style}
**Use Case:** {use_case}
**Language:** {language}
**Primary Keywords:** {primary_keywords}
**Secondary Keywords:** {secondary_keywords}
**SEO Goals:** {seo_goals}
**Additional Elements:**
{"- Include timestamps for key sections." if include_timestamps else ""}
{"- Include relevant hashtags." if include_hashtags else ""}
{"- Include social media handles." if include_social_handles else ""}
**Requested Changes:**
{changes}
**Specific Instructions:**
* Keep the description informative and engaging.
* Use a conversational tone that matches the target audience.
* Include relevant keywords naturally.
* Add a call to action.
* Keep the length between 250-300 words for optimal SEO.
* Incorporate the requested changes into the description.
"""
try:
response = llm_text_gen(prompt, system_prompt=system_prompt)
return response
except Exception as err:
st.error(f"Error: Failed to get response from LLM: {err}")
return None

View File

@@ -0,0 +1,740 @@
"""
YouTube End Screen Generator Module
This module provides functionality for generating YouTube video end screens.
"""
import streamlit as st
import time
import logging
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_end_screen_generator')
def generate_end_screen_concepts(video_title, video_description, target_audience, content_type,
primary_goal, secondary_goal=None, num_concepts=3):
"""Generate end screen concept ideas based on video content."""
logger.info(f"Generating end screen concepts for: '{video_title}'")
logger.info(f"Parameters: target_audience={target_audience}, content_type={content_type}, "
f"primary_goal={primary_goal}, secondary_goal={secondary_goal}, num_concepts={num_concepts}")
# Create a system prompt for end screen concept generation
system_prompt = """You are a YouTube end screen expert specializing in creating engaging, action-driving end screen concepts.
Your task is to generate end screen concept ideas based on the provided video information.
Focus ONLY on creating end screens that are optimized for YouTube, with proper visual hierarchy, element placement, and call-to-action triggers.
Return ONLY the concept descriptions, without any additional commentary or explanations.
Each concept should include:
1. A main visual element or background
2. Element placement and content (subscribe button, playlist, video, website)
3. Color scheme suggestions
4. Text content for each element
5. Brief explanation of why this concept would be effective for the specified goals
IMPORTANT: Format each concept with a clear numbered heading like "1. [Concept Name]" to ensure proper parsing."""
# Build the prompt
prompt = f"""
**Instructions:**
Please generate {num_concepts} end screen 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}
**Primary Goal:** {primary_goal}
**Secondary Goal:** {secondary_goal if secondary_goal else "None specified"}
**Specific Instructions:**
* Each concept should be clearly separated and numbered with a heading like "1. [Concept Name]".
* Focus on creating end screens that drive the specified goals.
* Consider the target audience's interests and preferences.
* Include specific details about visual elements, element placement, and color schemes.
* Explain why each concept would be effective for this specific video and goals.
* Include text suggestions for each element (subscribe button, playlist, video, website).
"""
try:
logger.info("Sending request to LLM for end screen 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 end screen concepts: {err}")
logger.error(traceback.format_exc())
st.error(f"Error: Failed to generate end screen concepts: {err}")
return None
def generate_end_screen_design(concept_description, style_preference, element_count=2,
element_types=None, element_texts=None, aspect_ratio="16:9",
keywords=None, style=None, focus=None):
"""Generate an end screen image based on the concept description."""
logger.info(f"Generating end screen design for concept: '{concept_description[:50]}...'")
logger.info(f"Parameters: style_preference={style_preference}, element_count={element_count}, "
f"element_types={element_types}, element_texts={element_texts}, aspect_ratio={aspect_ratio}")
# Extract key elements from the concept description
# This helps focus the prompt on the most important aspects
concept_lines = concept_description.split('\n')
main_visual = ""
element_placement = ""
color_scheme = ""
text_content = ""
for line in concept_lines:
if "visual" in line.lower() or "background" in line.lower():
main_visual = line
elif "placement" in line.lower() or "layout" in line.lower():
element_placement = line
elif "color" in line.lower() or "scheme" in line.lower():
color_scheme = line
elif "text" in line.lower() or "content" in line.lower():
text_content = line
# Create a more focused prompt for the image generation
image_prompt = f"""
Create a YouTube end screen image with the following specifications:
MAIN VISUAL: {main_visual if main_visual else "Not specified"}
ELEMENT PLACEMENT: {element_placement if element_placement else "Not specified"}
COLOR SCHEME: {color_scheme if color_scheme else "Not specified"}
TEXT CONTENT: {text_content if text_content else "Not specified"}
STYLE: {style_preference}
ASPECT RATIO: {aspect_ratio}
NUMBER OF ELEMENTS: {element_count}
ELEMENT TYPES: {', '.join(element_types) if element_types else 'Not specified'}
ELEMENT TEXTS: {', '.join(element_texts) if element_texts else 'Not specified'}
IMPORTANT REQUIREMENTS:
1. This must be a VISUAL IMAGE of a YouTube end screen, not just a text description
2. The image should be high contrast and visually striking
3. All text should be large and readable
4. Elements should be properly placed for optimal viewer engagement
5. The design should follow the specified color scheme
6. The image should be optimized for the specified aspect ratio
PLEASE GENERATE AN ACTUAL IMAGE, NOT JUST A TEXT DESCRIPTION.
"""
try:
logger.info("Sending request to Gemini for end screen 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 end screen image: {err}")
logger.error(traceback.format_exc())
st.error(f"Error: Failed to generate end screen image: {err}")
return None
def edit_end_screen_image(img_path, edit_instructions):
"""Edit an end screen image based on user instructions."""
logger.info(f"Editing end screen 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, f"Edit this image according to these instructions: {edit_instructions}. IMPORTANT: Please generate an actual edited image, not just a text description. I need a visual representation of the edited end screen.")
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 end screen image: {err}")
logger.error(traceback.format_exc())
st.error(f"Error: Failed to edit end screen image: {err}")
return None
def analyze_end_screen(end_screen_path):
"""Analyze an end screen for effectiveness."""
logger.info(f"Analyzing end screen: '{end_screen_path}'")
# This would typically involve image analysis, but for now we'll use AI to provide feedback
system_prompt = """You are a YouTube end screen expert specializing in analyzing and providing feedback on end screen designs.
Your task is to analyze the end screen and provide constructive feedback on its effectiveness.
Focus on aspects like visual hierarchy, element placement, call-to-action clarity, and overall effectiveness."""
# For now, we'll just return a placeholder analysis
# In a real implementation, we would analyze the actual image
logger.info("Generating end screen analysis")
return """
**End Screen Analysis:**
- **Visual Hierarchy:** The main elements are well-positioned and stand out against the background.
- **Element Placement:** The call-to-action elements are strategically placed for optimal viewer engagement.
- **Call-to-Action Clarity:** The text and visual cues clearly communicate the desired actions.
- **Overall Effectiveness:** The design is likely to drive the specified goals due to its visual appeal and clear value proposition.
**Suggestions for Improvement:**
- Consider adding a subtle animation hint to draw attention to the most important element.
- 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")
# Split the concepts text by main concept headers
concepts = []
current_concept = ""
# Look for patterns like numbered headings (e.g., "1.", "2.", "3.") or "Concept 1:", "Concept 2:", etc.
concept_patterns = ["1.", "2.", "3.", "4.", "5.", "Concept 1:", "Concept 2:", "Concept 3:", "Concept 4:", "Concept 5:"]
for line in concepts_text.split('\n'):
# Check if line starts with a concept pattern
is_new_concept = False
for pattern in concept_patterns:
if line.strip().startswith(pattern):
# If we have a previous concept, add it to the list
if current_concept:
concepts.append(current_concept.strip())
# Start a new concept
current_concept = line
is_new_concept = True
break
if not is_new_concept:
# Add the line to the current concept
current_concept += "\n" + line
# Add the last concept
if current_concept:
concepts.append(current_concept.strip())
logger.info(f"Parsed {len(concepts)} concepts from the response")
return concepts
def write_yt_end_screen():
"""Create a user interface for YouTube End Screen Generator."""
logger.info("Initializing YouTube End Screen Generator UI")
st.title("YouTube End Screen Generator")
st.write("Create engaging, action-driving end screens for your YouTube videos.")
# Initialize session state for generated end screens if it doesn't exist
if "generated_end_screens" not in st.session_state:
st.session_state.generated_end_screens = []
if "end_screen_concepts" not in st.session_state:
st.session_state.end_screen_concepts = None
if "current_end_screen_path" not in st.session_state:
st.session_state.current_end_screen_path = None
if "concept_list" not in st.session_state:
st.session_state.concept_list = []
if "editing_end_screen" not in st.session_state:
st.session_state.editing_end_screen = False
if "edit_instructions" not in st.session_state:
st.session_state.edit_instructions = ""
if "edited_end_screen_path" not in st.session_state:
st.session_state.edited_end_screen_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 & Elements"])
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"
])
# End screen goals
st.subheader("End Screen Goals")
primary_goal = st.selectbox("Primary Goal", [
"Drive Subscriptions",
"Promote Playlist",
"Promote Next Video",
"Promote Website",
"Promote Social Media",
"Promote Product/Service",
"Encourage Comments",
"Mixed Goals"
])
secondary_goal = st.selectbox("Secondary Goal (Optional)", [
"None",
"Drive Subscriptions",
"Promote Playlist",
"Promote Next Video",
"Promote Website",
"Promote Social Media",
"Promote Product/Service",
"Encourage Comments"
])
if secondary_goal == "None":
secondary_goal = None
with tab2:
# Style preferences
st.subheader("Style Preferences")
# Create columns for style options
col1, col2 = st.columns(2)
with col1:
style_preference = st.selectbox("End Screen 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_branding = st.checkbox("Include Branding Elements", value=True)
if include_branding:
branding_elements = st.multiselect("Branding Elements", [
"Channel Logo",
"Channel Name",
"Channel Tagline",
"Brand Colors",
"Watermark"
])
# Element configuration
st.subheader("End Screen Elements")
# Number of elements
element_count = st.slider("Number of Elements", 1, 4, 2)
# Element types
element_types = []
element_texts = []
for i in range(element_count):
st.write(f"Element {i+1}")
col1, col2 = st.columns(2)
with col1:
element_type = st.selectbox(
f"Type",
["Subscribe Button", "Playlist", "Video", "Website", "Social Media"],
key=f"element_type_{i}"
)
element_types.append(element_type)
with col2:
element_text = st.text_input(
f"Text",
placeholder=f"Text for {element_type}",
key=f"element_text_{i}"
)
element_texts.append(element_text)
# 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 end screens.")
# 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 - placed outside of tabs for better visibility
st.markdown("---")
st.subheader("Generate End Screen Concepts")
st.write("Click the button below to generate end screen concepts based on your inputs.")
if st.button("Generate End Screen Concepts", type="primary"):
if not video_title:
st.error("Please enter a video title.")
return
with st.spinner("Generating end screen concepts..."):
logger.info("User clicked Generate End Screen Concepts button")
concepts = generate_end_screen_concepts(
video_title,
video_description,
target_audience,
content_type,
primary_goal,
secondary_goal,
num_concepts
)
if concepts:
# Store the concepts in session state
st.session_state.end_screen_concepts = concepts
# Parse the concepts and store in session state
st.session_state.concept_list = parse_concepts(concepts)
logger.info("Stored end screen concepts in session state")
# Display the concepts in tabs
st.subheader("End Screen 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 end screen 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 end screen image with enhanced prompt
img_path = generate_end_screen_design(
selected_concept,
style_preference,
element_count,
element_types,
element_texts,
aspect_ratio.split()[0], # Extract just the ratio part
keywords=keywords,
style=style,
focus=focus
)
if img_path:
# Store the current end screen path in session state
st.session_state.current_end_screen_path = img_path
logger.info(f"Stored current end screen path in session state: {img_path}")
# Display the generated image
st.subheader("Generated End Screen")
st.image(img_path, use_container_width=True)
# Add download button
with open(img_path, "rb") as file:
st.download_button(
label="Download End Screen",
data=file,
file_name=f"youtube_end_screen_{int(time.time())}.png",
mime="image/png"
)
# Add image editing section
st.subheader("Edit End Screen")
st.write("Make changes to your end screen 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_end_screen = True
st.session_state.show_edit_form = True
# Rerun to update the UI
st.rerun()
# Add analysis button
if st.button("Analyze End Screen", key=f"analyze_{i}"):
logger.info("User clicked Analyze End Screen button")
analysis = analyze_end_screen(img_path)
st.subheader("End Screen Analysis")
st.markdown(analysis)
else:
st.error("Failed to generate end screen concepts. Please try again.")
# Display previously generated concepts if they exist in session state
elif st.session_state.end_screen_concepts and st.session_state.concept_list:
logger.info("Displaying previously generated concepts from session state")
st.subheader("End Screen 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 end screen 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 end screen image with enhanced prompt
img_path = generate_end_screen_design(
selected_concept,
style_preference,
element_count,
element_types,
element_texts,
aspect_ratio.split()[0], # Extract just the ratio part
keywords=keywords,
style=style,
focus=focus
)
if img_path:
# Store the current end screen path in session state
st.session_state.current_end_screen_path = img_path
logger.info(f"Stored current end screen path in session state: {img_path}")
# Display the generated image
st.subheader("Generated End Screen")
st.image(img_path, use_container_width=True)
# Add download button
with open(img_path, "rb") as file:
st.download_button(
label="Download End Screen",
data=file,
file_name=f"youtube_end_screen_{int(time.time())}.png",
mime="image/png"
)
# Add image editing section
st.subheader("Edit End Screen")
st.write("Make changes to your end screen 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_end_screen = True
st.session_state.show_edit_form = True
# Rerun to update the UI
st.rerun()
# Add analysis button
if st.button("Analyze End Screen", key=f"analyze_existing_{i}"):
logger.info("User clicked Analyze End Screen button")
analysis = analyze_end_screen(img_path)
st.subheader("End Screen Analysis")
st.markdown(analysis)
# Display current end screen if it exists in session state
elif st.session_state.current_end_screen_path:
logger.info(f"Displaying current end screen from session state: {st.session_state.current_end_screen_path}")
st.subheader("Current End Screen")
st.image(st.session_state.current_end_screen_path, use_container_width=True)
# Add download button
with open(st.session_state.current_end_screen_path, "rb") as file:
st.download_button(
label="Download End Screen",
data=file,
file_name=f"youtube_end_screen_{int(time.time())}.png",
mime="image/png"
)
# Add image editing section
st.subheader("Edit End Screen")
st.write("Make changes to your end screen 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 new element, 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_end_screen = True
st.session_state.show_edit_form = True
# Rerun to update the UI
st.rerun()
# Add analysis button
if st.button("Analyze End Screen", key="analyze_current"):
logger.info("User clicked Analyze End Screen button")
analysis = analyze_end_screen(st.session_state.current_end_screen_path)
st.subheader("End Screen Analysis")
st.markdown(analysis)
# Handle the editing process
if st.session_state.editing_end_screen and st.session_state.show_edit_form:
st.subheader("Editing End Screen")
# Show a spinner while editing
with st.spinner("Editing end screen..."):
logger.info(f"User provided edit instructions: '{st.session_state.edit_instructions}'")
# Edit the end screen image
edited_img_path = edit_end_screen_image(st.session_state.current_end_screen_path, st.session_state.edit_instructions)
if edited_img_path:
# Update the current end screen path in session state
st.session_state.edited_end_screen_path = edited_img_path
logger.info(f"Updated current end screen path in session state: {edited_img_path}")
# Reset editing flags
st.session_state.editing_end_screen = False
st.session_state.show_edit_form = False
# Display the edited image
st.subheader("Edited End Screen")
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 End Screen",
data=file,
file_name=f"youtube_end_screen_edited_{int(time.time())}.png",
mime="image/png"
)
# Update the current end screen path to the edited one
st.session_state.current_end_screen_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_end_screen = False
st.session_state.show_edit_form = False
st.error("Failed to edit the end screen. Please try again with different instructions.")

View File

@@ -0,0 +1,556 @@
"""
YouTube Script Generator Module
This module provides functionality for generating YouTube video scripts.
"""
import streamlit as st
import time
import json
import os
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
def generate_youtube_script(target_audience, main_points, tone_style, use_case, script_structure,
include_hook=False, include_cta=False, include_engagement=False,
include_timestamps=False, include_visual_cues=False, engagement_hooks=None,
community_interactions=None, language="English"):
"""Generate a YouTube script based on the provided parameters."""
# Create a custom system prompt for YouTube script generation
system_prompt = f"""You are a YouTube script expert specializing in creating engaging, well-structured video scripts in {language}.
Your task is to generate YouTube video scripts based on the provided information.
Focus ONLY on creating scripts that are optimized for YouTube, with proper structure, engagement hooks, and calls to action.
Return ONLY the script text, without any additional commentary or explanations.
Format the script with clear sections, speaker notes, and visual cues where appropriate.
Write the entire script in {language}."""
# Build structure-specific instructions
structure_instructions = {
"Problem-Solution": "Structure the script to first present a problem, then provide a solution.",
"Before-After-Bridge": "Structure the script to show the before state, the transformation process, and the after state.",
"Hook-Problem-Solution-Call to Action": "Start with a hook, present the problem, provide the solution, and end with a call to action.",
"Compare and Contrast": "Structure the script to compare and contrast different options or approaches.",
"Step-by-Step Tutorial": "Break down the content into clear, sequential steps.",
"Case Study": "Present a real-world example or case study to illustrate the main points.",
"Interview Format": "Structure the script as an interview with questions and answers.",
"Review Format": "Structure the script as a review with pros, cons, and a final verdict.",
"Vlog Format": "Structure the script as a personal video blog with a conversational tone.",
"Educational Format": "Structure the script to teach a concept with examples and explanations.",
"Entertainment Format": "Structure the script to entertain while delivering the main message."
}
# Build the prompt
prompt = f"""
**Instructions:**
Please generate a YouTube script in {language} for a video about **{main_points}** based on the following information:
**Target Audience:** {target_audience}
**Tone and Style:** {tone_style}
**Use Case:** {use_case}
**Script Structure:** {script_structure}
**Language:** {language}
**Structure Instructions:**
{structure_instructions.get(script_structure, "Follow a logical flow to present the content.")}
**Additional Elements:**
{"- Include a hook at the beginning to grab attention." if include_hook else ""}
{"- End with a strong call to action." if include_cta else ""}
{"- Include prompts for viewer engagement (e.g., questions, polls)." if include_engagement else ""}
{"- Include suggested timestamps for key sections." if include_timestamps else ""}
{"- Include visual cues and transitions." if include_visual_cues else ""}
"""
# Add engagement hooks if provided
if engagement_hooks:
prompt += "\n**Engagement Hooks:**\n"
for hook in engagement_hooks:
prompt += f"- {hook}\n"
# Add community interaction points if provided
if community_interactions:
prompt += "\n**Community Interaction Points:**\n"
for interaction in community_interactions:
prompt += f"- {interaction}\n"
prompt += """
**Specific Instructions:**
* Keep the language clear and engaging.
* Use a conversational tone that matches the target audience.
* Include relevant examples and explanations.
* Ensure the script flows naturally and maintains viewer interest.
"""
try:
response = llm_text_gen(prompt, system_prompt=system_prompt)
return response
except Exception as err:
st.error(f"Error: Failed to get response from LLM: {err}")
return None
def generate_youtube_script_with_changes(target_audience, main_points, tone_style, use_case, script_structure,
include_hook=False, include_cta=False, include_engagement=False,
include_timestamps=False, include_visual_cues=False, engagement_hooks=None,
community_interactions=None, changes="", language="English"):
"""Generate a YouTube script based on the provided parameters and requested changes."""
# Create a custom system prompt for YouTube script generation
system_prompt = f"""You are a YouTube script expert specializing in creating engaging, well-structured video scripts in {language}.
Your task is to generate YouTube video scripts based on the provided information.
Focus ONLY on creating scripts that are optimized for YouTube, with proper structure, engagement hooks, and calls to action.
Return ONLY the script text, without any additional commentary or explanations.
Format the script with clear sections, speaker notes, and visual cues where appropriate.
Write the entire script in {language}."""
# Build structure-specific instructions
structure_instructions = {
"Problem-Solution": "Structure the script to first present a problem, then provide a solution.",
"Before-After-Bridge": "Structure the script to show the before state, the transformation process, and the after state.",
"Hook-Problem-Solution-Call to Action": "Start with a hook, present the problem, provide the solution, and end with a call to action.",
"Compare and Contrast": "Structure the script to compare and contrast different options or approaches.",
"Step-by-Step Tutorial": "Break down the content into clear, sequential steps.",
"Case Study": "Present a real-world example or case study to illustrate the main points.",
"Interview Format": "Structure the script as an interview with questions and answers.",
"Review Format": "Structure the script as a review with pros, cons, and a final verdict.",
"Vlog Format": "Structure the script as a personal video blog with a conversational tone.",
"Educational Format": "Structure the script to teach a concept with examples and explanations.",
"Entertainment Format": "Structure the script to entertain while delivering the main message."
}
# Build the prompt
prompt = f"""
**Instructions:**
Please generate a YouTube script in {language} for a video about **{main_points}** based on the following information:
**Target Audience:** {target_audience}
**Tone and Style:** {tone_style}
**Use Case:** {use_case}
**Script Structure:** {script_structure}
**Language:** {language}
**Structure Instructions:**
{structure_instructions.get(script_structure, "Follow a logical flow to present the content.")}
**Additional Elements:**
{"- Include a hook at the beginning to grab attention." if include_hook else ""}
{"- End with a strong call to action." if include_cta else ""}
{"- Include prompts for viewer engagement (e.g., questions, polls)." if include_engagement else ""}
{"- Include suggested timestamps for key sections." if include_timestamps else ""}
{"- Include visual cues and transitions." if include_visual_cues else ""}
"""
# Add engagement hooks if provided
if engagement_hooks:
prompt += "\n**Engagement Hooks:**\n"
for hook in engagement_hooks:
prompt += f"- {hook}\n"
# Add community interaction points if provided
if community_interactions:
prompt += "\n**Community Interaction Points:**\n"
for interaction in community_interactions:
prompt += f"- {interaction}\n"
# Add requested changes
prompt += f"""
**Requested Changes:**
{changes}
**Specific Instructions:**
* Keep the language clear and engaging.
* Use a conversational tone that matches the target audience.
* Include relevant examples and explanations.
* Ensure the script flows naturally and maintains viewer interest.
* Incorporate the requested changes into the script.
"""
try:
response = llm_text_gen(prompt, system_prompt=system_prompt)
return response
except Exception as err:
st.error(f"Error: Failed to get response from LLM: {err}")
return None
def export_script(script, format_type, filename=None):
"""Export the script in various formats."""
if not filename:
filename = "youtube_script"
if format_type == "Text":
return script, f"{filename}.txt", "text/plain"
elif format_type == "Markdown":
return script, f"{filename}.md", "text/markdown"
elif format_type == "HTML":
html_content = f"<html><body><pre>{script}</pre></body></html>"
return html_content, f"{filename}.html", "text/html"
elif format_type == "JSON":
json_content = json.dumps({"script": script}, indent=2)
return json_content, f"{filename}.json", "application/json"
elif format_type == "Subtitles (SRT)":
# Convert script to basic SRT format
lines = script.split('\n')
srt_content = ""
for i, line in enumerate(lines):
if line.strip():
start_time = f"00:00:{i*5:02d},000"
end_time = f"00:00:{(i+1)*5:02d},000"
srt_content += f"{i+1}\n{start_time} --> {end_time}\n{line}\n\n"
return srt_content, f"{filename}.srt", "text/plain"
else:
return script, f"{filename}.txt", "text/plain"
def write_yt_script():
"""Create a user interface for YouTube Script Generator."""
st.write("Generate professional YouTube video scripts with optimized structures for engagement.")
# Initialize session state for generated script if it doesn't exist
if "generated_script" not in st.session_state:
st.session_state.generated_script = None
# Create tabs for different sections
tab1, tab2, tab3 = st.tabs(["Basic Info", "Advanced Options", "Engagement & Export"])
with tab1:
# Basic information inputs
main_points = st.text_area("Main Points/Keywords (comma-separated)",
placeholder="e.g., cooking tips, healthy recipes, quick meals")
target_audience = st.text_input("Target Audience",
placeholder="e.g., beginners, professionals, parents")
# Create columns for tone, use case, structure, and language
col1, col2, col3, col4 = st.columns(4)
with col1:
tone_style = st.selectbox("Tone/Style",
["Professional", "Casual", "Humorous", "Educational", "Entertaining", "Inspirational"])
with col2:
use_case = st.selectbox("Use Case",
["How-to/Tutorial", "Vlog", "Review", "Educational", "Entertainment", "News"])
with col3:
script_structure = st.selectbox("Script Structure", [
"Problem-Solution",
"Before-After-Bridge",
"Hook-Problem-Solution-Call to Action",
"Compare and Contrast",
"Step-by-Step Tutorial",
"Case Study",
"Interview Format",
"Review Format",
"Vlog Format",
"Educational Format",
"Entertainment Format"
])
with col4:
language = st.selectbox("Language", [
"English",
"Spanish",
"French",
"German",
"Italian",
"Portuguese",
"Russian",
"Japanese",
"Korean",
"Chinese",
"Hindi",
"Arabic"
])
with tab2:
# Advanced options
st.subheader("Additional Elements")
include_hook = st.checkbox("Include Hook", value=True)
include_cta = st.checkbox("Include Call to Action", value=True)
include_engagement = st.checkbox("Include Viewer Engagement Prompts", value=True)
include_timestamps = st.checkbox("Include Suggested Timestamps", value=True)
include_visual_cues = st.checkbox("Include Visual Cues/Transitions", value=True)
with tab3:
# Engagement hooks
st.subheader("Engagement Hooks")
st.write("Select engagement hooks to include in your script:")
engagement_hooks = []
if st.checkbox("Question Hook", value=False):
engagement_hooks.append("Start with a thought-provoking question to engage viewers immediately")
if st.checkbox("Story Hook", value=False):
engagement_hooks.append("Begin with a brief, relevant story or anecdote")
if st.checkbox("Statistic Hook", value=False):
engagement_hooks.append("Open with an interesting statistic or fact")
if st.checkbox("Controversy Hook", value=False):
engagement_hooks.append("Present a controversial statement or opinion to spark interest")
if st.checkbox("Promise Hook", value=False):
engagement_hooks.append("Make a promise about what viewers will learn or gain")
if st.checkbox("Scenario Hook", value=False):
engagement_hooks.append("Describe a scenario or situation viewers can relate to")
if st.checkbox("Mystery Hook", value=False):
engagement_hooks.append("Create a sense of mystery or intrigue")
if st.checkbox("Quote Hook", value=False):
engagement_hooks.append("Start with a relevant quote from an expert or notable figure")
# Community interaction points
st.subheader("Community Interaction Points")
st.write("Select community interaction points to include in your script:")
community_interactions = []
if st.checkbox("Comment Prompt", value=False):
community_interactions.append("Ask viewers to share their experiences or opinions in the comments")
if st.checkbox("Poll Suggestion", value=False):
community_interactions.append("Suggest creating a poll for viewers to vote on")
if st.checkbox("Question for Comments", value=False):
community_interactions.append("Pose a specific question for viewers to answer in the comments")
if st.checkbox("Challenge", value=False):
community_interactions.append("Challenge viewers to try something and report back")
if st.checkbox("Tag Friends", value=False):
community_interactions.append("Encourage viewers to tag friends who might benefit from the content")
if st.checkbox("Share Request", value=False):
community_interactions.append("Ask viewers to share the video with others who might find it helpful")
if st.checkbox("Community Post", value=False):
community_interactions.append("Mention creating a community post to continue the discussion")
if st.checkbox("Live Stream Teaser", value=False):
community_interactions.append("Tease an upcoming live stream on the same topic")
# Export options
st.subheader("Export Options")
export_format = st.selectbox("Export Format", [
"Text",
"Markdown",
"HTML",
"JSON",
"Subtitles (SRT)"
])
custom_filename = st.text_input("Custom Filename (optional)",
placeholder="Leave blank for default filename")
if st.button("Generate Script"):
if not main_points:
st.error("Please enter main points/keywords.")
return
with st.spinner("Generating script..."):
script = generate_youtube_script(
target_audience, main_points, tone_style, use_case, script_structure,
include_hook, include_cta, include_engagement, include_timestamps, include_visual_cues,
engagement_hooks if engagement_hooks else None,
community_interactions if community_interactions else None,
language
)
if script:
# Store the script in session state
st.session_state.generated_script = script
# Store other parameters in session state for regeneration
st.session_state.script_params = {
"target_audience": target_audience,
"main_points": main_points,
"tone_style": tone_style,
"use_case": use_case,
"script_structure": script_structure,
"include_hook": include_hook,
"include_cta": include_cta,
"include_engagement": include_engagement,
"include_timestamps": include_timestamps,
"include_visual_cues": include_visual_cues,
"engagement_hooks": engagement_hooks if engagement_hooks else None,
"community_interactions": community_interactions if community_interactions else None,
"language": language
}
st.subheader("Generated Script")
# Display script with tabs for different views
script_tab1, script_tab2 = st.tabs(["Formatted View", "Plain Text"])
with script_tab1:
st.markdown(script)
with script_tab2:
st.code(script)
# Export options
st.subheader("Export Script")
# Get export data
export_data, export_filename, mime_type = export_script(
script,
export_format,
custom_filename if custom_filename else None
)
# Create columns for the buttons
btn_col1, btn_col2 = st.columns(2)
with btn_col1:
# Download button
st.download_button(
label=f"Download as {export_format}",
data=export_data,
file_name=export_filename,
mime=mime_type
)
with btn_col2:
# Regenerate button
if st.button("Regenerate"):
st.session_state.show_regenerate_popover = True
# Regenerate popover
if st.session_state.get("show_regenerate_popover", False):
with st.form("regenerate_form"):
st.subheader("Regenerate Script")
st.write("Specify changes you'd like to make to the script:")
changes = st.text_area("Changes to make",
placeholder="e.g., Make it more casual, add more call-to-actions, focus on product benefits")
submitted = st.form_submit_button("Regenerate with Changes")
if submitted and changes:
with st.spinner("Regenerating script..."):
# Get the stored parameters
params = st.session_state.script_params
# Generate a new script with the changes
new_script = generate_youtube_script_with_changes(
params["target_audience"],
params["main_points"],
params["tone_style"],
params["use_case"],
params["script_structure"],
params["include_hook"],
params["include_cta"],
params["include_engagement"],
params["include_timestamps"],
params["include_visual_cues"],
params["engagement_hooks"],
params["community_interactions"],
changes,
params["language"]
)
if new_script:
# Update the stored script
st.session_state.generated_script = new_script
st.session_state.show_regenerate_popover = False
st.rerun()
else:
st.error("Failed to regenerate script. Please try again.")
# Additional export options
if st.checkbox("Show additional export options"):
col1, col2 = st.columns(2)
with col1:
if st.button("Copy to Clipboard"):
st.code(script)
st.success("Script copied to clipboard!")
with col2:
if st.button("Save to Local File"):
# This is a placeholder - actual file saving would require additional backend functionality
st.info("This feature would save the file locally on your device.")
else:
st.error("Failed to generate script. Please try again.")
# Display previously generated script if it exists in session state
elif st.session_state.generated_script:
script = st.session_state.generated_script
params = st.session_state.script_params
st.subheader("Generated Script")
# Display script with tabs for different views
script_tab1, script_tab2 = st.tabs(["Formatted View", "Plain Text"])
with script_tab1:
st.markdown(script)
with script_tab2:
st.code(script)
# Export options
st.subheader("Export Script")
# Get export data
export_data, export_filename, mime_type = export_script(
script,
export_format,
custom_filename if custom_filename else None
)
# Create columns for the buttons
btn_col1, btn_col2 = st.columns(2)
with btn_col1:
# Download button
st.download_button(
label=f"Download as {export_format}",
data=export_data,
file_name=export_filename,
mime=mime_type
)
with btn_col2:
# Regenerate button
if st.button("Regenerate"):
st.session_state.show_regenerate_popover = True
# Regenerate popover
if st.session_state.get("show_regenerate_popover", False):
with st.form("regenerate_form"):
st.subheader("Regenerate Script")
st.write("Specify changes you'd like to make to the script:")
changes = st.text_area("Changes to make",
placeholder="e.g., Make it more casual, add more call-to-actions, focus on product benefits")
submitted = st.form_submit_button("Regenerate with Changes")
if submitted and changes:
with st.spinner("Regenerating script..."):
# Generate a new script with the changes
new_script = generate_youtube_script_with_changes(
params["target_audience"],
params["main_points"],
params["tone_style"],
params["use_case"],
params["script_structure"],
params["include_hook"],
params["include_cta"],
params["include_engagement"],
params["include_timestamps"],
params["include_visual_cues"],
params["engagement_hooks"],
params["community_interactions"],
changes,
params["language"]
)
if new_script:
# Update the stored script
st.session_state.generated_script = new_script
st.session_state.show_regenerate_popover = False
st.rerun()
else:
st.error("Failed to regenerate script. Please try again.")
# Additional export options
if st.checkbox("Show additional export options"):
col1, col2 = st.columns(2)
with col1:
if st.button("Copy to Clipboard"):
st.code(script)
st.success("Script copied to clipboard!")
with col2:
if st.button("Save to Local File"):
# This is a placeholder - actual file saving would require additional backend functionality
st.info("This feature would save the file locally on your device.")

View File

@@ -0,0 +1,622 @@
"""
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.")

View File

@@ -0,0 +1,452 @@
"""
YouTube Title Generator Module
This module provides functionality for generating YouTube video titles.
"""
import streamlit as st
import time
import logging
from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('youtube_title_generator')
def analyze_title(title):
"""Analyze a YouTube title for SEO and clickbait."""
logger.info(f"Analyzing title: '{title}'")
# Character count
char_count = len(title)
optimal_length = 50 <= char_count <= 60
logger.info(f"Character count: {char_count}, Optimal length: {optimal_length}")
# Clickbait detection. TBD: Use AI to detect clickbait.
clickbait_phrases = [
"shocking", "you won't believe", "gone wrong", "gone sexual",
"free v-bucks", "free robux", "100%", "gone viral", "viral",
"you need to see this", "wait till the end", "at 3am", "3am",
"don't watch this", "watch till the end", "gone too far",
"insane", "unbelievable", "mind-blowing", "life-changing",
"secret", "hidden", "revealed", "exposed", "leaked",
"never before seen", "first time ever", "world's first",
"no one knows", "experts hate this", "doctors hate this",
"this will change your life", "this will blow your mind",
"you've been doing it wrong", "the truth about", "the real reason",
"what they don't want you to know", "what they're hiding",
"what they don't tell you", "what you need to know",
"what you should know", "what you must know", "what you must see",
"what you must watch", "what you must do", "what you must have",
"what you must buy", "what you must try", "what you must avoid",
"what you must stop doing", "what you must start doing",
"what you must change", "what you must learn", "what you must understand",
"what you must realize", "what you must accept", "what you must believe",
"what you must know about", "what you must see about", "what you must watch about",
"what you must do about", "what you must have about", "what you must buy about",
"what you must try about", "what you must avoid about", "what you must stop doing about",
"what you must start doing about", "what you must change about", "what you must learn about",
"what you must understand about", "what you must realize about", "what you must accept about",
"what you must believe about", "what you must know about", "what you must see about",
"what you must watch about", "what you must do about", "what you must have about",
"what you must buy about", "what you must try about", "what you must avoid about",
"what you must stop doing about", "what you must start doing about", "what you must change about",
"what you must learn about", "what you must understand about", "what you must realize about",
"what you must accept about", "what you must believe about"
]
clickbait_score = 0
detected_phrases = []
for phrase in clickbait_phrases:
if phrase.lower() in title.lower():
clickbait_score += 1
detected_phrases.append(phrase)
is_clickbait = clickbait_score > 0
logger.info(f"Clickbait detection: score={clickbait_score}, is_clickbait={is_clickbait}")
if detected_phrases:
logger.info(f"Detected clickbait phrases: {', '.join(detected_phrases)}")
# SEO elements
has_number = any(char.isdigit() for char in title)
has_question = "?" in title
has_colon = ":" in title
has_brackets = "[" in title or "]" in title or "(" in title or ")" in title
logger.info(f"SEO elements: has_number={has_number}, has_question={has_question}, has_colon={has_colon}, has_brackets={has_brackets}")
# Calculate SEO score
seo_score = 0
if optimal_length:
seo_score += 3
if has_number:
seo_score += 1
if has_question:
seo_score += 1
if has_colon:
seo_score += 1
if has_brackets:
seo_score += 1
if not is_clickbait:
seo_score += 2
logger.info(f"Final SEO score: {seo_score}/10")
return {
"char_count": char_count,
"optimal_length": optimal_length,
"is_clickbait": is_clickbait,
"clickbait_score": clickbait_score,
"seo_score": seo_score,
"has_number": has_number,
"has_question": has_question,
"has_colon": has_colon,
"has_brackets": has_brackets
}
def generate_youtube_title(target_audience, main_points, tone_style, use_case, num_titles=5, progress_bar=None):
""" Generate youtube title generator """
logger.info(f"Starting title generation with parameters: target_audience='{target_audience}', main_points='{main_points}', tone_style='{tone_style}', use_case='{use_case}', num_titles={num_titles}")
# Create a custom system prompt that doesn't include blog-specific instructions
system_prompt = """You are a YouTube title expert specializing in creating engaging, clickable video titles.
Your task is to generate YouTube video titles based on the provided information.
Focus ONLY on creating titles that are optimized for YouTube.
Return ONLY the titles, one per line, without any numbering or additional text."""
prompt = f"""
**Instructions:**
Please generate {num_titles} YouTube title options for a video about **{main_points}** based on the following information:
**Target Audience:** {target_audience}
**Tone and Style:** {tone_style}
**Use Case:** {use_case}
**Specific Instructions:**
* Make the titles catchy and attention-grabbing.
* Use relevant keywords to improve SEO.
* Tailor the language and tone to the target audience.
* Ensure the title reflects the content and use case of the video.
* Return ONLY the titles, one per line, without any numbering or additional text.
"""
logger.info("Generated prompt for title generation")
logger.debug(f"Prompt: {prompt}")
logger.debug(f"System prompt: {system_prompt}")
try:
# Update progress bar if provided
if progress_bar:
progress_bar.progress(30)
progress_bar.text("Analyzing your content and target audience...")
logger.info("Progress bar updated: 30% - Analyzing content and target audience")
# Simulate some processing time to show progress
time.sleep(1)
if progress_bar:
progress_bar.progress(60)
progress_bar.text("Generating creative title options...")
logger.info("Progress bar updated: 60% - Generating creative title options")
# Get the response from the language model with custom system prompt
logger.info("Calling LLM for title generation with custom system prompt")
start_time = time.time()
response = llm_text_gen(prompt, system_prompt=system_prompt)
end_time = time.time()
logger.info(f"LLM response received in {end_time - start_time:.2f} seconds")
logger.debug(f"Raw LLM response: {response}")
if progress_bar:
progress_bar.progress(90)
progress_bar.text("Processing and formatting titles...")
logger.info("Progress bar updated: 90% - Processing and formatting titles")
# Split the response into individual titles
titles = [title.strip() for title in response.split('\n') if title.strip()]
logger.info(f"Generated {len(titles)} titles")
for i, title in enumerate(titles, 1):
logger.info(f"Title {i}: '{title}'")
if progress_bar:
progress_bar.progress(100)
progress_bar.text("Titles generated successfully!")
logger.info("Progress bar updated: 100% - Titles generated successfully")
return titles
except Exception as err:
logger.error(f"Error generating titles: {err}", exc_info=True)
if progress_bar:
progress_bar.progress(100)
progress_bar.text("Error generating titles. Please try again.")
logger.info("Progress bar updated: 100% - Error generating titles")
st.error(f"Error: Failed to get response from LLM: {err}")
return None
def write_yt_title():
"""Create a user interface for YouTube Title Generator."""
logger.info("Initializing YouTube Title Generator UI")
st.write("Generate engaging YouTube video titles that drive clicks and views.")
# Initialize session state for generated titles if it doesn't exist
if "generated_titles" not in st.session_state:
st.session_state.generated_titles = None
# Main points input (full width)
main_points = st.text_area("Main Points/Keywords (comma-separated)",
placeholder="e.g., cooking tips, healthy recipes, quick meals")
# Create columns for the other inputs
col1, col2, col3, col4 = st.columns(4)
with col1:
tone_style = st.selectbox("Tone/Style",
["Professional", "Casual", "Humorous", "Educational", "Entertaining", "Inspirational"])
with col2:
target_audience = st.text_input("Target Audience",
placeholder="e.g., beginners, professionals, parents")
with col3:
use_case = st.selectbox("Use Case",
["How-to/Tutorial", "Vlog", "Review", "Educational", "Entertainment", "News"])
with col4:
num_titles = st.number_input("Number of Titles",
min_value=1,
max_value=20,
value=5,
step=1)
if st.button("Generate Titles"):
logger.info("Generate Titles button clicked")
logger.info(f"User inputs: main_points='{main_points}', tone_style='{tone_style}', target_audience='{target_audience}', use_case='{use_case}', num_titles={num_titles}")
if not main_points:
logger.warning("No main points provided")
st.error("Please enter main points/keywords.")
return
# Create a progress bar
progress_bar = st.progress(0)
progress_bar.text("Initializing title generation...")
logger.info("Created progress bar for title generation")
# Generate titles with progress updates
logger.info("Calling generate_youtube_title function")
titles = generate_youtube_title(main_points, tone_style, target_audience, use_case, num_titles, progress_bar)
# Clear the progress bar after a short delay
time.sleep(1)
progress_bar.empty()
logger.info("Cleared progress bar")
if titles:
logger.info(f"Successfully generated {len(titles)} titles")
# Store titles in session state for persistence
st.session_state.generated_titles = titles
# Display titles section
st.markdown("""
<div style='background-color: #f0f2f6; padding: 20px; border-radius: 10px; margin-bottom: 20px;'>
<h2 style='color: #FF0000; text-align: center;'>Generated YouTube Titles</h2>
<p style='text-align: center;'>Click on a title to see detailed analysis and copy options</p>
</div>
""", unsafe_allow_html=True)
# Display titles with analysis
for i, title in enumerate(titles, 1):
logger.info(f"Analyzing title {i}: '{title}'")
# Create a more visually appealing expander
with st.expander(f"Title {i}: {title}", expanded=False):
# Add a divider for better visual separation
st.markdown("---")
# Title display with better formatting
st.markdown(f"""
<div style='background-color: #f8f9fa; padding: 15px; border-radius: 5px; border-left: 5px solid #FF0000;'>
<h3 style='margin: 0;'>{title}</h3>
</div>
""", unsafe_allow_html=True)
# Analysis section
st.markdown("### Analysis")
analysis = analyze_title(title)
# Create columns for analysis metrics
col1, col2 = st.columns(2)
with col1:
# Character count
st.markdown("#### Character Count")
st.write(f"**{analysis['char_count']}** characters")
if analysis['optimal_length']:
st.success("✅ Optimal length (50-60 characters)")
else:
st.warning("⚠️ Not optimal length (should be 50-60 characters)")
# Clickbait detection
st.markdown("#### Clickbait Detection")
if analysis['is_clickbait']:
st.error(f"⚠️ Possible clickbait detected (score: {analysis['clickbait_score']})")
else:
st.success("✅ No clickbait detected")
with col2:
# SEO score
st.markdown("#### SEO Score")
score_color = "#28a745" if analysis['seo_score'] >= 7 else "#ffc107" if analysis['seo_score'] >= 5 else "#dc3545"
st.markdown(f"<h2 style='color: {score_color};'>{analysis['seo_score']}/10</h2>", unsafe_allow_html=True)
if analysis['seo_score'] >= 7:
st.success("✅ Good SEO score")
elif analysis['seo_score'] >= 5:
st.warning("⚠️ Moderate SEO score")
else:
st.error("❌ Low SEO score")
# SEO elements
st.markdown("#### SEO Elements")
elements = []
if analysis['has_number']:
elements.append("✅ Contains numbers")
if analysis['has_question']:
elements.append("✅ Contains question mark")
if analysis['has_colon']:
elements.append("✅ Contains colon")
if analysis['has_brackets']:
elements.append("✅ Contains brackets/parentheses")
for element in elements:
st.write(element)
# Copy functionality using session state
st.markdown("### Copy Title")
st.code(title, language="text")
# Use a different approach for copy functionality
copy_key = f"copy_{i}"
if st.button(f"Copy Title {i}", key=copy_key):
# Use JavaScript to copy to clipboard
escaped_title = title.replace('"', '\\"')
st.markdown(
f"""
<script>
navigator.clipboard.writeText("{escaped_title}");
</script>
""",
unsafe_allow_html=True
)
st.success(f"✅ Title {i} copied to clipboard!")
else:
logger.error("Failed to generate titles")
st.error("Failed to generate titles. Please try again.")
# Display previously generated titles if they exist in session state
elif st.session_state.generated_titles:
titles = st.session_state.generated_titles
# Display titles section
st.markdown("""
<div style='background-color: #f0f2f6; padding: 20px; border-radius: 10px; margin-bottom: 20px;'>
<h2 style='color: #FF0000; text-align: center;'>Generated YouTube Titles</h2>
<p style='text-align: center;'>Click on a title to see detailed analysis and copy options</p>
</div>
""", unsafe_allow_html=True)
# Display titles with analysis
for i, title in enumerate(titles, 1):
logger.info(f"Analyzing title {i}: '{title}'")
# Create a more visually appealing expander
with st.expander(f"Title {i}: {title}", expanded=False):
# Add a divider for better visual separation
st.markdown("---")
# Title display with better formatting
st.markdown(f"""
<div style='background-color: #f8f9fa; padding: 15px; border-radius: 5px; border-left: 5px solid #FF0000;'>
<h3 style='margin: 0;'>{title}</h3>
</div>
""", unsafe_allow_html=True)
# Analysis section
st.markdown("### Analysis")
analysis = analyze_title(title)
# Create columns for analysis metrics
col1, col2 = st.columns(2)
with col1:
# Character count
st.markdown("#### Character Count")
st.write(f"**{analysis['char_count']}** characters")
if analysis['optimal_length']:
st.success("✅ Optimal length (50-60 characters)")
else:
st.warning("⚠️ Not optimal length (should be 50-60 characters)")
# Clickbait detection
st.markdown("#### Clickbait Detection")
if analysis['is_clickbait']:
st.error(f"⚠️ Possible clickbait detected (score: {analysis['clickbait_score']})")
else:
st.success("✅ No clickbait detected")
with col2:
# SEO score
st.markdown("#### SEO Score")
score_color = "#28a745" if analysis['seo_score'] >= 7 else "#ffc107" if analysis['seo_score'] >= 5 else "#dc3545"
st.markdown(f"<h2 style='color: {score_color};'>{analysis['seo_score']}/10</h2>", unsafe_allow_html=True)
if analysis['seo_score'] >= 7:
st.success("✅ Good SEO score")
elif analysis['seo_score'] >= 5:
st.warning("⚠️ Moderate SEO score")
else:
st.error("❌ Low SEO score")
# SEO elements
st.markdown("#### SEO Elements")
elements = []
if analysis['has_number']:
elements.append("✅ Contains numbers")
if analysis['has_question']:
elements.append("✅ Contains question mark")
if analysis['has_colon']:
elements.append("✅ Contains colon")
if analysis['has_brackets']:
elements.append("✅ Contains brackets/parentheses")
for element in elements:
st.write(element)
# Copy functionality using session state
st.markdown("### Copy Title")
st.code(title, language="text")
# Use a different approach for copy functionality
copy_key = f"copy_{i}"
if st.button(f"Copy Title {i}", key=copy_key):
# Use JavaScript to copy to clipboard
escaped_title = title.replace('"', '\\"')
st.markdown(
f"""
<script>
navigator.clipboard.writeText("{escaped_title}");
</script>
""",
unsafe_allow_html=True
)
st.success(f"✅ Title {i} copied to clipboard!")

View File

@@ -0,0 +1,242 @@
"""
YouTube AI Writer
This module provides a comprehensive suite of tools for generating YouTube content.
"""
import streamlit as st
import importlib
import sys
import os
from pathlib import Path
from .modules.title_generator import write_yt_title
from .modules.description_generator import write_yt_description
from .modules.script_generator import write_yt_script
from .modules.thumbnail_generator import write_yt_thumbnail
from .modules.end_screen_generator import write_yt_end_screen
def youtube_main_menu():
"""Main function for the YouTube AI Writer."""
# Initialize session state for selected tool if it doesn't exist
if "selected_tool" not in st.session_state:
st.session_state.selected_tool = None
# Define the YouTube tools with their details
youtube_tools = [
# Content Creation Tools
{
"name": "YT Title Generator",
"icon": "📝",
"description": "Create engaging YouTube video titles that drive clicks and views.",
"color": "#FF0000", # YouTube red
"category": "Content Creation",
"function": write_yt_title,
"status": "active"
},
{
"name": "YT Description Generator",
"icon": "📄",
"description": "Generate SEO-optimized descriptions for your YouTube videos.",
"color": "#FF0000", # YouTube red
"category": "Content Creation",
"function": write_yt_description,
"status": "active"
},
{
"name": "YT Script Generator",
"icon": "🎬",
"description": "Create professional YouTube scripts with optimized structures for engagement.",
"color": "#FF0000", # YouTube red
"category": "Content Creation",
"function": write_yt_script,
"status": "active"
},
# Optimization Tools
{
"name": "Thumbnail Generator",
"icon": "🎨",
"description": "Create engaging thumbnail ideas and descriptions with color scheme suggestions based on your brand.",
"color": "#FF0000", # YouTube red
"category": "Optimization",
"function": write_yt_thumbnail,
"status": "active"
},
{
"name": "Tags Generator",
"icon": "🏷️",
"description": "Generate optimized tags for your videos with trending tag suggestions to improve discoverability.",
"color": "#CC0000", # Darker red for coming soon
"category": "Optimization",
"function": None,
"status": "coming_soon"
},
# Engagement Tools (Coming Soon)
{
"name": "End Screen Generator",
"icon": "🎬",
"description": "Create effective end screen content and CTAs with template suggestions based on video type.",
"color": "#FF0000", # YouTube red
"category": "Engagement",
"function": write_yt_end_screen,
"status": "active"
},
{
"name": "Playlist Description Generator",
"icon": "📚",
"description": "Generate SEO-optimized descriptions for your playlists with organization suggestions.",
"color": "#CC0000", # Darker red for coming soon
"category": "Engagement",
"function": None,
"status": "coming_soon"
},
# Future Tools (Coming Soon)
{
"name": "Analytics Insights",
"icon": "📊",
"description": "Get AI-powered insights and recommendations based on your channel analytics.",
"color": "#990000", # Even darker red for future
"category": "Future Tools",
"function": None,
"status": "future"
},
{
"name": "Channel Trailer Generator",
"icon": "🎥",
"description": "Create compelling channel trailers that convert visitors into subscribers.",
"color": "#990000", # Even darker red for future
"category": "Future Tools",
"function": None,
"status": "future"
},
{
"name": "Video Series Planner",
"icon": "📅",
"description": "Plan and organize your video series with content calendars and topic ideas.",
"color": "#990000", # Even darker red for future
"category": "Future Tools",
"function": None,
"status": "future"
},
{
"name": "Shorts Script Generator",
"icon": "📱",
"description": "Create engaging scripts optimized for YouTube Shorts format.",
"color": "#990000", # Even darker red for future
"category": "Future Tools",
"function": None,
"status": "future"
},
{
"name": "Community Post Generator",
"icon": "💬",
"description": "Generate engaging community posts to keep your audience active between videos.",
"color": "#990000", # Even darker red for future
"category": "Future Tools",
"function": None,
"status": "future"
}
]
# Create a container for the dashboard
dashboard_container = st.container()
# Create a container for the tool input section
tool_container = st.container()
# If a tool is selected, show its input section
if st.session_state.selected_tool is not None:
with tool_container:
# Display the selected tool's input section
st.markdown("---")
st.markdown(f"# {st.session_state.selected_tool['icon']} {st.session_state.selected_tool['name']}")
# Add a back button
if st.button("← Back to Dashboard", key="back_to_dashboard"):
# Clear the selected tool from session state
st.session_state.selected_tool = None
st.rerun()
# Call the function for the selected tool
if st.session_state.selected_tool["function"]:
# Directly call the function instead of using it as a reference
st.session_state.selected_tool["function"]()
else:
# Display coming soon or future tool information
st.info(f"**{st.session_state.selected_tool['status'].replace('_', ' ').title()}!**")
st.write(st.session_state.selected_tool["description"])
st.image(f"https://via.placeholder.com/600x300?text={st.session_state.selected_tool['name']}+Coming+Soon", use_column_width=True)
else:
with dashboard_container:
# Display the dashboard
# Header
st.markdown("""
<div style='background-color: #f0f2f6; padding: 20px; border-radius: 10px; margin-bottom: 20px;'>
<h1 style='color: #FF0000; text-align: center;'>🎥 YouTube AI Writer</h1>
<p style='text-align: center;'>Generate professional YouTube content with AI-powered tools</p>
</div>
""", unsafe_allow_html=True)
# Introduction
st.markdown("""
## Welcome to the YouTube AI Writer Suite
This dashboard provides access to a variety of tools for creating and optimizing YouTube content.
Select a tool below to get started with generating professional content for your channel.
### How to Use This Dashboard
1. Browse the available tools below
2. Click on a tool card to access its specific functionality
3. Fill in the required information
4. Generate high-quality content for your YouTube channel
""")
# Group tools by category
categories = {}
for tool in youtube_tools:
category = tool["category"]
if category not in categories:
categories[category] = []
categories[category].append(tool)
# Display tools by category
for category, tools in categories.items():
st.markdown(f"## {category}")
# Create a 3-column layout for the tool cards
cols = st.columns(3)
# Display the tool cards
for i, tool in enumerate(tools):
# Determine which column to use
col = cols[i % 3]
with col:
# Create a card for each tool
status_badge = ""
if tool["status"] == "coming_soon":
status_badge = "<span style='background-color: #FFA500; color: white; padding: 2px 8px; border-radius: 10px; font-size: 0.8em;'>Coming Soon</span>"
elif tool["status"] == "future":
status_badge = "<span style='background-color: #808080; color: white; padding: 2px 8px; border-radius: 10px; font-size: 0.8em;'>Future</span>"
st.markdown(f"""
<div style='background-color: {tool["color"]}; padding: 20px; border-radius: 10px; margin-bottom: 20px; color: white;'>
<h2 style='color: white;'>{tool["icon"]} {tool["name"]} {status_badge}</h2>
<p>{tool["description"]}</p>
</div>
""", unsafe_allow_html=True)
# Add a button to access the tool
if st.button(f"Use {tool['name']}", key=f"btn_{tool['name']}"):
# Store the selected tool in session state
st.session_state.selected_tool = tool
st.rerun()
if __name__ == "__main__":
youtube_ai_writer()

View File

@@ -0,0 +1,377 @@
import os
from PIL import Image
from io import BytesIO
import PIL
import streamlit as st
from google import genai
from google.genai import types
import logging
import datetime
import base64
import random
import time
from .save_image import save_generated_image
# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger('gemini_image_generator')
# With image generation in Gemini, your imagination is the limit.
# If what you see doesn't quite match what you had in mind, try adding more details to the prompt.
# The more specific you are, the better Gemini can create images that reflect your vision.
# Generate images using Gemini
# Gemini 2.0 Flash Experimental supports the ability to output text and inline images.
# This lets you use Gemini to conversationally edit images or generate outputs with interwoven text (for example, generating a blog post with text and images in a single turn).
# Note: Make sure to include responseModalities: ["Text", "Image"] in your generation configuration for text and image output with gemini-2.0-flash-exp-image-generation. Image only is not allowed.
class AIPromptGenerator:
"""
Generates enhanced AI image prompts based on user keywords,
following the guidelines of the Imagen documentation.
"""
def __init__(self):
self.photography_styles = ["photo", "photograph"]
self.art_styles = ["painting", "sketch", "drawing", "illustration", "digital art", "render"]
self.art_techniques = ["technical pencil drawing", "charcoal drawing", "color pencil drawing", "pastel painting", "digital art", "art deco (poster)", "impressionist painting", "renaissance painting", "pop art"]
self.camera_proximity = ["close-up", "zoomed out", "taken from far away"]
self.camera_position = ["aerial", "from below"]
self.lighting = ["natural lighting", "dramatic lighting", "warm lighting", "cold lighting", "studio lighting", "golden hour lighting"]
self.camera_settings = ["motion blur", "soft focus", "bokeh", "portrait"]
self.lens_types = ["35mm lens", "50mm lens", "fisheye lens", "wide angle lens", "macro lens", "telephoto lens"]
self.film_types = ["black and white film", "polaroid"]
self.materials = ["made of cheese", "made of paper", "made of neon tubes", "metallic", "glass", "wooden", "stone"]
self.shapes = ["in the shape of a bird", "angular", "curved", "geometric"]
self.quality_modifiers_general = ["high-quality", "beautiful", "stylized", "detailed", "epic", "grand"]
self.quality_modifiers_photo = ["4K", "HDR", "studio photo", "professional photo", "photorealistic"]
self.quality_modifiers_art = ["by a professional artist", "intricate details", "masterpiece"]
self.aspect_ratios = ["1:1 aspect ratio", "4:3 aspect ratio", "3:4 aspect ratio", "16:9 aspect ratio", "9:16 aspect ratio"]
self.photorealistic_modifiers = {
"portraits": ["prime lens", "zoom lens", "24-35mm", "black and white film", "film noir", "shallow depth of field", "duotone (mention two colors)"],
"objects": ["macro lens", "60-105mm", "high detail", "precise focusing", "controlled lighting"],
"motion": ["telephoto zoom lens", "100-400mm", "fast shutter speed", "action shot", "movement tracking"],
"wide-angle": ["wide-angle lens", "10-24mm", "long exposure", "sharp focus", "smooth water or clouds", "astro photography"]
}
def generate_prompt(self, keywords):
"""
Generates an enhanced AI image prompt based on user-provided keywords.
Args:
keywords (list): A list of keywords describing the desired image.
Returns:
str: An enhanced AI image prompt.
"""
if not keywords:
return "A beautiful image."
prompt_parts = []
subject = " ".join(keywords)
prompt_parts.append(subject)
# Add context and background (optional)
context_options = ["in a detailed background", "outdoors", "indoors", "in a studio", "with a blurred background"]
if random.random() < 0.6: # Add context with a probability
prompt_parts.append(random.choice(context_options))
# Add style (optional)
style_options = self.photography_styles + [f"{art} of" for art in self.art_styles]
if random.random() < 0.7:
prompt_parts.insert(0, random.choice(style_options))
if prompt_parts[0].startswith("painting of") or prompt_parts[0].startswith("sketch of") or prompt_parts[0].startswith("drawing of"):
if random.random() < 0.5:
prompt_parts.append(f"in the style of {random.choice(self.art_techniques)}")
# Add photography modifiers (if photography style is chosen)
if any(style in prompt_parts[0] for style in self.photography_styles):
if random.random() < 0.4:
prompt_parts.append(random.choice(self.camera_proximity))
if random.random() < 0.3:
prompt_parts.append(random.choice(self.camera_position))
if random.random() < 0.5:
prompt_parts.append(random.choice(self.lighting))
if random.random() < 0.3:
prompt_parts.append(random.choice(self.camera_settings))
if random.random() < 0.2:
prompt_parts.append(random.choice(self.lens_types))
if random.random() < 0.1:
prompt_parts.append(random.choice(self.film_types))
# Add shapes and materials (optional)
if random.random() < 0.3:
prompt_parts.append(random.choice(self.materials))
if random.random() < 0.2:
prompt_parts.append(random.choice(self.shapes))
# Add quality modifiers (optional)
if random.random() < 0.6:
quality_options = self.quality_modifiers_general
if any(style in prompt_parts[0] for style in self.photography_styles):
quality_options += self.quality_modifiers_photo
else:
quality_options += self.quality_modifiers_art
prompt_parts.append(random.choice(list(set(quality_options)))) # Avoid duplicates
# Add aspect ratio (optional)
if random.random() < 0.2:
prompt_parts.append(random.choice(self.aspect_ratios))
return ", ".join(prompt_parts)
def generate_photorealistic_prompt(self, keywords, focus=""):
"""
Generates an enhanced AI image prompt specifically for photorealistic images.
Args:
keywords (list): A list of keywords describing the desired image.
focus (str, optional): The focus of the photorealistic image (e.g., "portraits", "objects", "motion", "wide-angle"). Defaults to "".
Returns:
str: An enhanced photorealistic AI image prompt.
"""
if not keywords:
return "A photorealistic image."
prompt_parts = ["A photo of", "photorealistic"]
prompt_parts.append(" ".join(keywords))
if focus and focus in self.photorealistic_modifiers:
modifiers = self.photorealistic_modifiers[focus]
if modifiers:
num_modifiers = random.randint(1, min(3, len(modifiers)))
selected_modifiers = random.sample(modifiers, num_modifiers)
prompt_parts.extend(selected_modifiers)
# Add general quality modifiers
if random.random() < 0.5:
prompt_parts.append(random.choice(self.quality_modifiers_photo))
# Add lighting
if random.random() < 0.4:
prompt_parts.append(random.choice(self.lighting))
return ", ".join(prompt_parts)
def generate_gemini_image(prompt, keywords=None, style=None, focus=None, enhance_prompt=True, max_retries=3, initial_retry_delay=2):
"""
Generate images using Gemini
Depending on the prompt and context, Gemini will generate content in different modes (text to image, text to image and text, etc.).
Here are some examples:
1). Text to image
Example prompt: "Generate an image of the Eiffel tower with fireworks in the background."
2). Text to image(s) and text (interleaved)
Example prompt: "Generate an illustrated recipe for a paella."
Image generation may not always trigger:
- The model may output text only. Try asking for image outputs explicitly (e.g. "generate an image", "provide images as you go along", "update the image").
- The model may stop generating partway through. Try again or try a different prompt.
Args:
prompt (str): The prompt to generate the image from.
keywords (list, optional): Keywords to enhance the prompt. Defaults to None.
style (str, optional): The style of the image. Defaults to None.
focus (str, optional): The focus of the image (e.g., "portraits", "objects", "motion", "wide-angle"). Defaults to None.
enhance_prompt (bool, optional): Whether to enhance the prompt using AIPromptGenerator. Defaults to True.
max_retries (int, optional): Maximum number of retry attempts for handling 503 errors. Defaults to 3.
initial_retry_delay (int, optional): Initial delay in seconds before retrying. Defaults to 2.
Returns:
str: The path to the generated image.
"""
logger.info(f"Generating image with prompt: '{prompt[:100]}...'")
# Enhance the prompt if requested
if enhance_prompt and keywords:
prompt_generator = AIPromptGenerator()
if style == "photorealistic" and focus:
logger.info(f"Generating photorealistic prompt with focus: {focus}")
enhanced_prompt = prompt_generator.generate_photorealistic_prompt(keywords, focus)
else:
logger.info("Generating enhanced prompt")
enhanced_prompt = prompt_generator.generate_prompt(keywords)
# Combine the enhanced prompt with the original prompt
prompt = f"{prompt}\n\nEnhanced prompt: {enhanced_prompt}"
logger.info(f"Final prompt: '{prompt[:100]}...'")
retry_count = 0
retry_delay = initial_retry_delay
while retry_count <= max_retries:
try:
client = genai.Client(api_key=os.getenv("GEMINI_API_KEY"))
contents = (prompt)
logger.info("Sending request to Gemini API")
response = client.models.generate_content(
model="gemini-2.0-flash-exp-image-generation",
contents=contents,
config=types.GenerateContentConfig(
response_modalities=['Text', 'Image']
)
)
logger.info("Received response from Gemini API")
img_name = None
for part in response.candidates[0].content.parts:
if part.text is not None:
logger.info(f"Received text response: '{part.text[:100]}...'")
print(part.text)
elif part.inline_data is not None:
logger.info("Received image data from Gemini")
image = Image.open(BytesIO((part.inline_data.data)))
image.show()
if part.text is not None:
img_name = f'{part.text}-gemini-native-image.png'
else:
img_name = f'gemini-native-image-{datetime.datetime.now().strftime("%Y%m%d-%H%M%S")}.png'
try:
logger.info(f"Saving image to: {img_name}")
image.save(img_name)
# Create a dictionary with the expected format for save_generated_image
img_response = {
"artifacts": [
{
"base64": base64.b64encode(open(img_name, "rb").read()).decode('utf-8')
}
]
}
# Call save_generated_image with the correct format
save_generated_image(img_response)
except Exception as err:
logger.error(f"Failed to save image: {err}")
st.error(f"Failed to save image: {err}")
logger.info(f"Image generation completed. Image name: {img_name}")
return img_name
except Exception as err:
error_message = str(err)
logger.error(f"Error in generate_gemini_image: {err}")
# Check if this is a 503 UNAVAILABLE error
if "503 UNAVAILABLE" in error_message and retry_count < max_retries:
retry_count += 1
logger.info(f"Model is overloaded. Retrying in {retry_delay} seconds (attempt {retry_count}/{max_retries})")
st.warning(f"The image generation service is currently busy. Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
# Exponential backoff
retry_delay *= 2
else:
st.error(f"Error generating image: {err}")
return None
# If we've exhausted all retries
st.error("The image generation service is currently unavailable. Please try again later.")
return None
def edit_image(image_path, prompt, max_retries=3, initial_retry_delay=2):
"""
- Image editing (text and image to image)
Example prompt: "Edit this image to make it look like a cartoon"
Example prompt: [image of a cat] + [image of a pillow] + "Create a cross stitch of my cat on this pillow."
- Multi-turn image editing (chat)
Example prompts: [upload an image of a blue car.] "Turn this car into a convertible." "Now change the color to yellow."
Image editing with Gemini
To perform image editing, add an image as input.
The following example demonstrats uploading base64 encoded images.
For multiple images and larger payloads, check the image input section.
Args:
image_path (str): The path to the image to edit.
prompt (str): The prompt to edit the image with.
max_retries (int, optional): Maximum number of retry attempts for handling 503 errors. Defaults to 3.
initial_retry_delay (int, optional): Initial delay in seconds before retrying. Defaults to 2.
Returns:
str: The path to the edited image.
"""
import PIL.Image
image = PIL.Image.open(image_path)
retry_count = 0
retry_delay = initial_retry_delay
while retry_count <= max_retries:
try:
client = genai.Client()
text_input = (prompt)
logger.info("Sending request to Gemini API for image editing")
response = client.models.generate_content(
model="gemini-2.0-flash-exp-image-generation",
contents=[text_input, image],
config=types.GenerateContentConfig(
response_modalities=['Text', 'Image']
)
)
logger.info("Received response from Gemini API for image editing")
edited_img_name = None
for part in response.candidates[0].content.parts:
if part.text is not None:
logger.info(f"Received text response: '{part.text[:100]}...'")
st.write(part.text)
elif part.inline_data is not None:
logger.info("Received edited image data from Gemini")
edited_image = Image.open(BytesIO(part.inline_data.data))
edited_image.show()
# Save the edited image
edited_img_name = f'edited-{os.path.basename(image_path)}'
try:
logger.info(f"Saving edited image to: {edited_img_name}")
edited_image.save(edited_img_name)
# Create a dictionary with the expected format for save_generated_image
img_response = {
"artifacts": [
{
"base64": base64.b64encode(open(edited_img_name, "rb").read()).decode('utf-8')
}
]
}
# Call save_generated_image with the correct format
save_generated_image(img_response)
except Exception as err:
logger.error(f"Failed to save edited image: {err}")
st.error(f"Failed to save edited image: {err}")
logger.info(f"Image editing completed. Edited image name: {edited_img_name}")
return edited_img_name
except Exception as err:
error_message = str(err)
logger.error(f"Error in edit_image: {err}")
# Check if this is a 503 UNAVAILABLE error
if "503 UNAVAILABLE" in error_message and retry_count < max_retries:
retry_count += 1
logger.info(f"Model is overloaded. Retrying in {retry_delay} seconds (attempt {retry_count}/{max_retries})")
st.warning(f"The image editing service is currently busy. Retrying in {retry_delay} seconds...")
time.sleep(retry_delay)
# Exponential backoff
retry_delay *= 2
else:
st.error(f"Error editing image: {err}")
return None
# If we've exhausted all retries
st.error("The image editing service is currently unavailable. Please try again later.")
return None

View File

@@ -25,7 +25,7 @@ logger.add(sys.stdout,
from .gen_dali3_images import generate_dalle3_images
from .gen_stabl_diff_img import generate_stable_diffusion_image
from ..text_generation.main_text_generation import llm_text_gen
from .gen_gemini_images import generate_gemini_image
def generate_image(user_prompt):
"""
@@ -44,7 +44,7 @@ def generate_image(user_prompt):
--> user (str): A unique identifier representing your end-user, which will help OpenAI to monitor and detect abuse.
"""
# FIXME: Need to remove default value to match sidebar input.
image_engine = 'Stability-AI'
image_engine = 'Gemini-AI'
image_stored_at = None
if user_prompt:
@@ -57,6 +57,9 @@ def generate_image(user_prompt):
logger.info(f"Calling Stable diffusion text-to-image with prompt: \n{img_prompt}")
print("\n\n")
image_stored_at = generate_stable_diffusion_image(img_prompt)
elif 'Gemini-AI' in image_engine:
logger.info(f"Calling Gemini text-to-image with prompt: \n{img_prompt}")
image_stored_at = generate_gemini_image(img_prompt)
return image_stored_at
except Exception as err:
logger.error(f"Failed to generate Image: {err}")

View File

@@ -14,7 +14,7 @@ from lib.ai_writers.facebook_ai_writer import facebook_post_writer
from lib.ai_writers.linkedin_ai_writer import linked_post_writer
from lib.ai_writers.twitter_ai_writer import tweet_writer
from lib.ai_writers.insta_ai_writer import insta_writer
from lib.ai_writers.youtube_ai_writer import write_yt_title, write_yt_description, write_yt_script
from lib.ai_writers.youtube_writers.youtube_ai_writer import youtube_main_menu
from lib.ai_writers.web_url_ai_writer import blog_from_url
from lib.ai_writers.image_ai_writer import blog_from_image
from lib.ai_writers.ai_essay_writer import ai_essay_generator
@@ -485,7 +485,7 @@ def ai_social_writer():
("linkedin", "LinkedIn"),
("twitter", "Twitter"),
("instagram", "Instagram"),
("youtube", "YouTube") # Add YouTube
("youtube", "YouTube")
]
# Selectbox for choosing a platform
@@ -498,13 +498,5 @@ def ai_social_writer():
tweet_writer()
elif "instagram" in selected_platform:
insta_writer()
# elif "youtube" in selected_platform:
# options = ["Write YT Description", "Write YT Title", "Write YT Script"]
# selected_option = st.radio("", options)
#
# if selected_option == "Write YT Description":
# write_yt_description()
# elif selected_option == "Write YT Title":
# write_yt_title()
# elif selected_option == "Write YT Script":
# write_yt_script()
elif "youtube" in selected_platform:
youtube_main_menu()