Spaces:
Runtime error
Runtime error
Download app.py from anu151105/Contentkshxpost: direct link, hf CLI and curl.
- Browser
- Download file 18.8 kB
-
https://huggingface.co/spaces/anu151105/Contentkshxpost/resolve/main/app.py
- Command line
-
hf download hf://spaces/anu151105/Contentkshxpost/app.py
-
curl -L -o app.py https://huggingface.co/spaces/anu151105/Contentkshxpost/resolve/main/app.py
18.8 kB
| 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() |