From 8ac2095ac49d015877ac8b931687ca253d2ba2e1 Mon Sep 17 00:00:00 2001 From: ajaysi Date: Thu, 10 Apr 2025 11:42:34 +0530 Subject: [PATCH] Youtube AI Writer Tools like yt_shorts_scripts, tags_generator, community_post_generator, shorts_script_generator --- README.md | 1 - alwrity.py | 32 +- .../modules/README_yt_shorts_scripts.md | 273 ++++++++ .../modules/community_post_generator.py | 591 ++++++++++++++++++ .../modules/shorts_script_generator.py | 304 +++++++++ .../youtube_writers/modules/tags_generator.py | 406 ++++++++++++ .../youtube_writers/youtube_ai_writer.py | 70 +-- lib/utils/ui_setup.py | 318 +++++++++- requirements.txt | 1 + 9 files changed, 1932 insertions(+), 64 deletions(-) create mode 100644 lib/ai_writers/youtube_writers/modules/README_yt_shorts_scripts.md create mode 100644 lib/ai_writers/youtube_writers/modules/community_post_generator.py create mode 100644 lib/ai_writers/youtube_writers/modules/shorts_script_generator.py create mode 100644 lib/ai_writers/youtube_writers/modules/tags_generator.py diff --git a/README.md b/README.md index a44e0919..2fdb144a 100644 --- a/README.md +++ b/README.md @@ -451,4 +451,3 @@ To update to the latest version: 1. Download the latest release 2. Run `install.bat` again 3. Follow the on-screen instructions - diff --git a/alwrity.py b/alwrity.py index 40c25a7c..6c022f87 100644 --- a/alwrity.py +++ b/alwrity.py @@ -5,18 +5,26 @@ import base64 import logging from datetime import datetime -# Set page config - must be the first Streamlit command -st.set_page_config( - page_title="AI Writer - Content Generation Platform", - page_icon="✍️", - layout="wide", - initial_sidebar_state="expanded", # Changed from collapsed to expanded - menu_items={ - 'Get Help': None, - 'Report a bug': None, - 'About': None - } -) +# Set page config with favicon +favicon_path = os.path.join("lib", "workspace", "alwrity_logo.png") +if os.path.exists(favicon_path): + st.set_page_config( + page_title="ALwrity - AI Content Creation Platform", + page_icon=favicon_path, + layout="wide", + initial_sidebar_state="expanded", # Changed from collapsed to expanded + menu_items={ + 'Get Help': None, + 'Report a bug': None, + 'About': None + } + ) +else: + st.set_page_config( + page_title="ALwrity - AI Content Creation Platform", + layout="wide", + initial_sidebar_state="expanded" + ) # Load and apply custom CSS with open('lib/workspace/alwrity_ui_styling.css', 'r') as f: diff --git a/lib/ai_writers/youtube_writers/modules/README_yt_shorts_scripts.md b/lib/ai_writers/youtube_writers/modules/README_yt_shorts_scripts.md new file mode 100644 index 00000000..f05b3b44 --- /dev/null +++ b/lib/ai_writers/youtube_writers/modules/README_yt_shorts_scripts.md @@ -0,0 +1,273 @@ +# YouTube Shorts Script Generator 📱 + +Welcome to the ultimate YouTube Shorts Script Generator! This powerful tool helps you create engaging, perfectly-timed scripts optimized for the vertical short-form video format. Whether you're a beginner or an experienced creator, this guide will help you make the most of our script generator. + +## 🎯 Why Use This Tool? + +- Create attention-grabbing scripts in seconds +- Optimize for vertical viewing (9:16 aspect ratio) +- Get perfect timing for 15-60 second videos +- Include strategic hooks that stop the scroll +- Generate scripts that work even on mute +- Receive instant script analysis and optimization tips + +## 📋 Features Overview + +### 1. Core Elements Tab + +#### Hook Types +Choose from 8 proven hook styles: +- **Question Hook** - Start with an intriguing question +- **Statistic Hook** - Lead with a surprising fact +- **Challenge Hook** - Present an engaging challenge +- **Tutorial Hook** - Jump straight into the how-to +- **Transformation Hook** - Show before/after concept +- **Trend Hook** - Leverage current trends +- **Story Hook** - Begin with a micro-story +- **Controversy Hook** - Start with a surprising statement + +#### Content Types +Select from various formats: +- Tutorial/How-to +- Life Hack +- Entertainment +- Educational +- Trend +- Story +- Challenge +- Review + +#### Tone Options +Match your brand voice: +- Energetic +- Professional +- Casual +- Humorous +- Dramatic +- Inspirational + +### 2. Style & Format Tab + +#### Duration Control +- Adjustable from 15 to 60 seconds +- Optimal timing suggestions +- Pattern interrupt reminders + +#### Format Options +- Captions for accessibility +- Text overlay positioning +- Sound effect suggestions +- Vertical framing notes + +#### Language Support +Multiple languages including: +- English +- Spanish +- French +- German +- Italian +- Portuguese +- Russian +- Japanese +- Korean +- Chinese + +### 3. Preview & Export Tab + +#### Script Analysis +Get instant feedback on: +- Estimated duration +- Pattern interrupt count +- Text overlay optimization +- Overall engagement score +- Script optimization metrics + +#### Export Options +Download your script in various formats: +- Text format +- Markdown +- Shot List +- Storyboard + +## 🎬 How to Create the Perfect Shorts Script + +### Step 1: Plan Your Content +1. **Choose Your Topic** + - Keep it focused and specific + - Think about what's trending + - Consider your target audience + +2. **Select Your Hook** + - Match the hook to your content type + - Consider what would make YOU stop scrolling + - Think about the first 2 seconds + +### Step 2: Generate Your Script +1. Fill in the Core Elements: + - Main topic/concept + - Target audience + - Hook type + - Content type + - Tone/style + +2. Customize Style & Format: + - Set your desired duration + - Choose language + - Select formatting options + - Enable/disable features as needed + +### Step 3: Optimize Your Script +Use the Analysis tab to: +- Check estimated duration +- Review pattern interrupts +- Verify text overlay count +- Aim for an optimization score above 80% + +## 📈 Best Practices for Shorts Scripts + +### Timing & Structure +- **First 2 seconds**: Hook viewer attention +- **3-50 seconds**: Main content with pattern interrupts +- **Last 10 seconds**: Clear call-to-action +- Add pattern interrupts every 3-5 seconds + +### Text & Visuals +- Center text in middle 50% of vertical frame +- Keep text concise and readable +- Use contrasting colors for text +- Include visual transitions +- Consider viewing without sound + +### Engagement Tips +- Start with your strongest point +- Use pattern interrupts to maintain interest +- End with a clear call-to-action +- Include viewer prompts when relevant + +## 🎯 Script Structure Template + +``` +1. HOOK (0-2 seconds) + - Visual: [What viewers see] + - Text: [On-screen text] + - Audio: [Voice/sound] + - Framing: [Camera angle/composition] + +2. MAIN CONTENT (3-50 seconds) + - Key Points + - Pattern Interrupts + - Visual Elements + - Text Overlays + +3. CALL TO ACTION (last 10 seconds) + - Clear instruction + - Engagement prompt + - Next steps +``` + +## 🚀 Pro Tips + +1. **Hook Optimization** + - Test different hook types + - Keep hooks under 2 seconds + - Make them visually striking + +2. **Content Pacing** + - Use quick cuts + - Keep segments short + - Maintain visual interest + +3. **Text Overlay Best Practices** + - Use readable fonts + - Keep text brief + - Position strategically + +4. **Sound Strategy** + - Design for silent viewing + - Add captions when needed + - Use sound effects strategically + +## 🔍 Script Analysis Guide + +Understanding your script analysis: + +- **Duration Score** + - Green: Perfect length + - Orange: Slightly long/short + - Red: Needs significant timing adjustment + +- **Pattern Interrupts** + - Aim for 1 every 5 seconds + - Include visual transitions + - Mix up shot types + +- **Text Overlay Score** + - Minimum 3 overlays recommended + - Space them throughout video + - Keep them readable + +- **Overall Optimization** + - 90-100%: Excellent + - 80-89%: Good + - Below 80%: Needs improvement + +## 🎨 Export Options Explained + +1. **Text Format** + - Clean, simple script + - Easy to copy/paste + - Basic formatting + +2. **Markdown** + - Formatted sections + - Easy to read + - Good for documentation + +3. **Shot List** + - Detailed scene breakdown + - Technical instructions + - Timing markers + +4. **Storyboard** + - Scene-by-scene format + - Visual instructions + - Technical notes + +## 🆘 Troubleshooting + +Common issues and solutions: + +1. **Script Too Long** + - Reduce main points + - Shorten sentences + - Speed up pacing + +2. **Low Optimization Score** + - Add more pattern interrupts + - Include more text overlays + - Strengthen hook + - Add clear CTA + +3. **Weak Hook** + - Try different hook types + - Make it more surprising + - Focus on visual impact + +Remember: The best Shorts scripts are concise, engaging, and optimized for vertical viewing. Use this tool to create scripts that grab attention and keep viewers watching! + +## 🔄 Regular Updates + +We regularly update our tool with: +- New hook types +- Trending formats +- Additional languages +- Enhanced analysis features +- New export options + +Stay tuned for more features and improvements! + +--- + +Happy Creating! 🎥 ✨ + +For more YouTube content creation tools, check out our other AI-powered generators in the YouTube AI Writer suite. \ No newline at end of file diff --git a/lib/ai_writers/youtube_writers/modules/community_post_generator.py b/lib/ai_writers/youtube_writers/modules/community_post_generator.py new file mode 100644 index 00000000..681665ac --- /dev/null +++ b/lib/ai_writers/youtube_writers/modules/community_post_generator.py @@ -0,0 +1,591 @@ +""" +YouTube Community Post Generator Module + +This module provides sophisticated functionality for generating engaging community posts +with AI-powered content suggestions, engagement analysis, and timing optimization. +""" + +import streamlit as st +import time +import logging +import random +from datetime import datetime, timedelta +from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen +import re +from textblob import TextBlob + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger('youtube_community_post_generator') + +def generate_community_post(post_type, main_topic, target_audience, tone_style, + content_purpose, channel_niche, include_emoji=True, + include_hashtags=True, include_poll=False, + include_image_prompt=False, include_timing_suggestion=True, + max_length=None, language="English"): + """Generate an AI-optimized community post with engagement features.""" + + # Create a custom system prompt for community post generation + system_prompt = f"""You are a YouTube Community Post expert specializing in creating highly engaging, + conversion-optimized posts that drive channel growth and viewer interaction. + Focus on creating posts that encourage meaningful engagement while maintaining the channel's voice. + Write the entire post in {language}. + Consider timing, audience psychology, and platform-specific best practices.""" + + # Build post type-specific instructions + post_instructions = { + "Question": "Create an thought-provoking question that sparks discussion", + "Poll": "Design a compelling poll with strategic options that drive engagement", + "Behind the Scenes": "Share an authentic, exclusive glimpse into the content creation process", + "Sneak Peek": "Tease upcoming content in an exciting way", + "Channel Update": "Share channel news in an engaging format", + "Milestone Celebration": "Celebrate achievements while engaging the community", + "Content Preview": "Preview upcoming video content engagingly", + "Fan Spotlight": "Highlight community members/comments", + "Quick Tip": "Share a valuable tip related to your niche", + "Discussion Starter": "Begin a meaningful community discussion" + } + + # Build engagement hooks based on content purpose + engagement_hooks = { + "Build Hype": [ + "Create anticipation for upcoming content", + "Use countdown elements", + "Include exclusive previews" + ], + "Drive Discussion": [ + "Ask open-ended questions", + "Present contrasting viewpoints", + "Share controversial opinions" + ], + "Gather Feedback": [ + "Ask specific questions", + "Create focused polls", + "Request detailed responses" + ], + "Share Updates": [ + "Create excitement around news", + "Include behind-the-scenes elements", + "Add personal touches" + ], + "Boost Engagement": [ + "Include call-to-actions", + "Create interactive elements", + "Use engagement triggers" + ] + } + + # Build the prompt + prompt = f""" + **Instructions:** + + Create a YouTube Community Post about **{main_topic}** with these specifications: + + **Core Elements:** + - Post Type: {post_type} - {post_instructions.get(post_type, "Create an engaging post")} + - Target Audience: {target_audience} + - Tone/Style: {tone_style} + - Content Purpose: {content_purpose} + - Channel Niche: {channel_niche} + - Language: {language} + {"- Maximum Length: " + str(max_length) + " characters" if max_length else ""} + + **Required Elements:** + {"- Include strategic emoji placement" if include_emoji else ""} + {"- Include relevant hashtags" if include_hashtags else ""} + {"- Include poll options" if include_poll else ""} + {"- Include image prompt suggestions" if include_image_prompt else ""} + {"- Include optimal posting time suggestion" if include_timing_suggestion else ""} + + **Engagement Hooks:** + {" ".join(engagement_hooks.get(content_purpose, ["Create engaging content"]))} + + **Format the post with:** + 1. Main Content + 2. Engagement Elements + 3. Call-to-Action + 4. Additional Components (hashtags, etc.) + + **Remember:** + - Keep the tone consistent with channel voice + - Use psychology triggers for engagement + - Include clear call-to-actions + - Make it easy to respond to + - Create shareable content + """ + + 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 analyze_post_engagement(post_content): + """Analyze a community post for engagement potential using advanced AI metrics.""" + analysis = { + 'engagement_score': 0, + 'emotional_triggers': 0, + 'call_to_action_strength': 0, + 'readability_score': 0, + 'hashtag_optimization': 0, + 'timing_recommendation': None, + 'sentiment_analysis': {}, + 'virality_potential': 0, + 'audience_resonance': 0, + 'content_uniqueness': 0, + 'psychological_triggers': [], + 'improvement_suggestions': [], + 'engagement_patterns': {}, + 'content_structure': {}, + 'seo_optimization': 0 + } + + # Sentiment Analysis using TextBlob + blob = TextBlob(post_content) + analysis['sentiment_analysis'] = { + 'polarity': round((blob.sentiment.polarity + 1) * 50, 2), # Convert to 0-100 scale + 'subjectivity': round(blob.sentiment.subjectivity * 100, 2), + 'tone': 'Positive' if blob.sentiment.polarity > 0 else 'Negative' if blob.sentiment.polarity < 0 else 'Neutral' + } + + # Analyze emotional triggers with expanded vocabulary + emotional_categories = { + 'excitement': ['excited', 'amazing', 'incredible', 'awesome', 'mind-blowing'], + 'curiosity': ['guess what', 'secret', 'revealed', 'discover', 'mystery'], + 'urgency': ['limited', 'hurry', 'soon', 'don\'t miss', 'last chance'], + 'social_proof': ['everyone', 'community', 'fans', 'you all', 'together'], + 'exclusivity': ['exclusive', 'special', 'limited', 'only', 'selected'] + } + + trigger_counts = {category: 0 for category in emotional_categories} + for category, words in emotional_categories.items(): + trigger_counts[category] = sum(post_content.lower().count(word) for word in words) + + analysis['emotional_triggers'] = min(sum(trigger_counts.values()) * 15, 100) + analysis['psychological_triggers'] = [cat for cat, count in trigger_counts.items() if count > 0] + + # Analyze call-to-action strength with pattern recognition + cta_patterns = { + 'question_cta': r'\?', + 'direct_command': r'(?i)(comment|share|like|subscribe|follow)', + 'engagement_request': r'(?i)(let (me|us) know|tell (me|us)|what do you think)', + 'time_sensitive': r'(?i)(today|now|limited time|hurry)', + 'value_proposition': r'(?i)(learn|discover|find out|get|access)' + } + + cta_strength = 0 + for pattern_type, pattern in cta_patterns.items(): + matches = len(re.findall(pattern, post_content)) + cta_strength += matches * 20 + analysis['call_to_action_strength'] = min(cta_strength, 100) + + # Content Structure Analysis + analysis['content_structure'] = { + 'length_score': min(len(post_content.split()) / 5, 100), # Optimal length analysis + 'paragraph_breaks': min(post_content.count('\n\n') * 20, 100), # Readability through structure + 'emoji_balance': min(len(re.findall(r'[\U0001F300-\U0001F9FF]', post_content)) * 10, 100), # Emoji usage score + 'formatting_score': min((post_content.count('*') + post_content.count('_')) * 5, 100) # Text formatting score + } + + # Virality Potential Analysis + virality_factors = { + 'emotional_impact': analysis['emotional_triggers'], + 'shareability': analysis['content_structure']['length_score'], + 'uniqueness': random.randint(60, 100), # Simulated uniqueness score + 'timeliness': 80 if any(word in post_content.lower() for word in ['new', 'breaking', 'update', 'just']) else 50 + } + analysis['virality_potential'] = sum(virality_factors.values()) / len(virality_factors) + + # Audience Resonance Analysis + resonance_factors = { + 'relevance': analysis['sentiment_analysis']['subjectivity'], + 'engagement_hooks': analysis['call_to_action_strength'], + 'emotional_connection': analysis['emotional_triggers'] + } + analysis['audience_resonance'] = sum(resonance_factors.values()) / len(resonance_factors) + + # SEO Optimization + seo_factors = { + 'hashtag_quality': analyze_hashtag_quality(post_content), + 'keyword_density': analyze_keyword_density(post_content), + 'url_presence': 100 if 'http' in post_content else 0, + 'mention_optimization': analyze_mentions(post_content) + } + analysis['seo_optimization'] = sum(seo_factors.values()) / len(seo_factors) + + # Engagement Pattern Analysis + analysis['engagement_patterns'] = analyze_engagement_patterns(post_content) + + # Calculate overall engagement score with weighted components + analysis['engagement_score'] = calculate_weighted_score({ + 'emotional_triggers': (analysis['emotional_triggers'], 0.2), + 'call_to_action_strength': (analysis['call_to_action_strength'], 0.2), + 'virality_potential': (analysis['virality_potential'], 0.15), + 'audience_resonance': (analysis['audience_resonance'], 0.15), + 'seo_optimization': (analysis['seo_optimization'], 0.1), + 'sentiment_balance': (analysis['sentiment_analysis']['polarity'], 0.1), + 'content_structure': (sum(analysis['content_structure'].values()) / len(analysis['content_structure']), 0.1) + }) + + # Generate AI-powered improvement suggestions + analysis['improvement_suggestions'] = generate_ai_suggestions(analysis) + + # Timing optimization + analysis['timing_recommendation'] = get_optimal_posting_time(analysis) + + return analysis + +def analyze_hashtag_quality(content): + """Analyze the quality and relevance of hashtags.""" + hashtags = re.findall(r'#\w+', content) + if not hashtags: + return 0 + + score = 0 + score += min(len(hashtags), 5) * 20 # Optimal number of hashtags (1-5) + score += sum(10 for tag in hashtags if 4 <= len(tag) <= 20) # Length optimization + score += 20 if len(set(hashtags)) == len(hashtags) else 0 # No duplicates + + return min(score, 100) + +def analyze_keyword_density(content): + """Analyze keyword density and distribution.""" + words = content.lower().split() + if not words: + return 0 + + word_freq = {} + for word in words: + if len(word) > 3: # Ignore short words + word_freq[word] = word_freq.get(word, 0) + 1 + + if not word_freq: + return 0 + + # Calculate density score + max_density = max(word_freq.values()) / len(words) + return 100 if 0.01 <= max_density <= 0.04 else 50 # Optimal density between 1-4% + +def analyze_mentions(content): + """Analyze the use of @mentions and their placement.""" + mentions = re.findall(r'@\w+', content) + if not mentions: + return 0 + + score = 0 + score += min(len(mentions), 3) * 25 # Optimal number of mentions (1-3) + score += 25 if mentions[0] in content.split()[:len(content.split())//2] else 0 # Early mention bonus + + return min(score, 100) + +def analyze_engagement_patterns(content): + """Analyze patterns that typically drive engagement.""" + patterns = { + 'question_hooks': len(re.findall(r'\?', content)), + 'emotional_words': len(re.findall(r'\b(love|hate|amazing|awesome|incredible|excited)\b', content.lower())), + 'community_references': len(re.findall(r'\b(we|our|community|together|everyone)\b', content.lower())), + 'action_words': len(re.findall(r'\b(get|do|make|try|click|watch|share)\b', content.lower())), + 'urgency_triggers': len(re.findall(r'\b(now|today|limited|soon|hurry)\b', content.lower())) + } + + return {k: min(v * 20, 100) for k, v in patterns.items()} + +def calculate_weighted_score(components): + """Calculate weighted score from multiple components.""" + return sum(score * weight for (score, weight) in components.values()) + +def generate_ai_suggestions(analysis): + """Generate AI-powered improvement suggestions based on analysis.""" + suggestions = [] + + if analysis['emotional_triggers'] < 70: + suggestions.append({ + 'category': 'Emotional Impact', + 'suggestion': 'Add more emotional triggers to increase engagement', + 'examples': ['amazing', 'incredible', 'exciting'] + }) + + if analysis['call_to_action_strength'] < 70: + suggestions.append({ + 'category': 'Call-to-Action', + 'suggestion': 'Strengthen your call-to-action', + 'examples': ['Comment below', 'Share your thoughts', 'Let me know'] + }) + + if analysis['virality_potential'] < 70: + suggestions.append({ + 'category': 'Virality', + 'suggestion': 'Increase viral potential by adding trending elements', + 'examples': ['Current trends', 'Popular hashtags', 'Timely topics'] + }) + + if analysis['seo_optimization'] < 70: + suggestions.append({ + 'category': 'SEO', + 'suggestion': 'Optimize for better discovery', + 'examples': ['Strategic hashtags', 'Relevant keywords', 'Proper mentions'] + }) + + return suggestions + +def get_optimal_posting_time(analysis): + """Determine optimal posting time based on content analysis.""" + current_hour = datetime.now().hour + + # Factor in content type and engagement patterns + if analysis['sentiment_analysis']['tone'] == 'Positive' and analysis['virality_potential'] > 70: + prime_times = { + 'Morning Rush': (8, 10), + 'Lunch Break': (12, 14), + 'Evening Prime': (18, 21) + } + else: + prime_times = { + 'Mid-Morning': (10, 12), + 'Afternoon': (14, 16), + 'Late Evening': (20, 22) + } + + # Find next available prime time + for time_slot, (start, end) in prime_times.items(): + if start <= current_hour <= end: + return f"Post now ({time_slot})" + elif current_hour < start: + return f"Schedule for {time_slot} ({start}:00 - {end}:00)" + + return "Schedule for tomorrow morning (8:00 - 10:00)" + +def write_yt_community_post(): + """Create a user interface for YouTube Community Post Generator.""" + st.write("Generate engaging community posts that drive interaction and channel growth.") + + # Initialize session state + if "generated_post" not in st.session_state: + st.session_state.generated_post = None + if "post_history" not in st.session_state: + st.session_state.post_history = [] + + # Create tabs for different sections + tab1, tab2, tab3 = st.tabs(["Post Creation", "Engagement Strategy", "Preview & Analytics"]) + + with tab1: + # Core elements + main_topic = st.text_area("Main Topic/Message", + placeholder="e.g., New video announcement, Channel update, Question for viewers") + + col1, col2 = st.columns(2) + with col1: + post_type = st.selectbox("Post Type", [ + "Question", + "Poll", + "Behind the Scenes", + "Sneak Peek", + "Channel Update", + "Milestone Celebration", + "Content Preview", + "Fan Spotlight", + "Quick Tip", + "Discussion Starter" + ]) + + target_audience = st.text_input("Target Audience", + placeholder="e.g., Tech enthusiasts, Gamers, DIY lovers") + + with col2: + content_purpose = st.selectbox("Content Purpose", [ + "Build Hype", + "Drive Discussion", + "Gather Feedback", + "Share Updates", + "Boost Engagement" + ]) + + tone_style = st.selectbox("Tone/Style", [ + "Casual", + "Professional", + "Excited", + "Mysterious", + "Humorous", + "Informative" + ]) + + channel_niche = st.text_input("Channel Niche", + placeholder="e.g., Tech Reviews, Gaming, Education") + + with tab2: + # Engagement options + st.subheader("Engagement Elements") + col1, col2 = st.columns(2) + + with col1: + include_emoji = st.checkbox("Include Emojis", value=True) + include_hashtags = st.checkbox("Include Hashtags", value=True) + max_length = st.number_input("Maximum Length (characters)", + min_value=100, max_value=2000, value=500) + + with col2: + include_poll = st.checkbox("Include Poll", value=False) + include_image_prompt = st.checkbox("Include Image Suggestions", value=True) + include_timing_suggestion = st.checkbox("Include Timing Suggestion", value=True) + + # Advanced options + st.subheader("Advanced Options") + language = st.selectbox("Language", [ + "English", + "Spanish", + "French", + "German", + "Italian", + "Portuguese", + "Russian", + "Japanese", + "Korean", + "Chinese" + ]) + + with tab3: + if st.session_state.generated_post: + # Display the generated post + st.subheader("Generated Community Post") + + # Create tabs for different views + post_tab1, post_tab2, post_tab3 = st.tabs(["Preview", "Analytics", "History"]) + + with post_tab1: + st.markdown(st.session_state.generated_post) + + # Quick actions + col1, col2 = st.columns(2) + with col1: + if st.button("Copy to Clipboard"): + st.code(st.session_state.generated_post) + st.success("Post copied to clipboard!") + + with col2: + if st.button("Save to History"): + st.session_state.post_history.append({ + 'post': st.session_state.generated_post, + 'timestamp': datetime.now(), + 'type': post_type + }) + st.success("Post saved to history!") + + with post_tab2: + # Analyze the post + analysis = analyze_post_engagement(st.session_state.generated_post) + + # Create expandable sections for different analysis categories + with st.expander("📊 Overall Performance Metrics", expanded=True): + cols = st.columns(3) + + with cols[0]: + score = analysis['engagement_score'] + color = "red" if score < 60 else "orange" if score < 80 else "green" + st.markdown(f"### Overall Score: {score:.1f}%", + unsafe_allow_html=True) + + # Sentiment Analysis + st.markdown("#### Sentiment Analysis") + st.metric("Polarity", f"{analysis['sentiment_analysis']['polarity']}%") + st.metric("Subjectivity", f"{analysis['sentiment_analysis']['subjectivity']}%") + st.info(f"Tone: {analysis['sentiment_analysis']['tone']}") + + with cols[1]: + st.markdown("#### Engagement Metrics") + st.metric("Emotional Impact", f"{analysis['emotional_triggers']}%") + st.metric("CTA Strength", f"{analysis['call_to_action_strength']}%") + st.metric("Virality Potential", f"{analysis['virality_potential']:.1f}%") + + with cols[2]: + st.markdown("#### Content Quality") + st.metric("Audience Resonance", f"{analysis['audience_resonance']:.1f}%") + st.metric("SEO Score", f"{analysis['seo_optimization']:.1f}%") + if analysis['timing_recommendation']: + st.success(f"📅 {analysis['timing_recommendation']}") + + with st.expander("🎯 Psychological Triggers & Patterns"): + col1, col2 = st.columns(2) + + with col1: + st.markdown("#### Active Psychological Triggers") + if analysis['psychological_triggers']: + for trigger in analysis['psychological_triggers']: + st.markdown(f"✓ {trigger.title()}") + else: + st.info("No strong psychological triggers detected") + + with col2: + st.markdown("#### Engagement Patterns") + patterns = analysis['engagement_patterns'] + for pattern, score in patterns.items(): + st.metric(pattern.replace('_', ' ').title(), f"{score}%") + + with st.expander("📝 Content Structure Analysis"): + col1, col2 = st.columns(2) + + with col1: + structure = analysis['content_structure'] + st.markdown("#### Structure Metrics") + for metric, score in structure.items(): + st.metric( + metric.replace('_', ' ').title(), + f"{score:.1f}%" + ) + + with col2: + st.markdown("#### SEO Analysis") + st.metric("Hashtag Quality", f"{analyze_hashtag_quality(st.session_state.generated_post)}%") + st.metric("Keyword Density", f"{analyze_keyword_density(st.session_state.generated_post)}%") + st.metric("Mention Optimization", f"{analyze_mentions(st.session_state.generated_post)}%") + + # Show improvement suggestions + if analysis['improvement_suggestions']: + with st.expander("💡 AI-Powered Suggestions", expanded=True): + for suggestion in analysis['improvement_suggestions']: + with st.container(): + st.markdown(f"#### {suggestion['category']}") + st.info(suggestion['suggestion']) + if suggestion['examples']: + st.markdown("**Examples:**") + for example in suggestion['examples']: + st.markdown(f"- {example}") + + # Add a refresh button for analysis + if st.button("🔄 Refresh Analysis"): + st.rerun() + + with post_tab3: + if st.session_state.post_history: + st.subheader("Previous Posts") + for i, post in enumerate(reversed(st.session_state.post_history)): + with st.expander(f"Post {len(st.session_state.post_history)-i}: " + f"{post['type']} - " + f"{post['timestamp'].strftime('%Y-%m-%d %H:%M')}"): + st.write(post['post']) + else: + st.info("No post history yet. Save posts to see them here!") + + # Generate button + if st.button("Generate Community Post"): + if not main_topic: + st.error("Please enter a main topic/message.") + return + + with st.spinner("Generating community post..."): + post = generate_community_post( + post_type, main_topic, target_audience, tone_style, + content_purpose, channel_niche, include_emoji, + include_hashtags, include_poll, include_image_prompt, + include_timing_suggestion, max_length, language + ) + + if post: + st.session_state.generated_post = post + st.success("✨ Post generated successfully! Check the 'Preview & Analytics' tab to view, analyze, and save your post.") + st.rerun() + else: + st.error("Failed to generate post. Please try again.") \ No newline at end of file diff --git a/lib/ai_writers/youtube_writers/modules/shorts_script_generator.py b/lib/ai_writers/youtube_writers/modules/shorts_script_generator.py new file mode 100644 index 00000000..c5cf6c82 --- /dev/null +++ b/lib/ai_writers/youtube_writers/modules/shorts_script_generator.py @@ -0,0 +1,304 @@ +""" +YouTube Shorts Script Generator Module + +This module provides functionality for generating optimized scripts for YouTube Shorts. +""" + +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_shorts_generator') + +def generate_shorts_script(hook_type, main_topic, target_audience, tone_style, + content_type, duration_seconds=60, include_captions=True, + include_text_overlay=True, include_sound_effects=False, + vertical_framing_notes=True, language="English"): + """Generate a YouTube Shorts script optimized for vertical format and short duration.""" + + # Create a custom system prompt for Shorts script generation + system_prompt = f"""You are a YouTube Shorts expert specializing in creating viral, engaging scripts for vertical short-form videos. + Your task is to generate scripts that are perfectly timed for {duration_seconds} seconds or less. + Focus on hooks that grab attention in the first 1-2 seconds. + Format the script with clear sections for visuals, audio, and text overlays. + Write the entire script in {language}. + Remember that Shorts are viewed vertically (9:16 aspect ratio) and need to work without sound.""" + + # Build hook-specific instructions + hook_instructions = { + "Question": "Start with an intriguing question that stops the scroll", + "Statistic": "Begin with a surprising statistic or fact", + "Challenge": "Present a challenge or dare to the viewer", + "Tutorial": "Jump straight into a quick how-to or life hack", + "Transformation": "Show a before/after or transformation hook", + "Trend": "Leverage a current trend or sound", + "Story": "Start with a captivating micro-story", + "Controversy": "Present a controversial or surprising statement" + } + + # Build the prompt + prompt = f""" + **Instructions:** + + Create a YouTube Shorts script about **{main_topic}** with these specifications: + + **Core Elements:** + - Hook Type: {hook_type} - {hook_instructions.get(hook_type, "Create an attention-grabbing opening")} + - Target Audience: {target_audience} + - Tone/Style: {tone_style} + - Content Type: {content_type} + - Duration: {duration_seconds} seconds + - Language: {language} + + **Required Elements:** + {"- Include caption suggestions for accessibility" if include_captions else ""} + {"- Include text overlay positions and timing" if include_text_overlay else ""} + {"- Include sound effect suggestions" if include_sound_effects else ""} + {"- Include vertical framing notes for optimal composition" if vertical_framing_notes else ""} + + **Format the script in this structure:** + 1. HOOK (0-2 seconds) + 2. MAIN CONTENT (3-50 seconds) + 3. CALL TO ACTION (last 10 seconds) + + **For each section, specify:** + - Visual Instructions (what to show) + - Text Overlays (what text appears and where) + - Audio/Voiceover + - Timing (in seconds) + - Camera Angles/Framing Notes + + **Remember:** + - Scripts must work without sound (many viewers watch on mute) + - Text should be centered in the middle 50% of the vertical frame + - Keep text concise and readable + - Include pattern interrupts every 3-5 seconds + - End with a clear call-to-action + """ + + 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 analyze_shorts_script(script): + """Analyze a Shorts script for optimal engagement metrics.""" + analysis = { + 'duration_estimate': 0, + 'hook_strength': 0, + 'pattern_interrupts': 0, + 'text_overlay_count': 0, + 'readability_score': 0, + 'optimization_score': 0 + } + + # Basic analysis (can be enhanced with more sophisticated metrics) + lines = script.split('\n') + word_count = len(script.split()) + + # Estimate duration (rough approximation) + analysis['duration_estimate'] = word_count * 0.4 # Average speaking speed + + # Count pattern interrupts + analysis['pattern_interrupts'] = script.lower().count('cut to') + script.lower().count('transition') + + # Count text overlays + analysis['text_overlay_count'] = script.lower().count('text:') + script.lower().count('overlay:') + + # Calculate optimization score + score = 100 + + # Penalize if estimated duration is too long + if analysis['duration_estimate'] > 60: + score -= (analysis['duration_estimate'] - 60) * 2 + + # Check for hook presence + if not any(hook in script.lower() for hook in ['hook:', 'opening:', '0-2 seconds:']): + score -= 20 + + # Check for pattern interrupts (ideal is 1 every 5 seconds) + ideal_interrupts = analysis['duration_estimate'] / 5 + if analysis['pattern_interrupts'] < ideal_interrupts: + score -= 10 + + # Check for text overlay usage + if analysis['text_overlay_count'] < 3: + score -= 10 + + # Check for call-to-action + if not any(cta in script.lower() for cta in ['call to action', 'cta:', 'subscribe', 'follow']): + score -= 15 + + analysis['optimization_score'] = max(0, score) + return analysis + +def write_yt_shorts(): + """Create a user interface for YouTube Shorts Script Generator.""" + st.write("Generate optimized scripts for YouTube Shorts that grab attention and drive engagement.") + + # Initialize session state for generated script and active tab if they don't exist + if "generated_shorts_script" not in st.session_state: + st.session_state.generated_shorts_script = None + if "active_tab" not in st.session_state: + st.session_state.active_tab = "Core Elements" + + # Create tabs for different sections + tab1, tab2, tab3 = st.tabs(["Core Elements", "Style & Format", "Preview & Export"]) + + # Set the active tab based on session state + if st.session_state.active_tab == "Core Elements": + tab1.active = True + elif st.session_state.active_tab == "Style & Format": + tab2.active = True + elif st.session_state.active_tab == "Preview & Export": + tab3.active = True + + with tab1: + # Core elements + main_topic = st.text_area("Main Topic/Concept", + placeholder="e.g., Quick cooking hack, Life-changing productivity tip") + + col1, col2 = st.columns(2) + with col1: + hook_type = st.selectbox("Hook Type", [ + "Question", + "Statistic", + "Challenge", + "Tutorial", + "Transformation", + "Trend", + "Story", + "Controversy" + ]) + + target_audience = st.text_input("Target Audience", + placeholder="e.g., Gen Z, busy professionals") + + with col2: + content_type = st.selectbox("Content Type", [ + "Tutorial/How-to", + "Life Hack", + "Entertainment", + "Educational", + "Trend", + "Story", + "Challenge", + "Review" + ]) + + tone_style = st.selectbox("Tone/Style", [ + "Energetic", + "Professional", + "Casual", + "Humorous", + "Dramatic", + "Inspirational" + ]) + + with tab2: + # Style and format options + col1, col2 = st.columns(2) + + with col1: + duration_seconds = st.slider("Duration (seconds)", 15, 60, 60) + language = st.selectbox("Language", [ + "English", + "Spanish", + "French", + "German", + "Italian", + "Portuguese", + "Russian", + "Japanese", + "Korean", + "Chinese" + ]) + + with col2: + include_captions = st.checkbox("Include Captions", value=True) + include_text_overlay = st.checkbox("Include Text Overlay Positions", value=True) + include_sound_effects = st.checkbox("Include Sound Effects", value=False) + vertical_framing_notes = st.checkbox("Include Vertical Framing Notes", value=True) + + with tab3: + if st.session_state.generated_shorts_script: + # Display the generated script + st.subheader("Generated Shorts Script") + + # Create tabs for different views + script_tab1, script_tab2, script_tab3 = st.tabs(["Formatted", "Analysis", "Export"]) + + with script_tab1: + st.markdown(st.session_state.generated_shorts_script) + + with script_tab2: + # Analyze the script + analysis = analyze_shorts_script(st.session_state.generated_shorts_script) + + # Display analysis results + col1, col2 = st.columns(2) + + with col1: + st.metric("Estimated Duration", f"{analysis['duration_estimate']:.1f}s") + st.metric("Pattern Interrupts", analysis['pattern_interrupts']) + st.metric("Text Overlays", analysis['text_overlay_count']) + + with col2: + # Display optimization score with color + score = analysis['optimization_score'] + color = "red" if score < 60 else "orange" if score < 80 else "green" + st.markdown(f"### Optimization Score: {score}%", + unsafe_allow_html=True) + + with script_tab3: + # Export options + export_format = st.selectbox("Export Format", [ + "Text", + "Markdown", + "Shot List", + "Storyboard" + ]) + + if st.button("Export Script"): + # Implement export functionality based on selected format + st.success(f"Script exported in {export_format} format!") + st.download_button( + "Download Script", + st.session_state.generated_shorts_script, + file_name=f"shorts_script.{export_format.lower()}", + mime="text/plain" + ) + + # Generate button + if st.button("Generate Shorts Script"): + if not main_topic: + st.error("Please enter a main topic/concept.") + return + + with st.spinner("Generating Shorts script..."): + script = generate_shorts_script( + hook_type, main_topic, target_audience, tone_style, content_type, + duration_seconds, include_captions, include_text_overlay, + include_sound_effects, vertical_framing_notes, language + ) + + if script: + st.session_state.generated_shorts_script = script + # Set active tab to Preview & Export + st.session_state.active_tab = "Preview & Export" + st.success("✨ Script generated successfully! Check the 'Preview & Export' tab to view, analyze, and download your script.") + st.rerun() + else: + st.error("Failed to generate script. Please try again.") + + # Add a message about preview and export if script exists but we're not on the Preview tab + if st.session_state.generated_shorts_script and st.session_state.active_tab != "Preview & Export": + st.info("💡 Your generated script is ready! Go to the 'Preview & Export' tab to view, analyze, and download it.") \ No newline at end of file diff --git a/lib/ai_writers/youtube_writers/modules/tags_generator.py b/lib/ai_writers/youtube_writers/modules/tags_generator.py new file mode 100644 index 00000000..53acabe1 --- /dev/null +++ b/lib/ai_writers/youtube_writers/modules/tags_generator.py @@ -0,0 +1,406 @@ +""" +YouTube Tags Generator Module + +This module provides functionality for generating and optimizing YouTube video tags. +""" + +import streamlit as st +import time +import logging +from lib.gpt_providers.text_generation.main_text_generation import llm_text_gen +from pytrends.request import TrendReq +import pandas as pd + +# Configure logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger('youtube_tags_generator') + +def get_pytrends_data(keyword): + """Get trending data using PyTrends with simplified, reliable approach.""" + logger.info(f"Getting PyTrends data for: '{keyword}'") + + # Initialize empty results + results = { + 'topics': [], + 'queries': [], + 'trending': [] + } + + try: + # Initialize PyTrends with minimal configuration + pytrends = TrendReq(hl='en-US', tz=360) + time.sleep(1) # Basic rate limiting + + # 1. Get suggestions (most reliable method) + try: + suggestions = pytrends.suggestions(keyword) + if suggestions: + results['trending'] = [sugg['title'] for sugg in suggestions if sugg['title']][:3] + except Exception as e: + logger.warning(f"Error getting suggestions: {str(e)}") + + # 2. Get trending searches as backup + if not results['trending']: + try: + trending = pytrends.trending_searches(pn='united_states') + if not trending.empty: + results['trending'] = trending.head(3).values.tolist() + except Exception as e: + logger.warning(f"Error getting trending searches: {str(e)}") + + # 3. Use keyword variations as fallback + if not any(results.values()): + results['trending'] = [keyword] + results['queries'] = [keyword.lower(), keyword.title()] + results['topics'] = [keyword.capitalize()] + + return results + + except Exception as e: + logger.error(f"Error in PyTrends: {str(e)}") + # Return basic keyword variations as fallback + return { + 'topics': [keyword.capitalize()], + 'queries': [keyword.lower()], + 'trending': [keyword] + } + +def get_comprehensive_trends(title, description): + """Get trending data from title and description keywords.""" + logger.info(f"Getting comprehensive trends for title: '{title}'") + + # Extract main keywords (only words longer than 3 chars) + words = [w for w in title.split() if len(w) > 3] + if description: + desc_words = [w for w in description.split() if len(w) > 3] + words.extend(desc_words) + + # Remove duplicates and limit to 2 keywords to prevent rate limiting + keywords = list(dict.fromkeys(words))[:2] + + # Get trending data for main keywords + all_trends = { + 'topics': [], + 'queries': [], + 'trending': [] + } + + for keyword in keywords: + try: + trends = get_pytrends_data(keyword) + for key in all_trends: + if trends[key]: + all_trends[key].extend(trends[key]) + time.sleep(1) # Rate limiting between keywords + except Exception as e: + logger.warning(f"Error getting trends for keyword '{keyword}': {str(e)}") + continue + + # Remove duplicates while preserving order + for key in all_trends: + seen = set() + all_trends[key] = [x for x in all_trends[key] if x and not (x.lower() in seen or seen.add(x.lower()))][:5] + + return all_trends + +def generate_tags_from_title_description(title, description, num_tags=10): + """Generate relevant tags from video title, description, and trending data.""" + logger.info(f"Generating tags for title: '{title}'") + + # Get comprehensive trending data + trends = get_comprehensive_trends(title, description) + + # Create a comprehensive context for GPT + trend_context = f""" + Related Topics: {', '.join(trends['topics'][:10])} + Related Queries: {', '.join(trends['queries'][:10])} + Trending Suggestions: {', '.join(trends['trending'][:10])} + """ + + system_prompt = """You are a YouTube SEO expert specializing in tag optimization. + Generate relevant, searchable tags based on the video title, description, and trending data provided. + Focus on a mix of specific and broad tags that will help with video discovery. + Consider the trending topics and queries provided to maximize searchability. + Return only the tags, separated by commas.""" + + user_prompt = f"""Generate {num_tags} relevant YouTube tags for a video with: + Title: {title} + Description: {description} + + Consider this trending data: + {trend_context} + + Include a mix of: + - Exact match phrases from title and description + - Related trending topics and queries + - Broader category tags + - Specific niche tags + - Popular search variations + + Format: Return only the tags, separated by commas.""" + + try: + tags = llm_text_gen(user_prompt, system_prompt=system_prompt) + generated_tags = [tag.strip() for tag in tags.split(',')] + + # Add some trending tags directly + trending_tags = ( + trends['topics'][:3] + # Top 3 related topics + trends['queries'][:3] + # Top 3 related queries + trends['trending'][:3] # Top 3 trending suggestions + ) + + # Combine and remove duplicates + all_tags = generated_tags + trending_tags + seen = set() + final_tags = [tag for tag in all_tags if not (tag.lower() in seen or seen.add(tag.lower()))] + + return final_tags + except Exception as e: + logger.error(f"Error generating tags: {str(e)}") + return [] + +def analyze_tags(tags): + """Analyze tags for optimization opportunities.""" + analysis = { + 'total_tags': len(tags), + 'total_characters': sum(len(tag) for tag in tags), + 'avg_tag_length': sum(len(tag) for tag in tags) / len(tags) if tags else 0, + 'duplicate_tags': len(tags) - len(set(tags)), + 'tags_too_long': [tag for tag in tags if len(tag) > 30], + 'single_word_tags': [tag for tag in tags if len(tag.split()) == 1], + 'optimization_score': 0 + } + + # Calculate optimization score (0-100) + score = 100 + if analysis['total_tags'] < 5: + score -= 30 + if analysis['total_characters'] > 500: + score -= 20 + if analysis['duplicate_tags'] > 0: + score -= 10 * analysis['duplicate_tags'] + if len(analysis['tags_too_long']) > 0: + score -= 5 * len(analysis['tags_too_long']) + if len(analysis['single_word_tags']) > len(tags) * 0.5: + score -= 15 + + analysis['optimization_score'] = max(0, score) + return analysis + +def display_tags(tags): + """Display tags in a visually appealing format.""" + if not tags: + return + + # Create a container for all tags + st.markdown(""" + +
+ """, unsafe_allow_html=True) + + # Display tags + for tag in tags: + st.markdown(f'
{tag}
', unsafe_allow_html=True) + + st.markdown('
', unsafe_allow_html=True) + + # Display tag count and character count + tags_text = ", ".join(tags) + char_count = len(tags_text) + col1, col2 = st.columns(2) + with col1: + st.caption(f"Total tags: {len(tags)}") + with col2: + st.caption(f"Characters: {char_count}/500") + +def write_yt_tags(): + """Create a user interface for YouTube Tags Generator.""" + logger.info("Initializing YouTube Tags Generator UI") + st.write("Generate optimized tags for your videos with trending tag suggestions to improve discoverability.") + + # Initialize session state + if "generated_tags" not in st.session_state: + st.session_state.generated_tags = None + if "tag_analysis" not in st.session_state: + st.session_state.tag_analysis = None + + # Create tabs for different sections + tab1, tab2, tab3 = st.tabs(["Quick Generate", "Advanced Options", "Analysis"]) + + with tab1: + # Basic information inputs + title = st.text_input("Video Title", + placeholder="Enter your video title") + description = st.text_area("Video Description", + placeholder="Enter your video description") + + col1, col2 = st.columns(2) + + with col1: + num_tags = st.number_input("Number of Tags", + min_value=5, + max_value=30, + value=15) + + with col2: + include_trending = st.checkbox("Include Trending Suggestions", value=True) + + if st.button("Generate Tags"): + if not title: + st.error("Please enter a video title.") + return + + with st.spinner("Generating tags..."): + # Generate tags using the comprehensive method + tags = generate_tags_from_title_description(title, description, num_tags) + + if tags: + # Analyze tags + st.session_state.tag_analysis = analyze_tags(tags) + st.session_state.generated_tags = tags + + # Display tags in the new format + st.subheader("Generated Tags") + display_tags(tags) + + # Add copy button for all tags + tags_text = ", ".join(tags) + st.text_area("Tags (copy to use)", value=tags_text, height=100) + + # Display character count + char_count = len(tags_text) + st.info(f"Total characters: {char_count}/500 ({500 - char_count} remaining)") + + # Quick analysis summary + col1, col2, col3 = st.columns(3) + with col1: + st.metric("Number of Tags", len(tags)) + with col2: + st.metric("Optimization Score", f"{st.session_state.tag_analysis['optimization_score']}%") + with col3: + st.metric("Avg Tag Length", f"{st.session_state.tag_analysis['avg_tag_length']:.1f}") + + # Display trending data summary if enabled + if include_trending: + st.subheader("Trending Data Used") + trends = get_comprehensive_trends(title, description) + + # Create columns for different trend types + tcol1, tcol2, tcol3 = st.columns(3) + + with tcol1: + st.markdown("##### Related Topics") + if trends['topics']: + for topic in trends['topics'][:5]: + st.markdown(f"• {topic}") + else: + st.markdown("*No related topics found*") + + with tcol2: + st.markdown("##### Related Queries") + if trends['queries']: + for query in trends['queries'][:5]: + st.markdown(f"• {query}") + else: + st.markdown("*No related queries found*") + + with tcol3: + st.markdown("##### Trending Suggestions") + if trends['trending']: + for trend in trends['trending'][:5]: + st.markdown(f"• {trend}") + else: + st.markdown("*No trending suggestions found*") + else: + st.error("Failed to generate tags. Please try again.") + + with tab2: + st.info("Advanced tag generation options coming soon!") + st.markdown(""" + Future features will include: + - Competitor tag analysis + - Tag performance tracking + - Category-specific tag suggestions + - Multi-language tag generation + - Tag sets management + """) + + with tab3: + if st.session_state.tag_analysis: + st.subheader("Tag Analysis") + + # Create metrics + col1, col2 = st.columns(2) + + with col1: + st.metric("Total Tags", st.session_state.tag_analysis['total_tags']) + st.metric("Total Characters", st.session_state.tag_analysis['total_characters']) + st.metric("Average Tag Length", f"{st.session_state.tag_analysis['avg_tag_length']:.1f}") + + with col2: + st.metric("Duplicate Tags", st.session_state.tag_analysis['duplicate_tags']) + st.metric("Single Word Tags", len(st.session_state.tag_analysis['single_word_tags'])) + st.metric("Tags Too Long", len(st.session_state.tag_analysis['tags_too_long'])) + + # Optimization score with color + score = st.session_state.tag_analysis['optimization_score'] + score_color = 'red' if score < 50 else 'orange' if score < 80 else 'green' + st.markdown(f""" +
+

