Skip to main content
Glama

YouTube to LinkedIn MCP Server

post_generation_service.py7.32 kB
import logging import os from typing import Dict, Any, List, Optional import openai import re from app.models.models import ToneEnum, AudienceEnum, VoiceEnum logger = logging.getLogger(__name__) class PostGenerationService: def __init__(self): self.api_key = os.getenv("OPENAI_API_KEY") if not self.api_key: logger.warning("OPENAI_API_KEY not found in environment variables") else: openai.api_key = self.api_key async def generate_post( self, summary: str, video_title: str, video_url: str, speaker_name: Optional[str] = None, hashtags: Optional[List[str]] = None, tone: ToneEnum = ToneEnum.PROFESSIONAL, voice: VoiceEnum = VoiceEnum.FIRST_PERSON, audience: AudienceEnum = AudienceEnum.GENERAL, include_call_to_action: bool = True, max_length: int = 1200, api_key: Optional[str] = None ) -> Dict[str, Any]: """Generate a LinkedIn post based on the video summary.""" # Use the provided API key if available, otherwise fall back to the environment variable openai_api_key = api_key or self.api_key if not openai_api_key: error_msg = "OpenAI API key not configured" logger.error(error_msg) return {"error": error_msg, "post_content": ""} if not summary: error_msg = "Empty summary provided" logger.error(error_msg) return {"error": error_msg, "post_content": ""} try: # Prepare the prompt for the language model prompt = self._create_post_prompt( summary=summary, video_title=video_title, video_url=video_url, speaker_name=speaker_name, hashtags=hashtags, tone=tone, voice=voice, audience=audience, include_call_to_action=include_call_to_action, max_length=max_length ) # Call the OpenAI API client = openai.OpenAI(api_key=openai_api_key) response = client.chat.completions.create( model="gpt-4", # Use appropriate model based on needs messages=[ {"role": "system", "content": "You are a professional content creator specializing in LinkedIn posts that drive engagement."}, {"role": "user", "content": prompt} ], temperature=0.7, max_tokens=1000, top_p=1.0, frequency_penalty=0.0, presence_penalty=0.0 ) # Process the response post_content = response.choices[0].message.content.strip() # Extract hashtags used in the post hashtags_used = self._extract_hashtags(post_content) # Calculate character count character_count = len(post_content) # Calculate estimated read time (average reading speed: 265 characters per minute) read_time_minutes = character_count / 1325 # 265 chars/min * 5 = 1325 chars/5min if read_time_minutes < 1: estimated_read_time = "Less than a minute" else: estimated_read_time = f"About {int(read_time_minutes)} minute{'s' if int(read_time_minutes) > 1 else ''}" return { "post_content": post_content, "character_count": character_count, "estimated_read_time": estimated_read_time, "hashtags_used": hashtags_used } except Exception as e: error_msg = f"Error generating LinkedIn post: {str(e)}" logger.error(error_msg) return {"error": error_msg, "post_content": ""} def _create_post_prompt( self, summary: str, video_title: str, video_url: str, speaker_name: Optional[str], hashtags: Optional[List[str]], tone: ToneEnum, voice: VoiceEnum, audience: AudienceEnum, include_call_to_action: bool, max_length: int ) -> str: """Create a prompt for the language model.""" audience_descriptions = { AudienceEnum.GENERAL: "general professionals on LinkedIn", AudienceEnum.TECHNICAL: "technical professionals and specialists", AudienceEnum.EXECUTIVE: "executives and decision-makers", AudienceEnum.ENTRY_LEVEL: "professionals new to the field", AudienceEnum.INDUSTRY_SPECIFIC: "industry-specific professionals" } tone_descriptions = { ToneEnum.EDUCATIONAL: "educational and informative", ToneEnum.INSPIRATIONAL: "inspirational and motivational", ToneEnum.PROFESSIONAL: "professional and authoritative", ToneEnum.CONVERSATIONAL: "conversational and approachable", ToneEnum.THOUGHT_LEADER: "thought-provoking and visionary" } voice_descriptions = { VoiceEnum.FIRST_PERSON: "first-person (using I, we, my, our)", VoiceEnum.THIRD_PERSON: "third-person (objective, reporting style)" } hashtag_str = "" if hashtags and len(hashtags) > 0: hashtag_str = "Include these hashtags in your post (preferably at the end): " + ", ".join([f"#{tag.strip('#')}" for tag in hashtags]) speaker_str = "" if speaker_name: speaker_str = f"The speaker/creator of the video is: {speaker_name}" cta_str = "" if include_call_to_action: cta_str = "Include a soft call to action at the end (e.g., asking for thoughts, suggesting to watch the video, etc.)" prompt = f""" Create an engaging LinkedIn post based on a YouTube video summary. Video Title: {video_title} Video URL: {video_url} {speaker_str} Post requirements: - Maximum length: {max_length} characters (LinkedIn optimal length) - Tone: {tone_descriptions.get(tone, "professional")} - Voice: {voice_descriptions.get(voice, "first-person")} - Target Audience: {audience_descriptions.get(audience, "general professionals")} - Structure: Start with an engaging hook, share insights from the video, and end with a thought-provoking question or call to action - Format: Use line breaks and emojis appropriately to make the post visually appealing and easy to read - Include the video URL somewhere in the post {hashtag_str} {cta_str} Here is the summary of the video: {summary} Create a LinkedIn post that feels authentic, valuable, and encourages engagement. """ return prompt def _extract_hashtags(self, post_content: str) -> List[str]: """Extract hashtags from the post content.""" # Find all hashtags in the post hashtags = re.findall(r'#(\w+)', post_content) return [f"#{tag}" for tag in hashtags]

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/NvkAnirudh/YT-to-LinkedIn-MCP-Server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server