Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import google.generativeai as genai | |
| import textstat | |
| import re | |
| import requests | |
| from urllib.parse import urlparse | |
| import json | |
| import time | |
| from typing import Dict, List, Tuple | |
| import concurrent.futures | |
| # --- Manual Keywords Processor --- | |
| class ManualKeywordProcessor: | |
| def parse_manual_keywords(self, manual_keywords: str) -> List[str]: | |
| """Parse and clean manual target keywords""" | |
| if not manual_keywords.strip(): | |
| return [] | |
| keywords = [] | |
| # Split by comma, semicolon, or newline | |
| raw_keywords = re.split(r'[,;\n]', manual_keywords) | |
| for keyword in raw_keywords: | |
| cleaned = keyword.strip().lower() | |
| if cleaned and len(cleaned) > 2: # Minimum keyword length | |
| keywords.append(cleaned) | |
| return list(set(keywords)) # Remove duplicates | |
| def integrate_manual_keywords(self, auto_keywords: Dict, manual_keywords: List[str]) -> Dict: | |
| """Integrate manual keywords with auto-generated ones""" | |
| # Add manual keywords to primary keywords list | |
| manual_keyword_objects = [] | |
| for kw in manual_keywords: | |
| manual_keyword_objects.append({ | |
| "keyword": kw, | |
| "search_volume": "manual", | |
| "difficulty": "unknown", | |
| "intent": "targeted" | |
| }) | |
| # Merge with existing keywords | |
| integrated = auto_keywords.copy() | |
| if "manual_targets" not in integrated: | |
| integrated["manual_targets"] = [] | |
| integrated["manual_targets"] = manual_keyword_objects | |
| return integrated | |
| class KeywordResearcher: | |
| def __init__(self): | |
| self.serp_api_key = None # Users can add their SERP API key for real data | |
| def suggest_keywords(self, seed_keyword: str, model) -> Dict: | |
| """Generate keyword suggestions with estimated metrics""" | |
| prompt = f""" | |
| Generate a comprehensive keyword research report for: "{seed_keyword}" | |
| Provide 15-20 related keywords with estimated metrics: | |
| - Primary keyword variations | |
| - Long-tail keywords | |
| - Question-based keywords | |
| - Commercial intent keywords | |
| - Informational intent keywords | |
| Format as JSON: | |
| {{ | |
| "primary_keywords": [ | |
| {{"keyword": "example", "search_volume": "high/medium/low", "difficulty": "easy/medium/hard", "intent": "informational/commercial/navigational"}} | |
| ], | |
| "long_tail": [...], | |
| "questions": [...], | |
| "commercial": [...] | |
| }} | |
| """ | |
| try: | |
| response = model.generate_content(prompt) | |
| # Parse JSON response | |
| json_text = response.text.replace('```json', '').replace('```', '').strip() | |
| return json.loads(json_text) | |
| except: | |
| return { | |
| "primary_keywords": [ | |
| {"keyword": f"{seed_keyword} guide", "search_volume": "medium", "difficulty": "medium", "intent": "informational"}, | |
| {"keyword": f"{seed_keyword} tips", "search_volume": "medium", "difficulty": "easy", "intent": "informational"}, | |
| {"keyword": f"best {seed_keyword}", "search_volume": "high", "difficulty": "hard", "intent": "commercial"} | |
| ], | |
| "long_tail": [ | |
| {"keyword": f"how to use {seed_keyword} effectively", "search_volume": "low", "difficulty": "easy", "intent": "informational"} | |
| ], | |
| "questions": [ | |
| {"keyword": f"what is {seed_keyword}", "search_volume": "medium", "difficulty": "easy", "intent": "informational"} | |
| ], | |
| "commercial": [ | |
| {"keyword": f"{seed_keyword} service", "search_volume": "medium", "difficulty": "medium", "intent": "commercial"} | |
| ] | |
| } | |
| # --- Competitor Analysis --- | |
| class CompetitorAnalyzer: | |
| def analyze_top_competitors(self, keyword: str, model) -> Dict: | |
| """Analyze top competitors for content gaps""" | |
| prompt = f""" | |
| Analyze the top 5 competitors ranking for "{keyword}" and identify: | |
| 1. Common content themes they cover | |
| 2. Content gaps they're missing | |
| 3. Average content length | |
| 4. Heading structures they use | |
| 5. Unique angles to differentiate our content | |
| Format as structured analysis with actionable insights. | |
| """ | |
| try: | |
| response = model.generate_content(prompt) | |
| return { | |
| "analysis": response.text, | |
| "content_gaps": self._extract_content_gaps(response.text), | |
| "avg_length": "2000-3000 words", | |
| "differentiation_angles": self._extract_angles(response.text) | |
| } | |
| except: | |
| return { | |
| "analysis": f"Competitors for '{keyword}' typically cover basic information. Opportunity to add more detailed examples and case studies.", | |
| "content_gaps": ["Specific examples", "Step-by-step tutorials", "Common mistakes section"], | |
| "avg_length": "2000-3000 words", | |
| "differentiation_angles": ["Personal experience", "Updated statistics", "Unique framework"] | |
| } | |
| def _extract_content_gaps(self, text: str) -> List[str]: | |
| """Extract content gaps from analysis""" | |
| gaps = [] | |
| lines = text.split('\n') | |
| for line in lines: | |
| if 'gap' in line.lower() or 'missing' in line.lower() or 'opportunity' in line.lower(): | |
| gaps.append(line.strip('- ').strip()) | |
| return gaps[:5] if gaps else ["Advanced techniques", "Case studies", "Common mistakes"] | |
| def _extract_angles(self, text: str) -> List[str]: | |
| """Extract differentiation angles""" | |
| angles = [] | |
| lines = text.split('\n') | |
| for line in lines: | |
| if 'unique' in line.lower() or 'different' in line.lower() or 'angle' in line.lower(): | |
| angles.append(line.strip('- ').strip()) | |
| return angles[:3] if angles else ["Personal experience", "Latest trends", "Actionable framework"] | |
| # --- Content Outline Generator --- | |
| class ContentOutliner: | |
| def generate_seo_outline(self, keyword: str, keywords_data: Dict, competitor_data: Dict, model) -> str: | |
| """Generate comprehensive SEO-optimized outline""" | |
| # Extract top keywords for outline | |
| all_keywords = [] | |
| for category in keywords_data.values(): | |
| if isinstance(category, list): | |
| all_keywords.extend([k["keyword"] for k in category]) | |
| content_gaps = competitor_data.get("content_gaps", []) | |
| prompt = f""" | |
| Create a comprehensive SEO content outline for: "{keyword}" | |
| REQUIREMENTS: | |
| - Include H1, H2, H3 structure | |
| - Integrate these keywords naturally: {', '.join(all_keywords[:10])} | |
| - Address these content gaps: {', '.join(content_gaps)} | |
| - Optimize for featured snippets | |
| - Include FAQ section | |
| - Add internal linking opportunities | |
| OUTLINE FORMAT: | |
| H1: [Compelling title with primary keyword] | |
| Introduction (150-200 words) | |
| - Hook with statistic or question | |
| - Include primary keyword in first 100 words | |
| - Promise what reader will learn | |
| H2: [First main section] | |
| H3: [Subsection] | |
| H3: [Subsection] | |
| [Continue with 4-6 main H2 sections] | |
| H2: FAQ Section | |
| - Question 1 (optimize for featured snippet) | |
| - Question 2 | |
| - Question 3 | |
| Conclusion (100-150 words) | |
| - Summarize key points | |
| - Strong call to action | |
| Add [INTERNAL LINK] and [IMAGE] suggestions throughout. | |
| """ | |
| try: | |
| response = model.generate_content(prompt) | |
| return response.text | |
| except: | |
| return f"Error generating outline. Please check your API key and try again." | |
| # --- Featured Snippet Optimizer --- | |
| class SnippetOptimizer: | |
| def optimize_for_snippets(self, content: str, questions: List[str], model) -> str: | |
| """Optimize content sections for featured snippets""" | |
| snippet_formats = { | |
| "paragraph": "Answer in 40-50 words, clear and direct", | |
| "list": "Format as numbered or bulleted list", | |
| "table": "Present data in simple table format", | |
| "steps": "Break down into clear step-by-step process" | |
| } | |
| optimized_sections = [] | |
| for question in questions[:5]: # Limit to top 5 questions | |
| prompt = f""" | |
| Optimize this answer for Google featured snippets: | |
| Question: {question} | |
| Requirements: | |
| - Answer directly in first sentence | |
| - Keep paragraph answers to 40-50 words | |
| - Use clear, simple language | |
| - Include the question keywords in the answer | |
| - Format for easy scanning | |
| Provide the optimized answer. | |
| """ | |
| try: | |
| response = model.generate_content(prompt) | |
| optimized_sections.append(f"<h3>{question}</h3>\n<p>{response.text}</p>\n") | |
| except: | |
| optimized_sections.append(f"<h3>{question}</h3>\n<p>Answer optimized for featured snippets will appear here.</p>\n") | |
| return "\n".join(optimized_sections) | |
| # --- Enhanced Link Management --- | |
| class LinkManager: | |
| def parse_manual_links(self, links_input: str) -> Dict: | |
| """Parse manual internal and external links""" | |
| internal_links = [] | |
| external_links = [] | |
| if not links_input.strip(): | |
| return {"internal": [], "external": []} | |
| lines = links_input.strip().split('\n') | |
| for line in lines: | |
| if ':' in line: | |
| anchor_text, url = line.split(':', 1) | |
| anchor_text = anchor_text.strip() | |
| url = url.strip() | |
| # Determine if internal or external | |
| if url.startswith('http'): | |
| # Check if it's same domain (simplified check) | |
| if 'website.com' in url or 'yourdomain.com' in url or url.startswith('/'): | |
| internal_links.append({"anchor": anchor_text, "url": url}) | |
| else: | |
| external_links.append({"anchor": anchor_text, "url": url}) | |
| else: | |
| # Assume internal if no http | |
| internal_links.append({"anchor": anchor_text, "url": url}) | |
| return {"internal": internal_links, "external": external_links} | |
| def suggest_internal_links(self, content: str, keyword: str, manual_links: Dict, model) -> List[Dict]: | |
| """Suggest relevant internal links based on content and manual links""" | |
| # Include manual internal links in suggestions | |
| existing_internal = manual_links.get("internal", []) | |
| prompt = f""" | |
| Analyze this content and suggest 5-7 internal linking opportunities: | |
| Primary keyword: {keyword} | |
| Content sample: {content[:1000]}... | |
| Already provided internal links: | |
| {chr(10).join([f"- {link['anchor']}: {link['url']}" for link in existing_internal])} | |
| For each NEW suggestion, provide: | |
| - Anchor text (natural, not over-optimized) | |
| - Context where it should be placed | |
| - Reason why it's valuable for SEO | |
| - Suggested target page type | |
| Format as actionable suggestions. Don't repeat the existing links above. | |
| """ | |
| try: | |
| response = model.generate_content(prompt) | |
| # Parse suggestions into structured format | |
| suggestions = [] | |
| lines = response.text.split('\n') | |
| current_suggestion = {} | |
| for line in lines: | |
| if line.strip(): | |
| if 'anchor' in line.lower() or line.startswith('1.') or line.startswith('-'): | |
| if current_suggestion: | |
| suggestions.append(current_suggestion) | |
| current_suggestion = {"text": line.strip()} | |
| else: | |
| if current_suggestion: | |
| current_suggestion["text"] += " " + line.strip() | |
| if current_suggestion: | |
| suggestions.append(current_suggestion) | |
| return suggestions[:7] | |
| except: | |
| return [ | |
| {"text": f"Link to related '{keyword}' resources in the introduction"}, | |
| {"text": f"Add contextual links to '{keyword}' tools or guides"}, | |
| {"text": f"Reference other '{keyword}' articles in conclusion"} | |
| ] | |
| def format_links_for_content(self, manual_links: Dict) -> str: | |
| """Format manual links for inclusion in content""" | |
| formatted_links = [] | |
| # Internal links | |
| internal_links = manual_links.get("internal", []) | |
| if internal_links: | |
| formatted_links.append("**Internal Links to Include:**") | |
| for link in internal_links: | |
| formatted_links.append(f'<a href="{link["url"]}">{link["anchor"]}</a>') | |
| # External links | |
| external_links = manual_links.get("external", []) | |
| if external_links: | |
| formatted_links.append("\n**External Links to Include:**") | |
| for link in external_links: | |
| formatted_links.append(f'<a href="{link["url"]}" target="_blank" rel="noopener">{link["anchor"]}</a>') | |
| return "\n".join(formatted_links) | |
| # --- Enhanced Image Strategy --- | |
| class ImageStrategist: | |
| def create_image_strategy(self, outline: str, keyword: str, model) -> Dict: | |
| """Create comprehensive image strategy""" | |
| prompt = f""" | |
| Based on this content outline, create a strategic image plan: | |
| {outline} | |
| For each major section, suggest: | |
| 1. Image type (infographic, screenshot, photo, diagram, chart) | |
| 2. Specific content description | |
| 3. SEO-optimized alt text | |
| 4. Placement strategy | |
| 5. Size recommendations | |
| Focus on images that: | |
| - Support the content narrative | |
| - Improve user engagement | |
| - Optimize for image search | |
| - Break up text effectively | |
| Primary keyword: {keyword} | |
| """ | |
| try: | |
| response = model.generate_content(prompt) | |
| return { | |
| "strategy": response.text, | |
| "image_count": self._count_suggested_images(response.text), | |
| "alt_texts": self._extract_alt_texts(response.text, keyword) | |
| } | |
| except: | |
| return { | |
| "strategy": f"Add 3-5 relevant images throughout the article about {keyword}", | |
| "image_count": 4, | |
| "alt_texts": [ | |
| f"Comprehensive guide to {keyword} - infographic", | |
| f"Step-by-step {keyword} process diagram", | |
| f"Benefits of {keyword} - visual comparison", | |
| f"Common {keyword} mistakes to avoid" | |
| ] | |
| } | |
| def _count_suggested_images(self, text: str) -> int: | |
| """Count suggested images in strategy""" | |
| return min(text.lower().count('image') + text.lower().count('infographic') + text.lower().count('diagram'), 8) | |
| def _extract_alt_texts(self, text: str, keyword: str) -> List[str]: | |
| """Extract alt text suggestions""" | |
| alt_texts = [] | |
| lines = text.split('\n') | |
| for line in lines: | |
| if 'alt' in line.lower() or 'description' in line.lower(): | |
| alt_texts.append(line.strip('- ').strip()) | |
| if not alt_texts: | |
| alt_texts = [ | |
| f"Complete {keyword} guide infographic", | |
| f"Step-by-step {keyword} tutorial", | |
| f"{keyword} benefits comparison chart", | |
| f"Real-world {keyword} examples" | |
| ] | |
| return alt_texts[:6] | |
| # --- Main Enhanced Generator --- | |
| def generate_complete_seo_content(api_key, seed_keyword, custom_outline, pov, tone, length, emotion, | |
| include_research, include_competitor, include_outline, include_snippets, | |
| include_linking, include_images, manual_target_keywords, manual_links_input, custom_cta): | |
| if not api_key or not seed_keyword: | |
| return "Please provide API key and seed keyword to generate content." | |
| try: | |
| genai.configure(api_key=api_key) | |
| model = genai.GenerativeModel('gemini-1.5-flash') | |
| except Exception as e: | |
| return f"Error configuring API: {str(e)}" | |
| # Initialize components | |
| researcher = KeywordResearcher() | |
| competitor_analyzer = CompetitorAnalyzer() | |
| outliner = ContentOutliner() | |
| snippet_optimizer = SnippetOptimizer() | |
| link_manager = LinkManager() | |
| image_strategist = ImageStrategist() | |
| keyword_processor = ManualKeywordProcessor() | |
| results = [] | |
| # Process manual keywords | |
| manual_keywords = keyword_processor.parse_manual_keywords(manual_target_keywords) | |
| if manual_keywords: | |
| results.append("π― **MANUAL TARGET KEYWORDS PROCESSED**") | |
| results.append("**Your Specified Keywords:**") | |
| for kw in manual_keywords: | |
| results.append(f"β’ {kw}") | |
| results.append("\n" + "="*50 + "\n") | |
| # Process manual links | |
| parsed_links = link_manager.parse_manual_links(manual_links_input) | |
| if parsed_links["internal"] or parsed_links["external"]: | |
| results.append("π **MANUAL LINKS PROCESSED**") | |
| if parsed_links["internal"]: | |
| results.append("**Internal Links:**") | |
| for link in parsed_links["internal"]: | |
| results.append(f"β’ {link['anchor']} β {link['url']}") | |
| if parsed_links["external"]: | |
| results.append("**External Links:**") | |
| for link in parsed_links["external"]: | |
| results.append(f"β’ {link['anchor']} β {link['url']}") | |
| results.append("\n" + "="*50 + "\n") | |
| # Step 1: Keyword Research | |
| if include_research: | |
| results.append("π **STEP 1: KEYWORD RESEARCH**") | |
| keywords_data = researcher.suggest_keywords(seed_keyword, model) | |
| # Integrate manual keywords | |
| if manual_keywords: | |
| keywords_data = keyword_processor.integrate_manual_keywords(keywords_data, manual_keywords) | |
| # Format keyword research results | |
| results.append("**Primary Keywords:**") | |
| for kw in keywords_data.get("primary_keywords", [])[:5]: | |
| results.append(f"β’ {kw['keyword']} (Volume: {kw['search_volume']}, Difficulty: {kw['difficulty']})") | |
| # Show manual keywords separately | |
| if manual_keywords: | |
| results.append("\n**Manual Target Keywords:**") | |
| for kw in keywords_data.get("manual_targets", []): | |
| results.append(f"β’ {kw['keyword']} (Manual Target)") | |
| results.append("\n**Long-tail Keywords:**") | |
| for kw in keywords_data.get("long_tail", [])[:3]: | |
| results.append(f"β’ {kw['keyword']} (Intent: {kw['intent']})") | |
| results.append("\n**Question Keywords:**") | |
| for kw in keywords_data.get("questions", [])[:3]: | |
| results.append(f"β’ {kw['keyword']}") | |
| results.append("\n" + "="*50 + "\n") | |
| else: | |
| keywords_data = {"primary_keywords": [{"keyword": seed_keyword, "search_volume": "medium", "difficulty": "medium", "intent": "informational"}]} | |
| if manual_keywords: | |
| keywords_data = keyword_processor.integrate_manual_keywords(keywords_data, manual_keywords) | |
| # Step 2: Competitor Analysis | |
| if include_competitor: | |
| results.append("π **STEP 2: COMPETITOR ANALYSIS**") | |
| competitor_data = competitor_analyzer.analyze_top_competitors(seed_keyword, model) | |
| results.append(competitor_data["analysis"]) | |
| results.append("\n**Content Gaps Identified:**") | |
| for gap in competitor_data["content_gaps"]: | |
| results.append(f"β’ {gap}") | |
| results.append("\n" + "="*50 + "\n") | |
| else: | |
| competitor_data = {"content_gaps": [], "analysis": ""} | |
| # Step 3: Content Outline | |
| if include_outline: | |
| results.append("π **STEP 3: SEO-OPTIMIZED OUTLINE**") | |
| if custom_outline.strip(): | |
| outline = custom_outline | |
| else: | |
| outline = outliner.generate_seo_outline(seed_keyword, keywords_data, competitor_data, model) | |
| results.append(outline) | |
| results.append("\n" + "="*50 + "\n") | |
| else: | |
| outline = custom_outline if custom_outline.strip() else f"Article about {seed_keyword}" | |
| # Step 4: Generate Main Content | |
| results.append("βοΈ **STEP 4: FULL ARTICLE CONTENT**") | |
| # Extract all keywords for content generation | |
| all_keywords = [seed_keyword] | |
| # Add manual keywords first (highest priority) | |
| all_keywords.extend(manual_keywords) | |
| # Add auto-generated keywords | |
| for category in keywords_data.values(): | |
| if isinstance(category, list): | |
| all_keywords.extend([k["keyword"] for k in category[:2]]) # Top 2 from each category | |
| # Prepare manual links for content integration | |
| manual_links_formatted = link_manager.format_links_for_content(parsed_links) | |
| # Enhanced content generation prompt | |
| content_prompt = f""" | |
| Write a comprehensive, SEO-optimized blog post based on this outline: | |
| {outline} | |
| REQUIREMENTS: | |
| - Primary keyword: "{seed_keyword}" | |
| - MUST integrate these manual target keywords naturally: {', '.join(manual_keywords) if manual_keywords else 'None specified'} | |
| - Also include these keywords: {', '.join(all_keywords[len(manual_keywords)+1:15])} | |
| - Tone: {tone}, POV: {pov}, Emotion: {emotion} | |
| - Length: {length} | |
| - Write in genuinely human style - avoid AI phrases | |
| - Use HTML formatting for WordPress | |
| - Include specific examples and actionable advice | |
| - Optimize for readability with short paragraphs | |
| LINKS TO INTEGRATE: | |
| {manual_links_formatted if manual_links_formatted else 'No manual links provided'} | |
| Instructions for links: | |
| - Naturally integrate the provided internal and external links where contextually relevant | |
| - Add [INTERNAL LINK OPPORTUNITY] markers for additional internal link suggestions | |
| - Add [IMAGE: description] placeholders for visuals | |
| Content should be comprehensive, engaging, and provide real value to readers. | |
| Make it feel like it's written by an expert who genuinely cares about helping the reader. | |
| Prioritize the manual target keywords - they should appear naturally throughout the content. | |
| """ | |
| try: | |
| content_response = model.generate_content(content_prompt) | |
| main_content = content_response.text | |
| results.append(main_content) | |
| except Exception as e: | |
| results.append(f"Error generating main content: {str(e)}") | |
| main_content = f"Content about {seed_keyword} would appear here." | |
| results.append("\n" + "="*50 + "\n") | |
| # Step 5: Featured Snippet Optimization | |
| if include_snippets: | |
| results.append("π― **STEP 5: FEATURED SNIPPET OPTIMIZATION**") | |
| questions = [kw["keyword"] for kw in keywords_data.get("questions", [])] | |
| if not questions: | |
| questions = [f"What is {seed_keyword}?", f"How to use {seed_keyword}?", f"Benefits of {seed_keyword}?"] | |
| snippet_content = snippet_optimizer.optimize_for_snippets(main_content, questions, model) | |
| results.append("**FAQ Section Optimized for Featured Snippets:**") | |
| results.append(snippet_content) | |
| results.append("\n" + "="*50 + "\n") | |
| # Step 6: Internal Linking Strategy | |
| if include_linking: | |
| results.append("π **STEP 6: LINKING STRATEGY**") | |
| # Show processed manual links | |
| if parsed_links["internal"] or parsed_links["external"]: | |
| results.append("**Your Manual Links (Already Integrated):**") | |
| if parsed_links["internal"]: | |
| results.append("*Internal Links:*") | |
| for link in parsed_links["internal"]: | |
| results.append(f"β’ {link['anchor']} β {link['url']}") | |
| if parsed_links["external"]: | |
| results.append("*External Links:*") | |
| for link in parsed_links["external"]: | |
| results.append(f"β’ {link['anchor']} β {link['url']}") | |
| results.append("") | |
| # Suggest additional internal links | |
| link_suggestions = link_manager.suggest_internal_links(main_content, seed_keyword, parsed_links, model) | |
| results.append("**Additional Internal Link Suggestions:**") | |
| for i, suggestion in enumerate(link_suggestions, 1): | |
| results.append(f"{i}. {suggestion['text']}") | |
| results.append("\n" + "="*50 + "\n") | |
| # Step 7: Image Strategy | |
| if include_images: | |
| results.append("πΌοΈ **STEP 7: IMAGE STRATEGY**") | |
| image_strategy = image_strategist.create_image_strategy(outline, seed_keyword, model) | |
| results.append(f"**Recommended Images: {image_strategy['image_count']}**") | |
| results.append(image_strategy["strategy"]) | |
| results.append("\n**Optimized Alt Texts:**") | |
| for i, alt_text in enumerate(image_strategy["alt_texts"], 1): | |
| results.append(f"{i}. {alt_text}") | |
| results.append("\n" + "="*50 + "\n") | |
| # Step 8: Meta Data | |
| results.append("π **STEP 8: SEO META DATA**") | |
| # Generate meta title and description | |
| meta_prompt = f""" | |
| Create SEO-optimized meta data for this content: | |
| Primary keyword: {seed_keyword} | |
| Manual target keywords: {', '.join(manual_keywords) if manual_keywords else 'None'} | |
| Content summary: {main_content[:500]}... | |
| Generate: | |
| 1. Meta title (under 60 characters, include primary keyword and/or manual keywords) | |
| 2. Meta description (under 160 characters, compelling and click-worthy) | |
| 3. 3 alternative title variations | |
| Prioritize the manual target keywords in meta data if provided. | |
| """ | |
| try: | |
| meta_response = model.generate_content(meta_prompt) | |
| results.append(meta_response.text) | |
| except: | |
| results.append(f"**Meta Title:** {seed_keyword.title()} - Complete Guide") | |
| results.append(f"**Meta Description:** Discover everything about {seed_keyword} in this comprehensive guide. Get actionable tips and expert insights.") | |
| # Final analysis including manual keywords | |
| word_count = len(main_content.split()) | |
| flesch_score = textstat.flesch_reading_ease(main_content) if main_content else 0 | |
| # Count manual keyword usage | |
| manual_keyword_usage = {} | |
| if manual_keywords: | |
| for kw in manual_keywords: | |
| count = main_content.lower().count(kw.lower()) | |
| manual_keyword_usage[kw] = count | |
| results.append(f"\n**Content Analysis:**") | |
| results.append(f"β’ Word Count: {word_count}") | |
| results.append(f"β’ Readability Score: {flesch_score:.1f}") | |
| results.append(f"β’ Total Keywords Integrated: {len(all_keywords)}") | |
| if manual_keyword_usage: | |
| results.append(f"β’ Manual Keywords Usage:") | |
| for kw, count in manual_keyword_usage.items(): | |
| density = round((count / word_count) * 100, 2) if word_count > 0 else 0 | |
| results.append(f" - '{kw}': {count} times ({density}% density)") | |
| if parsed_links["internal"] or parsed_links["external"]: | |
| results.append(f"β’ Manual Links Integrated: {len(parsed_links['internal']) + len(parsed_links['external'])}") | |
| return "\n".join(results) | |
| # --- Gradio Interface --- | |
| with gr.Blocks(css=""" | |
| #generate_button { | |
| background: linear-gradient(45deg, #10b981, #059669) !important; | |
| color: white !important; | |
| font-weight: bold !important; | |
| border: none !important; | |
| } | |
| .gradio-container { max-width: 1200px !important; } | |
| .step-header { color: #059669; font-weight: bold; } | |
| """) as demo: | |
| gr.Markdown("# π SeoPlan2Article v4 - Complete SEO Content System") | |
| gr.Markdown("*Full workflow: Keyword Research β Competitor Analysis β Content Outline β Article Generation β SEO Optimization β Link Strategy β Image Planning*") | |
| with gr.Accordion("π API Configuration", open=True): | |
| api_key_input = gr.Textbox( | |
| label="Gemini API Key", | |
| type="password", | |
| placeholder="Enter your Gemini API key..." | |
| ) | |
| seed_keyword_input = gr.Textbox( | |
| label="Seed Keyword", | |
| placeholder="e.g., sustainable gardening tips", | |
| info="Primary keyword for research and content generation" | |
| ) | |
| with gr.Accordion("π Content Outline (Optional)", open=False): | |
| custom_outline_input = gr.Textbox( | |
| label="Custom Content Outline", | |
| lines=8, | |
| placeholder="Leave blank to auto-generate, or paste your outline here...", | |
| info="If provided, this will be used instead of auto-generated outline" | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| pov_input = gr.Dropdown( | |
| label="π Point of View", | |
| choices=["First Person (I/We)", "Second Person (You/Your)", "Third Person (He/She/It/They)"], | |
| value="Second Person (You/Your)" | |
| ) | |
| tone_input = gr.Dropdown( | |
| label="π¨ Tone", | |
| choices=["Friendly", "Professional", "Witty", "Motivational", "Reassuring", "Authoritative"], | |
| value="Professional" | |
| ) | |
| with gr.Column(): | |
| length_input = gr.Dropdown( | |
| label="π Article Length", | |
| choices=["Short (800-1200)", "Standard (1500-2500)", "Long (2500-4000)", "Very Long (4000+)"], | |
| value="Standard (1500-2500)" | |
| ) | |
| emotion_input = gr.Dropdown( | |
| label="π Emotional Tone", | |
| choices=["Trust", "Excitement", "Curiosity", "Confidence", "Inspiration", "Urgency"], | |
| value="Trust" | |
| ) | |
| with gr.Accordion("π§ Workflow Steps", open=True): | |
| gr.Markdown("Select which steps to include in your SEO content workflow:") | |
| with gr.Row(): | |
| include_research = gr.Checkbox(label="π Keyword Research", value=True) | |
| include_competitor = gr.Checkbox(label="π Competitor Analysis", value=True) | |
| include_outline = gr.Checkbox(label="π Auto-Generate Outline", value=True) | |
| with gr.Row(): | |
| include_snippets = gr.Checkbox(label="π― Featured Snippet Optimization", value=True) | |
| include_linking = gr.Checkbox(label="π Internal Link Strategy", value=True) | |
| include_images = gr.Checkbox(label="πΌοΈ Image Strategy", value=True) | |
| with gr.Accordion("βοΈ Manual Targeting & Links", open=True): | |
| gr.Markdown("### π― Manual Target Keywords") | |
| manual_target_keywords_input = gr.Textbox( | |
| label="Manual Target Keywords", | |
| placeholder="fermented pickle, homemade fermentation, pickle fermentation process", | |
| lines=3, | |
| info="Comma-separated keywords you specifically want to target. These get PRIORITY in content generation." | |
| ) | |
| gr.Markdown("### π Manual Internal & External Links") | |
| manual_links_input = gr.Textbox( | |
| label="Links to Include", | |
| placeholder="fermented pickle guide: https://www.website.com/fermented-pickle-at-home\nhealthy fermentation: /internal-page\nfermentation benefits: https://external-authority.com/benefits", | |
| lines=6, | |
| info="Format: 'Anchor Text: URL' (one per line). Use full URLs for external, relative paths for internal." | |
| ) | |
| custom_cta_input = gr.Textbox( | |
| label="Custom Call-to-Action", | |
| placeholder="Leave blank for auto-generated CTA", | |
| info="Optional: Specify your preferred call-to-action" | |
| ) | |
| generate_btn = gr.Button("π Generate Complete SEO Content System", elem_id="generate_button", size="lg") | |
| output = gr.Markdown("Your complete SEO content analysis will appear here...") | |
| generate_btn.click( | |
| fn=generate_complete_seo_content, | |
| inputs=[ | |
| api_key_input, seed_keyword_input, custom_outline_input, pov_input, tone_input, | |
| length_input, emotion_input, include_research, include_competitor, include_outline, | |
| include_snippets, include_linking, include_images, manual_target_keywords_input, | |
| manual_links_input, custom_cta_input | |
| ], | |
| outputs=output | |
| ) | |
| gr.Markdown(""" | |
| ## π― What This System Does: | |
| **Complete SEO Workflow Coverage:** | |
| 1. **Manual Targeting** - Specify exact keywords and links you want prioritized | |
| 2. **Keyword Research** - Find primary, long-tail, and question keywords with intent analysis | |
| 3. **Competitor Analysis** - Identify content gaps and differentiation opportunities | |
| 4. **Content Outline** - Generate SEO-optimized H1/H2/H3 structure | |
| 5. **Article Generation** - Write comprehensive, human-like content with your manual targets | |
| 6. **Featured Snippet Optimization** - Format FAQs and answers for Google snippets | |
| 7. **Link Strategy** - Integrate your manual links + suggest additional opportunities | |
| 8. **Image Strategy** - Plan visual content with SEO-optimized alt texts | |
| 9. **Meta Data** - Generate optimized titles and descriptions | |
| **π NEW Manual Targeting Features:** | |
| - **Priority Keywords**: Your manual keywords get highest priority in content | |
| - **Smart Link Integration**: Automatically integrates your internal/external links contextually | |
| - **Link Classification**: Automatically detects internal vs external links | |
| - **Usage Tracking**: Shows exactly how many times your manual keywords appear | |
| - **Density Analysis**: Calculates keyword density for your target terms | |
| **Link Format Examples:** | |
| ``` | |
| fermented pickle guide: https://www.website.com/fermented-pickle-at-home | |
| healthy fermentation: /internal-fermentation-page | |
| scientific study: https://pubmed.ncbi.nlm.nih.gov/study-link | |
| ``` | |
| **Enhanced Features:** | |
| - Human tone enforcement (avoid AI-like phrases) | |
| - Semantic keyword integration | |
| - Competitor content gap analysis | |
| - Featured snippet formatting | |
| - Strategic image placement | |
| - Manual + automatic linking strategy | |
| - Complete meta data optimization | |
| - Priority keyword tracking | |
| """) | |
| if __name__ == "__main__": | |
| demo.launch(debug=True, show_error=True) |