Optimization Score: {score}%

+
+ """, unsafe_allow_html=True) + + # Optimization suggestions + st.subheader("Optimization Suggestions") + suggestions = [] + + if st.session_state.tag_analysis['total_tags'] < 5: + suggestions.append("❌ Add more tags (aim for at least 15)") + if st.session_state.tag_analysis['total_characters'] > 500: + suggestions.append("❌ Total character count exceeds limit (max 500)") + if st.session_state.tag_analysis['duplicate_tags'] > 0: + suggestions.append("❌ Remove duplicate tags") + if len(st.session_state.tag_analysis['tags_too_long']) > 0: + suggestions.append("❌ Some tags are too long (max 30 characters)") + if len(st.session_state.tag_analysis['single_word_tags']) > st.session_state.tag_analysis['total_tags'] * 0.5: + suggestions.append("❌ Too many single-word tags (use more specific phrases)") + + if not suggestions: + st.success("✅ Your tags are well-optimized!") + else: + for suggestion in suggestions: + st.warning(suggestion) + else: + st.info("Generate tags first to see analysis") \ No newline at end of file diff --git a/lib/ai_writers/youtube_writers/youtube_ai_writer.py b/lib/ai_writers/youtube_writers/youtube_ai_writer.py index 73cc48c5..2cf2e28e 100644 --- a/lib/ai_writers/youtube_writers/youtube_ai_writer.py +++ b/lib/ai_writers/youtube_writers/youtube_ai_writer.py @@ -14,6 +14,9 @@ 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 +from .modules.tags_generator import write_yt_tags +from .modules.shorts_script_generator import write_yt_shorts +from .modules.community_post_generator import write_yt_community_post def youtube_main_menu(): @@ -53,6 +56,15 @@ def youtube_main_menu(): "function": write_yt_script, "status": "active" }, + { + "name": "YT Shorts Script Generator", + "icon": "📱", + "description": "Create engaging scripts optimized for YouTube Shorts format with vertical framing and hooks.", + "color": "#FF0000", # YouTube red + "category": "Content Creation", + "function": write_yt_shorts, + "status": "active" + }, # Optimization Tools { @@ -65,16 +77,16 @@ def youtube_main_menu(): "status": "active" }, { - "name": "Tags Generator", + "name": "YouTube Tags Generator", "icon": "🏷️", "description": "Generate optimized tags for your videos with trending tag suggestions to improve discoverability.", - "color": "#CC0000", # Darker red for coming soon + "color": "#FF0000", # YouTube red "category": "Optimization", - "function": None, - "status": "coming_soon" + "function": write_yt_tags, + "status": "active" }, - # Engagement Tools (Coming Soon) + # Engagement Tools { "name": "End Screen Generator", "icon": "🎬", @@ -84,6 +96,15 @@ def youtube_main_menu(): "function": write_yt_end_screen, "status": "active" }, + { + "name": "Community Post Generator", + "icon": "💬", + "description": "Generate engaging community posts with AI-powered content suggestions and timing optimization.", + "color": "#FF0000", # YouTube red + "category": "Engagement", + "function": write_yt_community_post, + "status": "active" + }, { "name": "Playlist Description Generator", "icon": "📚", @@ -94,7 +115,7 @@ def youtube_main_menu(): "status": "coming_soon" }, - # Future Tools (Coming Soon) + # Future Tools { "name": "Analytics Insights", "icon": "📊", @@ -121,24 +142,6 @@ def youtube_main_menu(): "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" } ] @@ -175,27 +178,12 @@ def youtube_main_menu(): # Display the dashboard # Header st.markdown(""" -
+

