import gradio as gr import numpy as np import random import requests import io import re from datetime import datetime from PIL import Image, ImageDraw, ImageFont import textwrap import google.generativeai as genai from diffusers import DiffusionPipeline import torch # Set device and model for SDXL-Turbo device = "cuda" if torch.cuda.is_available() else "cpu" model_repo_id = "stabilityai/sdxl-turbo" torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32 # Initialize SDXL-Turbo pipeline pipe = DiffusionPipeline.from_pretrained(model_repo_id, torch_dtype=torch_dtype) pipe = pipe.to(device) MAX_SEED = np.iinfo(np.int32).max MAX_IMAGE_SIZE = 1024 class CurrentAffairsGenerator: def __init__(self, gemini_api_key, news_api_key=None, logo_url=None): self.gemini_api_key = gemini_api_key self.news_api_key = news_api_key self.logo_url = logo_url self.newsapi_base_url = "https://newsapi.org/v2/everything" if self.gemini_api_key: try: genai.configure(api_key=self.gemini_api_key) self.gemini_model = genai.GenerativeModel('gemini-2.5-flash') except Exception as e: gr.Warning(f"Could not configure Gemini API: {e}.") self.gemini_model = None else: self.gemini_model = None self.brand_colors = { 'primary': '#0F172A', 'accent': '#F59E0B', 'text': '#FFFFFF', 'text_shadow': '#000000', } def fetch_latest_news(self, category): if self.news_api_key: gr.Info(f"Searching for live news about '{category}'...") try: news_data = self._search_with_newsapi(category) if news_data: gr.Info("Successfully fetched live news!") return news_data gr.Warning("Could not find relevant live news. Using a sample article.") except Exception as e: gr.Error(f"News API Error: {e}. Using a sample article.") return self._get_sample_news(category) def _search_with_newsapi(self, category): params = { 'q': f'"{category}" AND "India"', 'language': 'en', 'sortBy': 'publishedAt', 'pageSize': 10, 'apiKey': self.news_api_key } response = requests.get(self.newsapi_base_url, params=params) response.raise_for_status() articles = response.json().get('articles', []) for article in articles: content = article.get('content') description = article.get('description') full_content = content if content and len(content) > 10 else description if full_content: full_content_cleaned = re.sub(r'\[\s*\+\d+\s*chars\s*\]$', '', full_content).strip() if len(full_content_cleaned) > 50: return { 'title': article['title'], 'full_content': full_content_cleaned, 'source': article['source']['name'], 'category': category } return None def _get_sample_news(self, topic=None): sample_news = [ {'title': 'ISRO Confirms Gaganyaan Crew Module is Flight-Ready', 'full_content': 'The Indian Space Research Organisation has successfully completed all qualification tests for its first crewed space mission. This milestone includes rigorous vibration tests, acoustic assessments, and a series of parachute deployment simulations. The crew module is now officially certified for flight, bringing India one step closer to sending its own astronauts into space.', 'source': 'ISRO', 'category': 'Science'}, {'title': 'India Launches "Bharat AI" Research Supercluster', 'full_content': 'In a major push for artificial intelligence, the government has funded a new initiative to build a world-class AI ecosystem. This supercluster will connect top research institutions and startups, providing them with immense computing power and a platform for collaboration to solve India-specific challenges.', 'source': 'Ministry of Technology', 'category': 'Technology'}, {'title': 'UPI Transactions Cross 10 Billion Mark in a Single Month', 'full_content': 'India\'s Unified Payments Interface (UPI) has set a new global benchmark for digital payments, crossing the 10 billion transaction mark in a single month. This reflects the rapid and widespread adoption of digital finance across both urban and rural India, transforming the nation\'s economic landscape.', 'source': 'Reserve Bank of India', 'category': 'Economy'} ] if topic: topic_lower = topic.lower() filtered_news = [news for news in sample_news if topic_lower in news['category'].lower()] if filtered_news: return random.choice(filtered_news) return random.choice(sample_news) def generate_llm_content(self, news_data): if not self.gemini_model: return news_data['title'], [f"Key Update: {news_data['full_content']}"] prompt = f""" Act as an expert social media analyst. Analyze this news: Title: "{news_data['title']}" Content: "{news_data['full_content']}" Your Task: 1. Create one short, impactful headline (under 10 words). 2. Generate 3 highly informative bullet points. 3. IMPORTANT: For the bullet points, provide ONLY the text. DO NOT include any leading emojis or symbols like '☐', '-', or '*'. Output Format (Strictly follow this): HEADLINE: [Your headline here] BULLETS: [Your first insightful bullet point text] [Your second insightful bullet point text] [Your third insightful bullet point text] """ try: response = self.gemini_model.generate_content(prompt) clean_text = re.sub(r'[\*#]', '', response.text) headline = clean_text.split("HEADLINE:")[1].split("BULLETS:")[0].strip() bullets_str = clean_text.split("BULLETS:")[1].strip() bullets = [line.strip().lstrip('- ') for line in bullets_str.split('\n') if line.strip()] return headline, bullets[:3] except Exception as e: gr.Warning(f"Gemini content generation failed: {e}. Using fallback.") return news_data['title'], [news_data['full_content']] def create_image_prompt(self, news_data): category = news_data.get('category', 'General').lower() prompts = { 'technology': [ "Futuristic cityscape with holographic data streams and glowing circuits, cinematic lighting, deep blue and gold palette, 4K hyper-detailed digital art.", "Abstract visualization of a neural network, glowing nodes connected by light trails, dark background, symbolizing AI and connectivity.", "Sleek server room with a liquid cooling system, neon blue lights reflecting on polished surfaces, representing high-performance computing.", "Digital art of a human brain merging with a glowing circuit board, illustrating the concept of human-computer interface.", ], 'economy': [ "Abstract visualization of a bullish stock market graph turning into a growing tree with coin-like leaves, against a modern city skyline.", "A dynamic 3D render of the Indian Rupee symbol glowing brightly, surrounded by upward-trending financial charts and graphs.", "A bustling, modern marketplace where digital transactions are visualized as light flowing between people and vendors, symbolizing a digital economy.", "Photorealistic image of a new infrastructure project like a bridge or highway, bathed in golden morning light, representing national growth.", ], 'science': [ "A stunning view of a satellite orbiting Earth with the Indian subcontinent visible below, against a cosmic background of nebulae and stars.", "A clean, modern laboratory with scientists working with holographic displays showing DNA strands and molecular structures, high-tech aesthetic.", "Digital painting of the Gaganyaan spacecraft soaring through the upper atmosphere, leaving a trail of fire and smoke.", "Microscopic view of new material discovery, with crystalline structures glowing with internal light, abstract and beautiful.", ], 'environment': [ "Lush green Indian landscape integrating futuristic eco-technology like solar panels that follow the sun and artistic wind turbines.", "A pristine river flowing through a regenerated forest, with clear water and abundant wildlife, symbolizing ecological restoration.", "Concept art of a 'vertical forest' skyscraper in an Indian city, covered in plants and trees, promoting urban greening.", "A split view showing a polluted city on one side and a clean, green city on the other, highlighting the choice for a sustainable future.", ], } prompt_list = prompts.get(category, prompts['technology']) base_prompt = random.choice(prompt_list) return f"{base_prompt}, professional design, high resolution, visually striking for social media." def generate_background_image(self, news_data, seed, randomize_seed, width=1024, height=1024, guidance_scale=0.0, num_inference_steps=2): prompt = self.create_image_prompt(news_data) gr.Info(f"🎨 Generating a unique image with prompt: \"{prompt}\"") if randomize_seed: seed = random.randint(0, MAX_SEED) generator = torch.Generator().manual_seed(seed) try: image = pipe( prompt=prompt, negative_prompt="", guidance_scale=guidance_scale, num_inference_steps=num_inference_steps, width=width, height=height, generator=generator, ).images[0] return image, seed except Exception as e: gr.Error(f"Image generation failed: {e}. Using fallback image.") return self._create_fallback_image(), seed def _create_fallback_image(self): img = Image.new('RGB', (1080, 1080)) draw = ImageDraw.Draw(img) for i in range(1080): r, g, b = int(15 + 15 * (i/1080)), int(23 + 18 * (i/1080)), int(42 + 17 * (i/1080)) draw.line([(0, i), (1080, i)], fill=(r, g, b)) return img def _get_logo(self, size=60): if self.logo_url: try: response = requests.get(self.logo_url) response.raise_for_status() logo_img = Image.open(io.BytesIO(response.content)).convert("RGBA") logo_img.thumbnail((size, size)) return logo_img except Exception as e: gr.Warning(f"Failed to load logo from URL: {e}. Using fallback.") logo = Image.new('RGBA', (size, size), (0, 0, 0, 0)) draw = ImageDraw.Draw(logo) draw.ellipse([0, 0, size, size], fill=self.brand_colors['accent']) try: font = ImageFont.truetype("seguibld.ttf", int(size * 0.7)) except IOError: font = ImageFont.load_default() draw.text((size/2, size/2), "P", font=font, fill='white', anchor="mm") return logo def _draw_text_with_outline(self, draw, position, text, font, fill_color, shadow_color): x, y = position outline = 2 for dx in range(-outline, outline + 1): for dy in range(-outline, outline + 1): if dx != 0 or dy != 0: draw.text((x + dx, y + dy), text, font=font, fill=shadow_color) draw.text(position, text, font=font, fill=fill_color) def create_instagram_post(self, category, seed, randomize_seed, width=1024, height=1024, guidance_scale=0.0, num_inference_steps=2): news_data = self.fetch_latest_news(category) if not news_data: gr.Error("Could not retrieve any news story. Please try again.") return None, None, None, seed headline, bullet_points = self.generate_llm_content(news_data) bg_image, seed = self.generate_background_image(news_data, seed, randomize_seed, width, height, guidance_scale, num_inference_steps) bg_image = bg_image.resize((1080, 1080)) overlay = Image.new('RGBA', (1080, 1080), (0, 0, 0, 0)) draw = ImageDraw.Draw(overlay) draw.rectangle([0, 0, 1080, 1080], fill=(0, 0, 0, 80)) try: title_font = ImageFont.truetype("impact.ttf", 85) bullet_font = ImageFont.truetype("verdana.ttf", 36) brand_font = ImageFont.truetype("verdana.ttf", 28) date_font = ImageFont.truetype("verdanab.ttf", 24) except IOError: gr.Warning("Custom fonts not found. Using default fonts.") title_font, bullet_font, brand_font, date_font = (ImageFont.load_default(),)*4 draw.text((60, 60), "TODAY'S UPDATE", font=date_font, fill=self.brand_colors['accent']) y_pos = 150 for line in textwrap.wrap(headline.upper(), width=25): self._draw_text_with_outline(draw, (60, y_pos), line, title_font, self.brand_colors['text'], self.brand_colors['text_shadow']) y_pos += 95 y_pos = 480 content_area_end_y = 930 stop_drawing = False for point_text in bullet_points: if stop_drawing: break bullet_radius = 6 bullet_x = 70 first_line_height = bullet_font.getbbox('A')[3] bullet_y = y_pos + (first_line_height / 2) - bullet_radius if bullet_y > content_area_end_y: break draw.ellipse( (bullet_x, bullet_y, bullet_x + bullet_radius*2, bullet_y + bullet_radius*2), fill=self.brand_colors['accent'] ) text_x = bullet_x + bullet_radius*2 + 20 wrapped_text = textwrap.wrap(point_text, width=40) line_y = y_pos for line in wrapped_text: if line_y + first_line_height > content_area_end_y: stop_drawing = True break self._draw_text_with_outline(draw, (text_x, line_y), line, bullet_font, self.brand_colors['text'], self.brand_colors['text_shadow']) line_y += 45 y_pos = line_y + 20 draw.rectangle([0, 950, 1080, 1080], fill=self.brand_colors['primary']) logo = self._get_logo(size=100) final_image = Image.alpha_composite(bg_image.convert('RGBA'), overlay) final_image.paste(logo, (940, 965), logo) draw_final = ImageDraw.Draw(final_image) draw_final.text((60, 990), "+91 9354253889", font=brand_font, fill=self.brand_colors['text'], anchor="lm") draw_final.text((920, 990), "pragzo.com", font=brand_font, fill=self.brand_colors['accent'], anchor="rm") return final_image, headline, news_data, seed # Gradio Interface def generate_post(gemini_api_key, news_api_key, logo_url, category, seed, randomize_seed, width, height, guidance_scale, num_inference_steps, progress=gr.Progress(track_tqdm=True)): if not gemini_api_key: gr.Error("Please provide a Gemini API key.") return None, None, None, seed generator = CurrentAffairsGenerator(gemini_api_key, news_api_key, logo_url) image, headline, news_data, seed = generator.create_instagram_post(category, seed, randomize_seed, width, height, guidance_scale, num_inference_steps) if image: img_buffer = io.BytesIO() image.save(img_buffer, format='PNG') return image, headline, f"**Original Title:** {news_data['title']}\n**Source:** {news_data['source']}\n**Content Analyzed by AI:**\n\n> {news_data['full_content']}", seed return None, None, None, seed css = """ #col-container { margin: 0 auto; max-width: 800px; } """ with gr.Blocks(css=css) as demo: gr.Markdown("# 🚀 Pragzo | AI Current Affairs Post Generator") gr.Markdown("Instantly create stunning, unique Instagram posts from live news, powered by AI and SDXL-Turbo.") with gr.Column(elem_id="col-container"): with gr.Row(): gemini_api_key = gr.Textbox(label="Gemini API Key", type="password", placeholder="Enter your Gemini API key") news_api_key = gr.Textbox(label="News API Key (Optional)", type="password", placeholder="Enter your News API key") logo_url = gr.Textbox(label="Logo URL (Optional)", placeholder="https://.../logo.png") with gr.Row(): category = gr.Dropdown( label="News Category", choices=["Technology", "Economy", "Science", "Environment", "Sports", "Education"], value="Technology" ) run_button = gr.Button("🛰️ Generate Post", variant="primary") result = gr.Image(label="Generated Instagram Post", show_label=True) headline = gr.Textbox(label="AI-Generated Headline", interactive=False) news_content = gr.Markdown(label="Source News Content") with gr.Accordion("Advanced Image Settings", open=False): seed = gr.Slider(label="Seed", minimum=0, maximum=MAX_SEED, step=1, value=0) randomize_seed = gr.Checkbox(label="Randomize seed", value=True) with gr.Row(): width = gr.Slider(label="Width", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=1024) height = gr.Slider(label="Height", minimum=256, maximum=MAX_IMAGE_SIZE, step=32, value=1024) with gr.Row(): guidance_scale = gr.Slider(label="Guidance scale", minimum=0.0, maximum=10.0, step=0.1, value=0.0) num_inference_steps = gr.Slider(label="Number of inference steps", minimum=1, maximum=50, step=1, value=2) run_button.click( fn=generate_post, inputs=[gemini_api_key, news_api_key, logo_url, category, seed, randomize_seed, width, height, guidance_scale, num_inference_steps], outputs=[result, headline, news_content, seed] ) if __name__ == "__main__": demo.launch()