Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os | |
| import time | |
| import json | |
| from pydub import AudioSegment | |
| from pydub.playback import play | |
| import google.generativeai as genai | |
| import narration | |
| import img | |
| import vid | |
| import requests | |
| import base64 | |
| import urllib.parse | |
| import uuid | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| # Title and description | |
| st.set_page_config( | |
| page_title="Educational Video Generator", | |
| page_icon="logo.png", | |
| layout="centered", | |
| initial_sidebar_state="auto", | |
| ) | |
| # Define themes | |
| themes = { | |
| "blue": { | |
| "primaryColor": "#007B83", | |
| "backgroundColor": "#F5F5F5", | |
| "secondaryBackgroundColor": "#E0E0E0", | |
| "textColor": "#333333", | |
| "font": "sans serif", | |
| }, | |
| "Dark": { | |
| "primaryColor": "#2D2D2D", | |
| "backgroundColor": "#121212", | |
| "secondaryBackgroundColor": "#242424", | |
| "textColor": "#FFFFFF", | |
| "font": "sans serif", | |
| }, | |
| } | |
| ms = st.session_state | |
| if "themes" not in ms: | |
| ms.themes = { | |
| "current_theme": "light", | |
| "refreshed": True, | |
| "light": { | |
| "theme.base": "light", | |
| "theme.backgroundColor": "#fefae0", | |
| "theme.primaryColor": "#fcd303", | |
| "theme.secondaryBackgroundColor": "#E0E0E0", | |
| "theme.textColor": "#262730", | |
| "theme.font": "sans serif", | |
| "button_face": "🌜", | |
| }, | |
| "dark": { | |
| "theme.base": "light", | |
| "theme.backgroundColor": "#121212", | |
| "theme.primaryColor": "#2D2D2D", | |
| "theme.secondaryBackgroundColor": "#242424", | |
| "theme.textColor": "#FFFFFF", | |
| "button_face": "🌞", | |
| }, | |
| } | |
| def ChangeTheme(): | |
| previous_theme = ms.themes["current_theme"] | |
| tdict = ( | |
| ms.themes["light"] | |
| if ms.themes["current_theme"] == "light" | |
| else ms.themes["dark"] | |
| ) | |
| for vkey, vval in tdict.items(): | |
| if vkey.startswith("theme"): | |
| st._config.set_option(vkey, vval) | |
| ms.themes["refreshed"] = False | |
| if previous_theme == "dark": | |
| ms.themes["current_theme"] = "light" | |
| elif previous_theme == "light": | |
| ms.themes["current_theme"] = "dark" | |
| btn_face = ( | |
| ms.themes["light"]["button_face"] | |
| if ms.themes["current_theme"] == "light" | |
| else ms.themes["dark"]["button_face"] | |
| ) | |
| # Create a row of columns. Adjust the number inside the list to change the width ratio of the buttons if needed. | |
| col1, col2, col3, col4 = st.columns(4) | |
| # Place each button in its own column | |
| with col1: | |
| st.button(f"Change Theme: {btn_face}", on_click=ChangeTheme) | |
| with col2: | |
| st.link_button("Contact Us 📱","https://collegitesai.web.app/contactus") | |
| with col3: | |
| st.link_button("Report Issue 🚨","https://collegitesai.web.app/contactus") | |
| with col4: | |
| st.link_button("Share Idea 💡","https://collegitesai.web.app/contactus") | |
| # Separator and centered text remain unchanged | |
| st.markdown("---") | |
| if ms.themes["refreshed"] == False: | |
| ms.themes["refreshed"] = True | |
| st.rerun() | |
| # Header with title and description | |
| st.title(":movie_camera: Educational Video Generator") | |
| # Initialize instruction visibility in session state | |
| if "instruction_visibility" not in st.session_state: | |
| st.session_state.instruction_visibility = False | |
| if "examples_visibility" not in st.session_state: | |
| st.session_state.examples_visibility = False | |
| st.markdown( | |
| """ | |
| Welcome to the **Educational Video Generator**! Create personalized educational videos effortlessly. Select a prompt, provide source material, and watch your custom video come to life with detailed explanations and visuals. | |
| """ | |
| ) | |
| # Button to toggle instructions visibility | |
| if st.button("Show/Hide Instructions :bookmark_tabs:"): | |
| st.session_state.instruction_visibility = ( | |
| not st.session_state.instruction_visibility | |
| ) | |
| # Display instructions if visibility is True | |
| if st.session_state.instruction_visibility: | |
| st.markdown( | |
| """ | |
| ### How it Works: | |
| 1. **Select a Prompt**: Choose a topic or concept for your video. | |
| 2. **Provide Source Material**: Input text or documents to base your video on. | |
| 3. **Generate Your Video**: Sit back and let our system create a professional educational video for you. | |
| ### Features: | |
| - Customizable video length and style | |
| - Real-time visualizations and annotations | |
| - Export options for sharing or embedding | |
| Ready to start? Try it out now! | |
| """ | |
| ) | |
| # Button to toggle examples visibility | |
| if st.button("Show/Hide Examples :clapper:"): | |
| st.session_state.examples_visibility = not st.session_state.examples_visibility | |
| if st.session_state.examples_visibility: | |
| st.markdown("### Examples:") | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| st.markdown("#### Example 1:") | |
| st.video("https://www.youtube.com/watch?v=5q6ZOv_i-mE") | |
| with col2: | |
| st.markdown("#### Example 2:") | |
| st.video("https://www.youtube.com/watch?v=dbnHg5o5rvQ") | |
| # Prompt options | |
| prompt_options = [ | |
| { | |
| "title": "Last Minute Exam preapration Video for College Students", | |
| "short_description": "Create a video to help Indian college students grasp key concepts for exams.", | |
| "prompt": "You are creating an educational video for Indian college students to quickly grasp and retain key concepts for exams so create a complete explanatory video. The video will provide detailed explanations, supported by visuals and clear narration for each line in Hindi words. Follow these guidelines:", | |
| }, | |
| { | |
| "title": "Comprehensive Study Review: Key Concepts and Definitions", | |
| "short_description": "Create a video summarizing key concepts and definitions for effective exam preparation.", | |
| "prompt": "You are creating a study review video summarizing key concepts and definitions from provided notes. Each slide should focus on a specific concept or term, providing clear explanations and examples where applicable. Use visuals and bilingual narration to aid understanding and retention.", | |
| }, | |
| { | |
| "title": "Analytical Overview: Critical Analysis of Provided Topics", | |
| "short_description": "Develop a video providing a critical analysis of topics covered in the provided notes.", | |
| "prompt": "Produce educational content offering a critical analysis of topics from provided notes. Each slide should analyze a specific topic or issue, presenting insights, perspectives, and supporting evidence. Include visual aids and bilingual narration for clarity and depth.", | |
| }, | |
| { | |
| "title": "Practical Applications: Real-world Examples and Case Studies", | |
| "short_description": "Craft a video demonstrating practical applications of theories or concepts from the provided notes.", | |
| "prompt": "Create instructional slides showcasing practical applications of theories or concepts from provided notes. Each slide should feature real-world examples, case studies, or scenarios illustrating the application of knowledge. Use visuals and clear bilingual narration to enhance comprehension.", | |
| }, | |
| { | |
| "title": "Step-by-Step Guide: Detailed Procedures or Processes", | |
| "short_description": "Generate a video providing a step-by-step guide to procedures or processes outlined in the provided notes.", | |
| "prompt": "Develop educational slides offering a detailed step-by-step guide to procedures or processes from provided notes. Each slide should break down a specific procedure, method, or workflow, explaining each step clearly. Utilize diagrams, flowcharts, and bilingual narration for thorough understanding.", | |
| }, | |
| { | |
| "title": "Comparative Analysis: Contrasting Perspectives or Theories", | |
| "short_description": "Produce a video comparing and contrasting different perspectives or theories discussed in the provided notes.", | |
| "prompt": "You are creating study slides for comparative analysis based on provided notes. Each slide should compare and contrast different perspectives, theories, or approaches to a topic. Provide visual aids and bilingual narration to elucidate differences and similarities effectively.", | |
| }, | |
| ] | |
| # Function to read input_prompt from file or user selection | |
| def get_input_prompt(): | |
| selected_prompt = st.radio( | |
| "Select a prompt option", | |
| [option["title"] for option in prompt_options] + ["Custom"], | |
| ) | |
| if selected_prompt == "Custom": | |
| return st.text_area("Enter your own prompt", height=200) | |
| else: | |
| for option in prompt_options: | |
| if option["title"] == selected_prompt: | |
| st.text_area( | |
| "Prompt details: " + option["short_description"], | |
| value=option["prompt"], | |
| height=130, | |
| disabled=True, | |
| ) | |
| return option["prompt"] | |
| return None | |
| # Function to configure the generative AI model | |
| def configure_model(): | |
| api_key = os.environ.get("GEMINI_API_KEY") | |
| genai.configure(api_key=api_key) | |
| return genai.GenerativeModel( | |
| model_name="gemini-1.5-flash", | |
| generation_config={ | |
| "temperature": 0.4, | |
| "top_p": 1, | |
| "top_k": 32, | |
| "max_output_tokens": 8192, | |
| }, | |
| safety_settings=[ | |
| { | |
| "category": "HARM_CATEGORY_HARASSMENT", | |
| "threshold": "BLOCK_MEDIUM_AND_ABOVE", | |
| }, | |
| { | |
| "category": "HARM_CATEGORY_HATE_SPEECH", | |
| "threshold": "BLOCK_MEDIUM_AND_ABOVE", | |
| }, | |
| { | |
| "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", | |
| "threshold": "BLOCK_MEDIUM_AND_ABOVE", | |
| }, | |
| { | |
| "category": "HARM_CATEGORY_DANGEROUS_CONTENT", | |
| "threshold": "BLOCK_MEDIUM_AND_ABOVE", | |
| }, | |
| ], | |
| ) | |
| # Function to generate the script content | |
| def generate_script(model, prompt_option, source_material): | |
| input_prompt = f""" | |
| {prompt_option} | |
| Follow these guidelines and format strictly as we are scrapping the text form your response: | |
| Title | |
| Content: Use bullet points for clarity, use only english language for slide content. | |
| Image Description: For AI image search; avoid referencing real people, sexual content, or celebrities. | |
| Narration: Provide a narration in Hindi Devanagari script. Use simple Hindi language and mix with english words do not use too deep hindi words instead use english words. | |
| Image Descriptions: Ensure they complement the content. | |
| Format: | |
| Slide Number, Title, and Content in square brackets. | |
| Separate image description and narration. | |
| !in a line use single #(hash symbol) only, for slide number and title . stick to the example format strictly. | |
| !important to generate each thing mentioned in the example, do not skip anything | |
| Slide Structure Example: | |
| # Slide [Slide Number]: [Title] | |
| [Slide Content] | |
| - Point 1 | |
| - Point 2 | |
| - Point n | |
| [Image Description: Description of a fitting image related to content] | |
| *Narration: [Hindi narration in Devanagari script] | |
| and so on..... generate the content until all topics covered | |
| ## Continue generating content in this format for all topics | |
| ## !important narration should be in Hindi Devanagari script only and contnet and title in englsih only | |
| ``` | |
| **Key improvements:** | |
| * **Clearer narration instruction:** Directly specifies "Provide a narration in Hindi Devanagari script." | |
| * **Removed redundant information:** Removed the Hinglish and phonetics references as they are not necessary for generating Devanagari text. | |
| * **Simplified format:** Removed unnecessary details and focused on the essential requirements. | |
| """ | |
| response = model.generate_content(input_prompt + source_material) | |
| return ( | |
| response.text.replace("’", "'") | |
| .replace("`", "'") | |
| .replace("…", "...") | |
| .replace("“", '"') | |
| .replace("”", '"') | |
| ) | |
| # Function to save text to a file | |
| def save_text_to_file(text, path): | |
| with open(path, "w", encoding="utf-8") as f: | |
| f.write(text) | |
| # Function to save JSON data to a file | |
| def save_json_to_file(data, path): | |
| with open(path, "w", encoding="utf-8") as f: | |
| json.dump(data, f, ensure_ascii=False) | |
| # Function to upload video to GitHub | |
| def upload_to_github(video_path): | |
| # Extract the folder ID from the URL | |
| folder_id = str(uuid.uuid4()) | |
| # Replace with your GitHub repository details | |
| # Load values from environment variables | |
| repo_owner = os.environ.get("REPO_OWNER") | |
| repo_name = os.environ.get("REPO_NAME") | |
| github_token = os.environ.get("GITHUB_TOKEN") | |
| print(repo_owner, repo_name, github_token) | |
| github_file_path = f"videos/{folder_id}/short.mp4" | |
| print(f"GitHub File Path: {github_file_path}") # For debugging | |
| # GitHub API URL | |
| url = f"https://api.github.com/repos/{repo_owner}/{repo_name}/contents/{github_file_path}" | |
| print(f"GitHub API URL: {url}") # For debugging | |
| headers = { | |
| "Authorization": f"token {github_token}", | |
| "Accept": "application/vnd.github.v3+json", | |
| } | |
| # Local file path for reading the video file | |
| local_file_path = urllib.parse.urlparse(video_path).path | |
| # Read video file as binary data | |
| with open(local_file_path, "rb") as f: | |
| content = f.read() | |
| # Base64 encode the content | |
| encoded_content = base64.b64encode(content).decode("utf-8") | |
| # Prepare data for API request | |
| data = {"message": "Upload generated video", "content": encoded_content} | |
| # Make API request to create the file | |
| response = requests.put(url, headers=headers, json=data) | |
| if response.status_code in [200, 201]: | |
| print("File uploaded successfully to GitHub.") | |
| response_data = response.json() | |
| download_url = response_data["content"]["download_url"] | |
| print(f"Download URL: {download_url}") | |
| # os.remove(video_path) # Be cautious with automatically removing files | |
| return download_url | |
| else: | |
| print(f"Failed to upload file: {response.content}") | |
| return None | |
| # Main function to generate the video | |
| def generate_video(source_material, prompt_option, selected_bg): | |
| short_id = str(int(time.time())) # Generate a unique ID | |
| output_file = "short.mp4" | |
| basedir = os.path.join("videos", short_id) | |
| os.makedirs(basedir, exist_ok=True) | |
| model = configure_model() | |
| with st.spinner("Generating script..."): | |
| response_text = generate_script(model, prompt_option, source_material) | |
| save_text_to_file(response_text, os.path.join(basedir, "response.txt")) | |
| with st.spinner("Parsing response..."): | |
| data, narrations = narration.parse(response_text) | |
| # Now 'parsed_data' will be a list containing dictionaries for text and image information | |
| print(narrations) | |
| st.markdown("### Generated Narration:") | |
| st.text(narrations) | |
| save_json_to_file(data, os.path.join(basedir, "data.json")) | |
| with st.spinner("Generating narration..."): | |
| narration.create(data, os.path.join(basedir, "narrations")) | |
| with st.spinner("Generating images..."): | |
| img.create(data, os.path.join(basedir, "images")) | |
| with st.spinner("Generating video..."): | |
| vid.create(narrations, data, basedir, output_file, selected_bg) | |
| # Upload video directly to GitHub | |
| video_path = os.path.abspath(os.path.join(basedir, output_file)) | |
| video_path = video_path.replace("%5C", "/") | |
| print(video_path) | |
| giturl = upload_to_github(video_path) | |
| return giturl | |
| # User inputs | |
| input_prompt = get_input_prompt() | |
| st.subheader("Enter the Notes👇🏻") | |
| source_material = st.text_area("Your source material", height=200) | |
| # Initialize session state for selected_bg if it doesn't exist | |
| if "selected_bg" not in st.session_state: | |
| st.session_state.selected_bg = "Option 1" | |
| bg_options = { | |
| "Option 1": "pptbgs/0.png", | |
| "Option 2": "pptbgs/1.png", | |
| "Option 3": "pptbgs/2.png", | |
| "Option 4": "pptbgs/3.png", | |
| "Option 5": "pptbgs/4.png", | |
| "Option 6": "pptbgs/5.png", | |
| } | |
| ## Display Background Image Options | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| st.image(bg_options["Option 1"], use_column_width=True) | |
| if st.button("Select Option 1", key="option1"): | |
| st.session_state.selected_bg = "Option 1" | |
| with col2: | |
| st.image(bg_options["Option 2"], use_column_width=True) | |
| if st.button("Select Option 2", key="option2"): | |
| st.session_state.selected_bg = "Option 2" | |
| with col3: | |
| st.image(bg_options["Option 3"], use_column_width=True) | |
| if st.button("Select Option 3", key="option3"): | |
| st.session_state.selected_bg = "Option 3" | |
| col4, col5, col6 = st.columns(3) | |
| with col4: | |
| st.image(bg_options["Option 4"], use_column_width=True) | |
| if st.button("Select Option 4", key="option4"): | |
| st.session_state.selected_bg = "Option 4" | |
| with col5: | |
| st.image(bg_options["Option 5"], use_column_width=True) | |
| if st.button("Select Option 5", key="option5"): | |
| st.session_state.selected_bg = "Option 5" | |
| with col6: | |
| st.image(bg_options["Option 6"], use_column_width=True) | |
| if st.button("Select Option 6", key="option6"): | |
| st.session_state.selected_bg = "Option 6" | |
| ## Display Selected Background Image | |
| # st.write(f"Selected Background Image: {st.session_state.selected_bg}") | |
| # Display the selected image | |
| # Display the selected image within an expander | |
| with st.expander( | |
| f"Selected Background Image: {st.session_state.selected_bg}", expanded=False | |
| ): | |
| image_path = bg_options[st.session_state.selected_bg] | |
| st.image(image_path, caption=st.session_state.selected_bg, use_column_width=True) | |
| selected_bg_path = bg_options[st.session_state.selected_bg] | |
| # Adding an image to visualize the concept | |
| if st.button("Generate Video :movie_camera:") and source_material: | |
| st.text("Generating video...") | |
| print(selected_bg_path) | |
| video_path = generate_video(source_material, input_prompt, selected_bg_path) | |
| st.success("Video generated successfully!") | |
| st.markdown(f'Download your video [here]({video_path.replace("%5C", "/")})') | |
| st.balloons() | |
| audio = AudioSegment.from_file("success_sound.wav", format="wav") | |
| play(audio) | |
| st.image("logo.png", use_column_width=True) |