import streamlit as st import os import json from datetime import datetime from crewai import Agent, Task, Crew from langchain_openai import OpenAI from openai import OpenAI as OpenAIClient import logging import requests import replicate import random from PIL import Image, ImageDraw, ImageFont from io import BytesIO import textwrap # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Constants for directory paths BASE_DIR = "data" QUOTES_DIR = os.path.join(BASE_DIR, "quotes") IMAGES_DIR = os.path.join(BASE_DIR, "images") FINAL_IMAGES_DIR = os.path.join(BASE_DIR, "final_images") MUSIC_DIR = os.path.join(BASE_DIR, "music") # New directory for storing music files # Ensure the directories exist try: os.makedirs(QUOTES_DIR, exist_ok=True) os.makedirs(IMAGES_DIR, exist_ok=True) os.makedirs(FINAL_IMAGES_DIR, exist_ok=True) os.makedirs(MUSIC_DIR, exist_ok=True) # Create music directory logger.info(f"Directories successfully created or verified: {BASE_DIR}, {QUOTES_DIR}, {IMAGES_DIR}, {FINAL_IMAGES_DIR}, {MUSIC_DIR}") except Exception as e: logger.error(f"Error creating directories: {str(e)}") def log_message(message, type="info"): if 'process_logs' not in st.session_state: st.session_state.process_logs = [] timestamp = datetime.now().strftime("%H:%M:%S") st.session_state.process_logs.append({ "timestamp": timestamp, "message": message, "type": type }) class SpiritualThemes: TRADITIONS = { "Eastern": ["Buddhism", "Hinduism", "Taoism", "Zen", "Vedanta", "Yoga Philosophy"], "Western": ["Christian Mysticism", "Sufi Wisdom", "Kabbalah", "Greek Philosophy"], "Indigenous": ["Native American Wisdom", "Aboriginal Spirituality", "African Traditional"], "Modern": ["Mindfulness", "Contemporary Philosophy", "Integrative Spirituality"] } THEMES = ["Inner Peace", "Mindfulness", "Compassion", "Wisdom", "Self-Discovery", "Unity", "Balance", "Transformation", "Enlightenment", "Purpose"] QUOTE_STYLES = ["contemplative", "inspirational", "philosophical", "practical wisdom", "mystical insight", "ancient teaching", "modern interpretation"] class ContentGenerator: def __init__(self, openai_api_key, replicate_api_key): self.llm = OpenAI(api_key=openai_api_key, temperature=0.7) self.openai_client = OpenAIClient(api_key=openai_api_key) self.replicate_client = replicate.Client(api_token=replicate_api_key) def _generate_prompt_variation(self): tradition_category = random.choice(list(SpiritualThemes.TRADITIONS.keys())) themes = random.sample(SpiritualThemes.THEMES, 2) return f"""Generate a spiritual quote from {tradition_category} wisdom tradition. Focus on themes of {themes[0]} and {themes[1]}. Make it profound yet accessible. Format: Return only the quote text on first line, then author name on second line.""" def _get_fallback_quote(self): fallback_quotes = [ {"text": "Be the change you wish to see in the world.", "author": "Mahatma Gandhi", "tradition": "Modern Spirituality"}, {"text": "Peace comes from within. Do not seek it without.", "author": "Buddha", "tradition": "Buddhism"}, {"text": "Love is the bridge between you and everything.", "author": "Rumi", "tradition": "Sufi Wisdom"} ] quote = random.choice(fallback_quotes) quote.update({ "theme": random.choice(SpiritualThemes.THEMES), "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "source": "fallback" }) log_message("â„šī¸ Using fallback quote system", "info") return quote def generate_quote(self): try: log_message("đŸŽ¯ Starting quote generation") response = self.openai_client.chat.completions.create( model="gpt-4", messages=[{ "role": "system", "content": "Generate a spiritual quote with attribution." }, { "role": "user", "content": self._generate_prompt_variation() }], temperature=0.7 ) text = response.choices[0].message.content lines = text.split('\n') quote_text = lines[0] if lines else "" author = lines[1].replace('-', '').strip() if len(lines) > 1 else "Unknown" tradition = random.choice(list(SpiritualThemes.TRADITIONS.keys())) theme = random.choice(SpiritualThemes.THEMES) result = { "text": quote_text, "author": author, "tradition": tradition, "theme": theme, "generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S") } # Save the quote to a file filename = f"quote_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json" file_path = os.path.join(QUOTES_DIR, filename) try: with open(file_path, 'w') as f: json.dump(result, f, indent=4) log_message(f"✨ Generated quote saved at {file_path}") logger.info(f"Quote saved successfully at: {file_path}") except Exception as e: log_message(f"❌ Failed to save quote to file: {str(e)}", "error") logger.error(f"Error saving quote: {str(e)}") return result except Exception as e: log_message(f"❌ Quote generation failed: {str(e)}") return self._get_fallback_quote() def generate_image(self, quote, tradition, theme): try: log_message("🎨 Starting image generation") style_prompts = { "Buddhism": "zen minimalism, misty mountains, lotus flowers", "Hinduism": "mandala patterns, sacred geometry, vibrant cosmos", "Taoism": "flowing water, yin-yang harmony, natural elements", "default": "serene minimalism, sacred geometry, natural elements" } style = style_prompts.get(tradition, style_prompts["default"]) prompt = f"""Create a spiritual and contemplative image inspired by this theme: '{theme}' Style inspiration: {style} Mood: serene, inspiring, contemplative Requirements: Suitable for Instagram, space for text overlay, balanced composition""" response = self.openai_client.images.generate( model="dall-e-3", prompt=prompt, size="1024x1024", quality="standard", n=1 ) image_url = response.data[0].url # Download and save the generated image image_response = requests.get(image_url) if image_response.status_code == 200: filename = f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg" file_path = os.path.join(IMAGES_DIR, filename) try: with open(file_path, 'wb') as f: f.write(image_response.content) log_message(f"✨ Image generated and saved at {file_path}") logger.info(f"Image saved successfully at: {file_path}") except Exception as e: log_message(f"❌ Failed to save image to file: {str(e)}", "error") logger.error(f"Error saving image: {str(e)}") else: raise Exception("Failed to download image") return image_url except Exception as e: log_message(f"❌ Image generation failed: {str(e)}", "error") logger.error(f"Image generation failed: {str(e)}") raise def generate_audio(self, tradition, theme): try: log_message("đŸŽĩ Starting audio generation") music_prompts = { "Buddhism": "Tibetan singing bowls and gentle bells with peaceful ambient drones", "Hinduism": "Indian bansuri flute with gentle tabla rhythms", "Modern Spirituality": "Ambient synthesizer with crystal bowls", "default": "Peaceful ambient music with gentle bells" } prompt = music_prompts.get(tradition, music_prompts["default"]) output = self.replicate_client.run( "meta/musicgen:671ac645ce5e552cc63a54a2bbff63fcf798043055d2dac5fc9e36a837eedcfb", input={ "prompt": prompt, "model_version": "stereo-large", "output_format": "mp3", "duration": 15 } ) audio_url = output if isinstance(output, str) else str(output) # Download and save the generated music music_response = requests.get(audio_url) if music_response.status_code == 200: filename = f"music_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp3" file_path = os.path.join(MUSIC_DIR, filename) try: with open(file_path, 'wb') as f: f.write(music_response.content) log_message(f"✨ Music generated and saved at {file_path}") logger.info(f"Music saved successfully at: {file_path}") except Exception as e: log_message(f"❌ Failed to save music to file: {str(e)}", "error") logger.error(f"Error saving music: {str(e)}") else: raise Exception("Failed to download audio") return audio_url except Exception as e: log_message(f"❌ Music generation failed: {str(e)}", "error") logger.error(f"Music generation failed: {str(e)}") raise def create_image_with_quote(self, image_url, quote_text, author): try: response = requests.get(image_url) img = Image.open(BytesIO(response.content)) width, height = img.size # Create blank image for text overlay txt = Image.new('RGBA', (width, height), (255, 255, 255, 0)) d = ImageDraw.Draw(txt) # Semi-transparent background for text d.rectangle([0, height / 2 - 100, width, height / 2 + 100], fill=(0, 0, 0, 127)) try: quote_font = ImageFont.truetype("DejaVuSans-Bold", 40) author_font = ImageFont.truetype("DejaVuSans", 30) except: quote_font = author_font = ImageFont.load_default() # Word wrap the quote text max_width = width - 40 # Leave some padding on both sides lines = textwrap.wrap(quote_text, width=40) # Draw each line of the wrapped text y_offset = height / 2 - 100 # Starting Y position for the quote for line in lines: # Calculate the text size using textbbox bbox = d.textbbox((0, 0), line, font=quote_font) line_width = bbox[2] - bbox[0] line_height = bbox[3] - bbox[1] x_position = (width - line_width) / 2 # Center the line horizontally d.text((x_position, y_offset), line, font=quote_font, fill=(255, 255, 255, 255)) y_offset += line_height + 5 # Move to the next line, with a small line gap # Draw the author text below the quote author_y_position = y_offset + 20 # Leave some space below the quote for the author author_text = f"- {author}" author_bbox = d.textbbox((0, 0), author_text, font=author_font) author_width = author_bbox[2] - author_bbox[0] author_x_position = (width - author_width) / 2 d.text((author_x_position, author_y_position), author_text, font=author_font, fill=(255, 255, 255, 255)) # Combine the original image with the overlay out = Image.alpha_composite(img.convert('RGBA'), txt) # Convert the image to bytes and save to a file buffer = BytesIO() out.convert('RGB').save(buffer, format='JPEG') final_image_bytes = buffer.getvalue() filename = f"final_image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg" file_path = os.path.join(FINAL_IMAGES_DIR, filename) try: with open(file_path, 'wb') as f: f.write(final_image_bytes) log_message(f"✨ Final image saved at {file_path}") logger.info(f"Final image saved successfully at: {file_path}") except Exception as e: log_message(f"❌ Failed to save final image to file: {str(e)}", "error") logger.error(f"Error saving final image: {str(e)}") return final_image_bytes except Exception as e: log_message(f"Error creating overlay: {str(e)}", "error") logger.error(f"Error creating image with quote: {str(e)}") return None def generate_hashtags(tradition, theme): base_tags = ["#spirituality", "#mindfulness", "#wisdom", "#inspiration"] tradition_tags = { "Buddhism": ["#buddhism", "#zen", "#buddhist"], "Hinduism": ["#hinduism", "#vedanta", "#yoga"], "Taoism": ["#taoism", "#tao", "#balance"], "Modern Spirituality": ["#spiritual", "#awakening", "#growth"] } theme_tags = { "Inner Peace": ["#peace", "#calm", "#serenity"], "Mindfulness": ["#mindful", "#present", "#awareness"], "Wisdom": ["#wisdom", "#truth", "#knowledge"] } custom_tags = tradition_tags.get(tradition, []) + theme_tags.get(theme, []) return list(set(base_tags + custom_tags))[:10] def main(): st.set_page_config(page_title="Spiritual Content Generator", page_icon="đŸ•‰ī¸", layout="wide") # Initialize session states if 'process_logs' not in st.session_state: st.session_state.process_logs = [] for state_var in ['generated_content', 'generated_image', 'generated_audio']: if state_var not in st.session_state: st.session_state[state_var] = None if not all(key in st.secrets for key in ['OPENAI_API_KEY', 'REPLICATE_API_TOKEN']): st.error("Missing API keys in secrets!") st.stop() # Create columns for content and debug log col1, col2 = st.columns([2, 1]) with col1: st.title("đŸ•‰ī¸ Spiritual Content Generator") generator = ContentGenerator(st.secrets["OPENAI_API_KEY"], st.secrets["REPLICATE_API_TOKEN"]) # Step 1: Generate Quote if st.button("Generate Quote"): try: quote_data = generator.generate_quote() st.session_state.generated_content = quote_data st.success("✨ Quote generated!") st.json(quote_data) except Exception as e: st.error(f"Error generating quote: {str(e)}") # Step 2: Create Image with Quote if st.session_state.generated_content and st.button("Create Image"): try: image_url = generator.generate_image( st.session_state.generated_content["text"], st.session_state.generated_content["tradition"], st.session_state.generated_content["theme"] ) st.session_state.generated_image = image_url st.success("✨ Image created!") st.image(image_url) except Exception as e: st.error(f"Error generating image: {str(e)}") # Step 3: Generate Music if st.session_state.generated_content and st.button("Generate Music"): try: audio_url = generator.generate_audio( st.session_state.generated_content["tradition"], st.session_state.generated_content["theme"] ) st.session_state.generated_audio = audio_url st.success("✨ Music generated!") audio_response = requests.get(audio_url) if audio_response.status_code == 200: st.audio(audio_response.content, format='audio/mp3') except Exception as e: st.error(f"Error generating audio: {str(e)}") # Step 4: Preview Quote on Image if st.session_state.generated_content and st.session_state.generated_image: st.subheader("Final Preview") final_image = generator.create_image_with_quote( st.session_state.generated_image, st.session_state.generated_content['text'], st.session_state.generated_content['author'] ) if final_image: st.image(final_image) st.download_button("đŸ“Ĩ Download Image", final_image, "spiritual_quote.jpg", "image/jpeg") # Generate Instagram Caption hashtags = generate_hashtags( st.session_state.generated_content['tradition'], st.session_state.generated_content.get('theme', 'Wisdom') ) caption = f"""✨ Wisdom from {st.session_state.generated_content['tradition']} ✨ "{st.session_state.generated_content['text']}" - {st.session_state.generated_content['author']} {' '.join(hashtags)}""" st.text_area("Instagram Caption", caption, height=200) # Live debug log with col2: st.markdown("### 🔍 Live Debug Log") placeholder = st.empty() def update_logs(): with placeholder.container(): for log in st.session_state.process_logs: if log["type"] == "error": st.error(f"{log['timestamp']} - {log['message']}") else: st.info(f"{log['timestamp']} - {log['message']}") update_logs() if __name__ == "__main__": main()