Spaces:
Sleeping
Sleeping
import gradio as gr | |
import json | |
import os | |
from datetime import datetime | |
import tempfile | |
import io | |
from gtts import gTTS | |
import pytesseract | |
from PIL import Image | |
import zipfile | |
# Global state for projects | |
PROJECTS_FILE = "projects.json" | |
current_project_id = None | |
def load_projects(): | |
"""Load projects from JSON file""" | |
if os.path.exists(PROJECTS_FILE): | |
with open(PROJECTS_FILE, 'r') as f: | |
return json.load(f) | |
return { | |
"projects": { | |
"1": { | |
"id": "1", | |
"name": "Sample Script", | |
"content": "Welcome to ScriptVoice! This is your first script. Start editing to create amazing voice content.", | |
"notes": "This is a sample note for your script.", | |
"created_at": datetime.now().isoformat(), | |
"word_count": 0 | |
} | |
}, | |
"settings": { | |
"dyslexic_mode": False, | |
"voice_speed": 1.0, | |
"voice_volume": 1.0 | |
} | |
} | |
def save_projects(data): | |
"""Save projects to JSON file""" | |
with open(PROJECTS_FILE, 'w') as f: | |
json.dump(data, f, indent=2) | |
def get_word_count(text): | |
"""Count words in text""" | |
if not text: | |
return 0 | |
return len(text.split()) | |
def update_word_count(text): | |
"""Update word count display""" | |
count = get_word_count(text) | |
return f"**Word Count:** {count}" | |
def create_new_project(name): | |
"""Create a new project""" | |
if not name.strip(): | |
return "Please enter a project name", None | |
data = load_projects() | |
new_id = str(len(data["projects"]) + 1) | |
data["projects"][new_id] = { | |
"id": new_id, | |
"name": name.strip(), | |
"content": "", | |
"notes": "", | |
"created_at": datetime.now().isoformat(), | |
"word_count": 0 | |
} | |
save_projects(data) | |
# Return updated project choices and select the new project | |
choices = [(proj["name"], proj_id) for proj_id, proj in data["projects"].items()] | |
return f"Project '{name}' created successfully!", gr.update(choices=choices, value=new_id) | |
def load_project(project_id): | |
"""Load a specific project""" | |
if not project_id: | |
return "", "", "**Word Count:** 0" | |
global current_project_id | |
current_project_id = project_id | |
data = load_projects() | |
if project_id in data["projects"]: | |
project = data["projects"][project_id] | |
word_count = get_word_count(project["content"]) | |
return project["content"], project["notes"], f"**Word Count:** {word_count}" | |
return "", "", "**Word Count:** 0" | |
def save_script_content(project_id, content, notes): | |
"""Save script content and notes""" | |
if not project_id: | |
return "No project selected" | |
data = load_projects() | |
if project_id in data["projects"]: | |
data["projects"][project_id]["content"] = content | |
data["projects"][project_id]["notes"] = notes | |
data["projects"][project_id]["word_count"] = get_word_count(content) | |
save_projects(data) | |
return "β Saved successfully" | |
return "β Error saving" | |
def generate_tts(text, speed=1.0): | |
"""Generate TTS audio from text""" | |
if not text.strip(): | |
return None, "Please enter some text to convert to speech" | |
try: | |
# Create a temporary file for the audio | |
tts = gTTS(text=text, lang='en', slow=(speed < 1.0)) | |
# Use a temporary file | |
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') as tmp_file: | |
tts.save(tmp_file.name) | |
return tmp_file.name, "β Audio generated successfully" | |
except Exception as e: | |
return None, f"β Error generating audio: {str(e)}" | |
def extract_text_from_image(image): | |
"""Extract text from uploaded image using OCR""" | |
if image is None: | |
return "", "Please upload an image" | |
try: | |
# Use pytesseract to extract text | |
text = pytesseract.image_to_string(Image.open(image)) | |
if text.strip(): | |
return text.strip(), "β Text extracted successfully" | |
else: | |
return "", "No text found in the image" | |
except Exception as e: | |
return "", f"β Error extracting text: {str(e)}" | |
def enhance_script_placeholder(text, enhancement_type): | |
"""Placeholder for AI script enhancement""" | |
enhancements = { | |
"dramatic": f"[DRAMATIC VERSION]\n{text}\n\n(Note: AI enhancement feature coming soon!)", | |
"romantic": f"[ROMANTIC VERSION]\n{text}\n\n(Note: AI enhancement feature coming soon!)", | |
"professional": f"[PROFESSIONAL VERSION]\n{text}\n\n(Note: AI enhancement feature coming soon!)", | |
"casual": f"[CASUAL VERSION]\n{text}\n\n(Note: AI enhancement feature coming soon!)" | |
} | |
return enhancements.get(enhancement_type, text), "β Enhancement applied (demo mode)" | |
def export_project(project_id, export_type): | |
"""Export project content""" | |
if not project_id: | |
return None, "No project selected" | |
data = load_projects() | |
if project_id not in data["projects"]: | |
return None, "Project not found" | |
project = data["projects"][project_id] | |
if export_type == "text": | |
# Create text file | |
content = f"Project: {project['name']}\n" | |
content += f"Created: {project['created_at']}\n" | |
content += f"Word Count: {project['word_count']}\n\n" | |
content += "SCRIPT:\n" + "="*50 + "\n" | |
content += project['content'] + "\n\n" | |
content += "NOTES:\n" + "="*50 + "\n" | |
content += project['notes'] | |
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt', encoding='utf-8') as tmp_file: | |
tmp_file.write(content) | |
return tmp_file.name, "β Text file exported" | |
elif export_type == "audio": | |
# Generate TTS audio | |
if not project['content'].strip(): | |
return None, "No content to convert to audio" | |
try: | |
tts = gTTS(text=project['content'], lang='en') | |
with tempfile.NamedTemporaryFile(delete=False, suffix='.mp3') as tmp_file: | |
tts.save(tmp_file.name) | |
return tmp_file.name, "β Audio file exported" | |
except Exception as e: | |
return None, f"β Error generating audio: {str(e)}" | |
return None, "Invalid export type" | |
# Initialize the Gradio interface | |
def create_interface(): | |
"""Create the main Gradio interface""" | |
# Load initial projects | |
data = load_projects() | |
project_choices = [(proj["name"], proj_id) for proj_id, proj in data["projects"].items()] | |
with gr.Blocks(title="ScriptVoice - TTS Script Editor", theme=gr.themes.Soft()) as app: | |
# Header | |
gr.HTML(""" | |
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px; margin-bottom: 20px;"> | |
<h1 style="margin: 0; font-size: 2.5em;"> | |
<span style="color: #ff6b6b;">Script</span><span style="color: #ffd93d;">Voice</span> | |
</h1> | |
<p style="margin: 10px 0 0 0; font-size: 1.2em;">AI-Powered TTS Script Editor</p> | |
</div> | |
""") | |
with gr.Row(): | |
# Left Sidebar | |
with gr.Column(scale=1, min_width=300): | |
gr.Markdown("### π Projects") | |
# New Project Section | |
with gr.Group(): | |
new_project_name = gr.Textbox(label="New Project Name", placeholder="Enter project name...") | |
create_btn = gr.Button("β Create Project", variant="primary") | |
create_status = gr.Textbox(label="Status", interactive=False, visible=False) | |
# Project Selection | |
project_dropdown = gr.Dropdown( | |
choices=project_choices, | |
label="Select Project", | |
value=list(data["projects"].keys())[0] if data["projects"] else None | |
) | |
# Notes Section | |
gr.Markdown("### π Notes") | |
notes_textbox = gr.Textbox( | |
label="Project Notes", | |
placeholder="Add your notes here...", | |
lines=5, | |
max_lines=10 | |
) | |
# Settings Section | |
gr.Markdown("### βοΈ Settings") | |
with gr.Group(): | |
dyslexic_mode = gr.Checkbox(label="Dyslexic-friendly font", value=False) | |
voice_speed = gr.Slider(0.5, 2.0, value=1.0, step=0.1, label="Voice Speed") | |
voice_volume = gr.Slider(0.1, 1.0, value=1.0, step=0.1, label="Voice Volume") | |
# Main Editor Panel | |
with gr.Column(scale=2): | |
# Word Count Display | |
word_count_display = gr.Markdown("**Word Count:** 0") | |
# Script Editor | |
script_textbox = gr.Textbox( | |
label="Script Editor", | |
placeholder="Start writing your script here...", | |
lines=15, | |
max_lines=25 | |
) | |
# Control Buttons Row | |
with gr.Row(): | |
save_btn = gr.Button("πΎ Save", variant="secondary") | |
tts_btn = gr.Button("π Play TTS", variant="primary") | |
save_status = gr.Textbox(label="Save Status", interactive=False, visible=False) | |
# TTS Audio Output | |
audio_output = gr.Audio(label="Generated Audio") | |
tts_status = gr.Textbox(label="TTS Status", interactive=False, visible=False) | |
# OCR Section | |
with gr.Group(): | |
gr.Markdown("### π· Extract Text from Image") | |
with gr.Row(): | |
image_input = gr.Image(type="filepath", label="Upload Image") | |
ocr_btn = gr.Button("Extract Text") | |
ocr_status = gr.Textbox(label="OCR Status", interactive=False, visible=False) | |
# AI Enhancement Section | |
with gr.Group(): | |
gr.Markdown("### π€ AI Script Enhancement") | |
with gr.Row(): | |
enhancement_type = gr.Dropdown( | |
choices=["dramatic", "romantic", "professional", "casual"], | |
label="Enhancement Style", | |
value="dramatic" | |
) | |
enhance_btn = gr.Button("β¨ Enhance Script") | |
enhance_status = gr.Textbox(label="Enhancement Status", interactive=False, visible=False) | |
# Export Section | |
with gr.Group(): | |
gr.Markdown("### π€ Export") | |
with gr.Row(): | |
export_type = gr.Dropdown( | |
choices=["text", "audio"], | |
label="Export Type", | |
value="text" | |
) | |
export_btn = gr.Button("π₯ Export") | |
export_file = gr.File(label="Download") | |
export_status = gr.Textbox(label="Export Status", interactive=False, visible=False) | |
# Event Handlers | |
# Create new project | |
create_btn.click( | |
fn=create_new_project, | |
inputs=[new_project_name], | |
outputs=[create_status, project_dropdown] | |
).then( | |
lambda: ("", gr.update(visible=True)), | |
outputs=[new_project_name, create_status] | |
) | |
# Load project when selected | |
project_dropdown.change( | |
fn=load_project, | |
inputs=[project_dropdown], | |
outputs=[script_textbox, notes_textbox, word_count_display] | |
) | |
# Update word count as user types | |
script_textbox.change( | |
fn=update_word_count, | |
inputs=[script_textbox], | |
outputs=[word_count_display] | |
) | |
# Save script content | |
save_btn.click( | |
fn=save_script_content, | |
inputs=[project_dropdown, script_textbox, notes_textbox], | |
outputs=[save_status] | |
).then( | |
lambda: gr.update(visible=True), | |
outputs=[save_status] | |
) | |
# Generate TTS | |
tts_btn.click( | |
fn=generate_tts, | |
inputs=[script_textbox, voice_speed], | |
outputs=[audio_output, tts_status] | |
).then( | |
lambda: gr.update(visible=True), | |
outputs=[tts_status] | |
) | |
# OCR text extraction | |
ocr_btn.click( | |
fn=extract_text_from_image, | |
inputs=[image_input], | |
outputs=[script_textbox, ocr_status] | |
).then( | |
lambda: gr.update(visible=True), | |
outputs=[ocr_status] | |
) | |
# AI Enhancement | |
enhance_btn.click( | |
fn=enhance_script_placeholder, | |
inputs=[script_textbox, enhancement_type], | |
outputs=[script_textbox, enhance_status] | |
).then( | |
lambda: gr.update(visible=True), | |
outputs=[enhance_status] | |
) | |
# Export functionality | |
export_btn.click( | |
fn=export_project, | |
inputs=[project_dropdown, export_type], | |
outputs=[export_file, export_status] | |
).then( | |
lambda: gr.update(visible=True), | |
outputs=[export_status] | |
) | |
return app | |
if __name__ == "__main__": | |
# Create and launch the app | |
app = create_interface() | |
app.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=True, | |
show_error=True | |
) | |