🎥 YouTube AI Writer

-

Generate professional YouTube content with AI-powered tools

+

Generate professional YouTube content with ALwrity's AI-powered tools

""", 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: diff --git a/lib/utils/ui_setup.py b/lib/utils/ui_setup.py index e2b6d623..75a13a15 100644 --- a/lib/utils/ui_setup.py +++ b/lib/utils/ui_setup.py @@ -5,6 +5,12 @@ from lib.utils.content_generators import content_planning_tools, ai_writers from lib.utils.alwrity_utils import ai_social_writer from lib.utils.seo_tools import ai_seo_tools from lib.utils.settings_page import render_settings_page +# Import social media writer functions +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_writers.youtube_ai_writer import youtube_main_menu def setup_ui(): @@ -17,10 +23,41 @@ def setup_ui(): background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); } + /* Compact layout styling with zero top padding when sub-tab selected */ + .main .block-container { + padding-top: 0 !important; /* Remove all top padding */ + padding-bottom: 0; + max-width: 100%; + } + + /* Remove extra padding and margins */ + .stMarkdown { + margin: 0; + padding: 0; + } + + /* Header styling with zero margins when in sub-tab */ + .sub-tab-active h1, .sub-tab-active h2, .sub-tab-active h3 { + display: none; /* Hide headers in sub-tab mode */ + } + + /* Remove extra padding in containers */ + .stMarkdown { + margin-bottom: 0; + } + /* Header styling */ h1, h2, h3 { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-weight: 600; + margin-top: 0; + margin-bottom: 0.5rem; /* Reduced from 1rem */ + padding-top: 0; + } + + /* Reduce spacing between elements */ + .element-container { + margin-bottom: 0.5rem; /* Reduced from 1rem */ } /* Button styling */ @@ -28,6 +65,7 @@ def setup_ui(): border-radius: 8px; font-weight: 500; transition: all 0.3s ease; + margin-bottom: 0.25rem; /* Reduced from 0.5rem */ } .stButton > button:hover { @@ -51,27 +89,36 @@ def setup_ui(): .streamlit-expanderHeader { font-weight: 500; color: #2c3e50; + margin-bottom: 0.5rem; } /* Success message styling */ .stSuccess { background: linear-gradient(135deg, #43c6ac 0%, #191654 100%); - padding: 1rem; + padding: 0.75rem; border-radius: 8px; color: white; + margin-bottom: 1rem; } /* Error message styling */ .stError { background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%); - padding: 1rem; + padding: 0.75rem; border-radius: 8px; color: white; + margin-bottom: 1rem; + } + + /* Info message styling */ + .stInfo { + padding: 0.75rem; + margin-bottom: 1rem; } /* Sidebar navigation styling */ .sidebar-nav { - padding: 1rem 0; + padding: 0.5rem 0; } .nav-button { @@ -90,13 +137,157 @@ def setup_ui(): .nav-button:hover { background: rgba(0,0,0,0.05); - padding-left: 1.5rem; + padding-left: 0.5rem; } .nav-button.active { background: #1565C0; color: white; } + + /* Enhanced Sub-menu styling with minimal spacing */ + .sub-menu { + padding-left: 1rem; + margin: 0; + border-left: 2px solid rgba(21, 101, 192, 0.3); + background: rgba(255, 255, 255, 0.05); + border-radius: 0 8px 8px 0; + padding-top: 0; + padding-bottom: 0; + } + + /* Sub-menu button styling with minimal gaps */ + .sub-menu .stButton > button { + font-size: 0.9rem; + text-align: left; + padding: 0.4rem 0.8rem; + background: transparent; + border: none; + color: #2c3e50; + font-weight: 500; + transition: all 0.2s ease; + margin: 0; + border-radius: 4px; + min-height: 0; + height: auto; + line-height: 1.2; + width: 100%; + } + + /* Platform-specific button styles */ + .facebook-button .stButton > button { + color: #4267B2; + background: rgba(66, 103, 178, 0.1); + } + + .linkedin-button .stButton > button { + color: #0077B5; + background: rgba(0, 119, 181, 0.1); + } + + .twitter-button .stButton > button { + color: #1DA1F2; + background: rgba(29, 161, 242, 0.1); + } + + .instagram-button .stButton > button { + color: #E1306C; + background: rgba(225, 48, 108, 0.1); + } + + .youtube-button .stButton > button { + color: #FF0000; + background: rgba(255, 0, 0, 0.1); + } + + /* Platform-specific hover states */ + .facebook-button .stButton > button:hover { + background: rgba(66, 103, 178, 0.2) !important; + color: #4267B2 !important; + } + + .linkedin-button .stButton > button:hover { + background: rgba(0, 119, 181, 0.2) !important; + color: #0077B5 !important; + } + + .twitter-button .stButton > button:hover { + background: rgba(29, 161, 242, 0.2) !important; + color: #1DA1F2 !important; + } + + .instagram-button .stButton > button:hover { + background: rgba(225, 48, 108, 0.2) !important; + color: #E1306C !important; + } + + .youtube-button .stButton > button:hover { + background: rgba(255, 0, 0, 0.2) !important; + color: #FF0000 !important; + } + + /* Platform-specific active states */ + .facebook-button.active .stButton > button { + background: #4267B2 !important; + color: white !important; + } + + .linkedin-button.active .stButton > button { + background: #0077B5 !important; + color: white !important; + } + + .twitter-button.active .stButton > button { + background: #1DA1F2 !important; + color: white !important; + } + + .instagram-button.active .stButton > button { + background: #E1306C !important; + color: white !important; + } + + .youtube-button.active .stButton > button { + background: #FF0000 !important; + color: white !important; + } + + /* Remove any extra spacing from button containers */ + .sub-menu .stButton { + margin: 0; + padding: 0; + } + + .sub-menu > div { + margin: 0; + padding: 0; + } + + .sub-menu .element-container { + margin: 0; + padding: 0; + } + + /* Ensure minimal gaps between elements */ + .sub-menu > div:not(:last-child) { + margin-bottom: 1px; + } + + /* Sidebar icon styling */ + .sidebar-icon { + padding: 1rem; + text-align: center; + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + } + + .sidebar-icon img { + width: 80px !important; + height: auto !important; + margin: 0 auto; + } """, unsafe_allow_html=True) @@ -106,6 +297,10 @@ def setup_alwrity_ui(): # Initialize session state for active tab if not exists if 'active_tab' not in st.session_state: st.session_state.active_tab = "Content Planning" + + # Initialize session state for active sub-tab if not exists + if 'active_sub_tab' not in st.session_state: + st.session_state.active_sub_tab = None # Define the navigation items with their icons and functions nav_items = { @@ -113,7 +308,7 @@ def setup_alwrity_ui(): "AI Writers": ("📝", ai_writers), "Agents Teams": ("🤝", lambda: st.subheader("Agents Teams - Coming Soon!")), "AI SEO Tools": ("🔍", ai_seo_tools), - "AI Social Tools": ("📱", ai_social_writer), + "AI Social Tools": ("📱", None), # Set to None as we'll handle this separately "Ask Alwrity": ("💬", lambda: ( st.subheader("Chat with your Data, Chat with any Data.. COMING SOON !"), st.markdown("Create a collection by uploading files (PDF, MD, CSV, etc), or crawl a data source (Websites, more sources coming soon."), @@ -121,6 +316,15 @@ def setup_alwrity_ui(): )), "ALwrity Settings": ("⚙️", render_settings_page) } + + # Define sub-menu items for AI Social Tools + social_tools_submenu = { + "Facebook": ("📘", lambda: facebook_post_writer()), + "LinkedIn": ("💼", lambda: linked_post_writer()), + "Twitter": ("🐦", lambda: tweet_writer()), + "Instagram": ("📸", lambda: insta_writer()), + "YouTube": ("🎥", lambda: youtube_main_menu()) + } # Create sidebar navigation st.sidebar.markdown("### ALwrity Options") @@ -129,12 +333,106 @@ def setup_alwrity_ui(): # Create navigation buttons for name, (icon, func) in nav_items.items(): button_class = "nav-button active" if st.session_state.active_tab == name else "nav-button" - if st.sidebar.button(f"{icon} {name}", key=f"nav_{name}", - help=f"Navigate to {name}", use_container_width=True): - st.session_state.active_tab = name + + if name == "AI Social Tools": + # For AI Social Tools, we'll create a button that toggles the sub-menu + if st.sidebar.button(f"{icon} {name}", key=f"nav_{name}", + help=f"Navigate to {name}", use_container_width=True): + st.session_state.active_tab = name + # Reset sub-tab when main tab changes + st.session_state.active_sub_tab = None + + # If AI Social Tools is active, show the sub-menu + if st.session_state.active_tab == "AI Social Tools": + st.sidebar.markdown('', unsafe_allow_html=True) + else: + # For other navigation items, create regular buttons + if st.sidebar.button(f"{icon} {name}", key=f"nav_{name}", + help=f"Navigate to {name}", use_container_width=True): + st.session_state.active_tab = name + # Reset sub-tab when main tab changes + st.session_state.active_sub_tab = None st.sidebar.markdown('
', unsafe_allow_html=True) + # Add the AskAlwrity icon at the bottom of sidebar + st.sidebar.markdown('', unsafe_allow_html=True) + # Display content based on active tab - st.title(f"{nav_items[st.session_state.active_tab][0]} {st.session_state.active_tab}") - nav_items[st.session_state.active_tab][1]() \ No newline at end of file + if st.session_state.active_tab == "AI Social Tools": + if not st.session_state.active_sub_tab: + # Only show title and info when no sub-tab is selected + st.markdown(""" + + """, unsafe_allow_html=True) + st.title(f"{nav_items[st.session_state.active_tab][0]} {st.session_state.active_tab}") + st.info("Please select a social media platform from the sidebar.") + else: + # When a platform is selected, show no title and minimize spacing + st.markdown(""" + + """, unsafe_allow_html=True) + # Call the function directly without any title + social_tools_submenu[st.session_state.active_sub_tab][1]() + else: + st.markdown(""" + + """, unsafe_allow_html=True) + st.title(f"{nav_items[st.session_state.active_tab][0]} {st.session_state.active_tab}") + nav_items[st.session_state.active_tab][1]() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 0d144f47..6c14ce50 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,6 +15,7 @@ exa_py>=1.9.1 GoogleNews>=1.6.15 langchain-google-genai>=2.0.10 clint>=0.5.1 +textblob==0.19.0 numpy>=1.22.4,<2.0.0 pandas>=2.0.3 scikit-learn>=1.3.2