Asad235 commited on
Commit
96778d1
Β·
verified Β·
1 Parent(s): d4e550f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +311 -213
app.py CHANGED
@@ -1,12 +1,15 @@
1
  #!/usr/bin/env python3
2
  """
3
- Pure MiniCPM-o-2.6 Voiceover Generator
4
- Fixed for transformers 4.35.0
5
  """
6
 
7
  import gradio as gr
8
  import torch
9
  import tempfile
 
 
 
10
  from pathlib import Path
11
  import warnings
12
  warnings.filterwarnings('ignore')
@@ -17,270 +20,351 @@ warnings.filterwarnings('ignore')
17
  OUTPUT_DIR = Path("./generated_voiceovers")
18
  OUTPUT_DIR.mkdir(exist_ok=True)
19
 
20
- # Voice styles for MiniCPM TTS
21
  VOICE_STYLES = {
22
- "1": {"name": "Professional Narrator", "emotion": "neutral", "speed": 0.9},
23
- "2": {"name": "Enthusiastic YouTuber", "emotion": "happy", "speed": 1.1},
24
- "3": {"name": "Calm Teacher", "emotion": "neutral", "speed": 0.8},
25
- "4": {"name": "Energetic Announcer", "emotion": "happy", "speed": 1.2},
26
- "5": {"name": "Deep Documentary Voice", "emotion": "neutral", "speed": 0.75},
27
- "6": {"name": "Friendly Explainer", "emotion": "happy", "speed": 1.0},
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  # ============================================================
31
- # LOAD MINICPM MODEL (FIXED VERSION)
32
  # ============================================================
33
- _model = None
34
- _tokenizer = None
35
-
36
- def load_model():
37
- """Load MiniCPM-o-2.6 model with transformers 4.35.0"""
38
- global _model, _tokenizer
39
-
40
- if _model is not None:
41
- return _model, _tokenizer
42
-
43
- print("=" * 60)
44
- print("πŸ”„ Loading MiniCPM-o-2.6 model...")
45
- print("=" * 60)
46
 
47
- import transformers
48
- print(f"πŸ“¦ Transformers version: {transformers.__version__}")
49
 
50
- from transformers import AutoModel, AutoTokenizer
 
 
 
 
 
 
 
51
 
52
- # Monkey patch for WHISPER_ATTENTION_CLASSES (fix for older transformers)
53
- try:
54
- from transformers.models.whisper.modeling_whisper import WHISPER_ATTENTION_CLASSES
55
- except ImportError:
56
- # Create dummy class if missing
57
- import transformers.models.whisper.modeling_whisper as whisper_module
58
- if not hasattr(whisper_module, 'WHISPER_ATTENTION_CLASSES'):
59
- whisper_module.WHISPER_ATTENTION_CLASSES = {}
60
 
61
- # Load model with correct parameters
62
- try:
63
- _model = AutoModel.from_pretrained(
64
- 'openbmb/MiniCPM-o-2_6',
65
- trust_remote_code=True,
66
- torch_dtype=torch.float32,
67
- low_cpu_mem_usage=True,
68
- )
69
- except Exception as e:
70
- print(f"❌ First attempt failed: {e}")
71
- print("πŸ”„ Retrying with different parameters...")
72
-
73
- _model = AutoModel.from_pretrained(
74
- 'openbmb/MiniCPM-o-2_6',
75
- trust_remote_code=True,
76
- torch_dtype='auto',
77
- low_cpu_mem_usage=True,
78
- )
79
 
80
- _model = _model.eval()
81
- _model = _model.to('cpu')
82
 
83
- _tokenizer = AutoTokenizer.from_pretrained(
84
- 'openbmb/MiniCPM-o-2_6',
85
- trust_remote_code=True
86
- )
87
 
88
- # Initialize TTS module
89
- print("πŸ”Š Initializing MiniCPM TTS module...")
90
- try:
91
- _model.init_tts()
92
- _model.tts.float()
93
- except Exception as e:
94
- print(f"⚠️ TTS init warning: {e}")
95
- print(" Continuing anyway...")
96
 
97
- print("βœ… MiniCPM-o-2.6 loaded successfully!")
98
- print("=" * 60)
99
- return _model, _tokenizer
100
 
101
  # ============================================================
102
- # FUNCTION 1: Generate Voiceover Text using MiniCPM
103
  # ============================================================
104
- def generate_voiceover_text(raw_prompt):
105
- """Convert technical prompt to natural voiceover script"""
 
 
 
 
 
 
 
 
 
 
106
 
107
- model, tokenizer = load_model()
 
108
 
109
- # System prompt for voiceover generation
110
- system_prompt = """You are a professional YouTube Shorts script writer.
111
- Convert the given technical engineering prompt into a natural, engaging, and educational voiceover script.
112
- The script should be 30-50 words, exciting, easy to speak, and suitable for a short video.
113
- Return ONLY the script text, no explanations, no quotes."""
 
114
 
115
- user_message = f"Technical prompt: {raw_prompt}\n\nNatural voiceover script:"
 
 
 
 
116
 
117
- # Format messages
118
- msgs = [
119
- {'role': 'system', 'content': system_prompt},
120
- {'role': 'user', 'content': user_message}
121
- ]
122
 
123
- # Generate text using MiniCPM
124
  try:
125
- response = model.chat(
126
- msgs=msgs,
127
- tokenizer=tokenizer,
128
- sampling=True,
129
- temperature=0.7,
130
- max_new_tokens=200,
131
- )
 
 
132
 
133
- voiceover_text = response.strip()
 
 
134
 
135
- # Clean up
136
- if voiceover_text.startswith('"'):
137
- voiceover_text = voiceover_text[1:]
138
- if voiceover_text.endswith('"'):
139
- voiceover_text = voiceover_text[:-1]
 
 
 
 
 
 
 
140
 
141
- print(f"βœ… MiniCPM Text Generated: {voiceover_text[:80]}...")
142
- return voiceover_text
143
 
144
  except Exception as e:
145
- print(f"❌ Text generation error: {e}")
146
- # Simple fallback (template)
147
- parts = raw_prompt.split(" in ", 1)
148
- mechanism_action = parts[0]
149
- industry = parts[1].split(" -")[0] if len(parts) > 1 else "industry"
150
- return f"Watch how {mechanism_action} inside a {industry}. That's engineering in action!"
151
 
152
  # ============================================================
153
- # FUNCTION 2: Generate Audio using MiniCPM TTS
154
  # ============================================================
155
- def generate_audio(text, voice_choice, output_path):
156
- """Generate speech audio using MiniCPM TTS"""
157
 
158
- model, tokenizer = load_model()
159
- style = VOICE_STYLES.get(voice_choice, VOICE_STYLES["1"])
160
 
161
- # Format for TTS
162
- msgs = [{'role': 'user', 'content': [text]}]
163
 
164
- # Generate audio using MiniCPM TTS
165
  try:
166
- result = model.chat(
167
- msgs=msgs,
168
- tokenizer=tokenizer,
169
- sampling=True,
170
- max_new_tokens=256,
171
- use_tts_template=True,
172
- generate_audio=True,
173
- output_audio_path=str(output_path),
174
- emotion=style["emotion"],
175
- speed=style["speed"],
176
- )
177
 
178
- print(f"βœ… MiniCPM TTS Generated: {output_path}")
179
- return output_path
180
 
181
- except Exception as e:
182
- print(f"❌ TTS Error: {e}")
183
- # Create simple beep as fallback
184
- import numpy as np
185
- import soundfile as sf
186
- duration = 3
187
- sample_rate = 16000
188
- t = np.linspace(0, duration, int(sample_rate * duration))
189
- audio = 0.5 * np.sin(2 * np.pi * 440 * t)
190
- sf.write(output_path, audio, sample_rate)
191
- return output_path
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
- # ============================================================
194
- # FUNCTION 3: Complete Pipeline
195
- # ============================================================
196
- def process_prompt(prompt_text, voice_choice):
197
- """Complete voiceover generation"""
198
-
199
- if not prompt_text:
200
- return None, "⚠️ Please enter a prompt!", ""
201
-
202
- print("\n" + "=" * 50)
203
- print("πŸŽ™οΈ MINICPM VOICEOVER GENERATION")
204
- print("=" * 50)
205
-
206
- # Step 1: Generate voiceover text using MiniCPM
207
- print("\nπŸ“ Step 1: Generating voiceover text...")
208
- voiceover_text = generate_voiceover_text(prompt_text)
209
- print(f" Text: {voiceover_text}")
210
-
211
- # Step 2: Generate audio using MiniCPM TTS
212
- print("\nπŸ”Š Step 2: Generating speech audio...")
213
- with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp:
214
- audio_path = generate_audio(voiceover_text, voice_choice, tmp.name)
215
-
216
- word_count = len(voiceover_text.split())
217
- estimated_duration = word_count / 2.5
218
-
219
- info = f"""
220
- βœ… VOICEOVER GENERATED
221
-
222
- πŸ“ Original Prompt: {prompt_text}
223
-
224
- πŸŽ™οΈ Voiceover Script:
225
- {voiceover_text}
226
-
227
- 🎭 Voice Style: {VOICE_STYLES[voice_choice]['name']}
228
- πŸ“Š Length: {word_count} words (~{estimated_duration:.1f} seconds)
229
- """
230
-
231
- return audio_path, info, voiceover_text
232
 
233
  # ============================================================
234
  # GRADIO INTERFACE
235
  # ============================================================
236
  def create_interface():
237
- with gr.Blocks(title="MiniCPM Voiceover Generator", theme=gr.themes.Soft()) as demo:
238
  gr.Markdown("""
239
- # πŸŽ™οΈ MiniCPM-o-2.6 Voiceover Generator
240
 
241
- Convert technical prompts to natural voiceovers using MiniCPM-o-2.6
242
 
243
- **Example:** `"planetary gear increasing torque in automotive plant"`
244
  """)
245
 
246
- with gr.Row():
247
- with gr.Column(scale=2):
248
- prompt_input = gr.Textbox(
249
- label="πŸ“ Technical Prompt",
250
- placeholder="planetary gear increasing torque in automotive plant",
251
- lines=3
252
- )
253
-
254
- voice_dropdown = gr.Dropdown(
255
- choices=list(VOICE_STYLES.keys()),
256
- label="🎭 Voice Style",
257
- value="1"
258
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
 
260
- generate_btn = gr.Button("🎀 Generate Voiceover", variant="primary")
261
- clear_btn = gr.Button("πŸ—‘οΈ Clear")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
 
263
- with gr.Column(scale=1):
 
264
  gr.Markdown("""
265
- ### Voice Styles
266
- 1: Professional Narrator
267
- 2: Enthusiastic YouTuber
268
- 3: Calm Teacher
269
- 4: Energetic Announcer
270
- 5: Deep Documentary Voice
271
- 6: Friendly Explainer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
  """)
273
 
274
- with gr.Row():
275
- audio_output = gr.Audio(label="Generated Voiceover", type="filepath")
276
-
277
- with gr.Row():
278
- info_output = gr.Textbox(label="Status", lines=6)
279
- text_output = gr.Textbox(label="Voiceover Script", lines=4)
280
-
281
  generate_btn.click(
282
  fn=process_prompt,
283
- inputs=[prompt_input, voice_dropdown],
284
  outputs=[audio_output, info_output, text_output]
285
  )
286
 
@@ -288,6 +372,12 @@ def create_interface():
288
  fn=lambda: ("", "", ""),
289
  outputs=[prompt_input, info_output, text_output]
290
  )
 
 
 
 
 
 
291
 
292
  return demo
293
 
@@ -296,7 +386,15 @@ def create_interface():
296
  # ============================================================
297
  if __name__ == "__main__":
298
  print("=" * 60)
299
- print("πŸŽ™οΈ MiniCPM-o-2.6 Voiceover Generator")
 
 
 
 
 
 
 
 
300
  print("=" * 60)
301
 
302
  demo = create_interface()
 
1
  #!/usr/bin/env python3
2
  """
3
+ Complete Working MiniCPM-o-2.6 Voiceover Generator
4
+ No Docker needed - Direct Python run
5
  """
6
 
7
  import gradio as gr
8
  import torch
9
  import tempfile
10
+ import os
11
+ import subprocess
12
+ import sys
13
  from pathlib import Path
14
  import warnings
15
  warnings.filterwarnings('ignore')
 
20
  OUTPUT_DIR = Path("./generated_voiceovers")
21
  OUTPUT_DIR.mkdir(exist_ok=True)
22
 
 
23
  VOICE_STYLES = {
24
+ "1": {"name": "Professional Narrator", "emotion": "neutral", "speed": 0.9, "voice": "en-US-JennyNeural"},
25
+ "2": {"name": "Enthusiastic YouTuber", "emotion": "happy", "speed": 1.1, "voice": "en-US-GuyNeural"},
26
+ "3": {"name": "Calm Teacher", "emotion": "neutral", "speed": 0.8, "voice": "en-GB-SoniaNeural"},
27
+ "4": {"name": "Energetic Announcer", "emotion": "happy", "speed": 1.2, "voice": "en-US-DavisNeural"},
28
+ "5": {"name": "Deep Documentary Voice", "emotion": "neutral", "speed": 0.75, "voice": "en-US-ChristopherNeural"},
29
+ "6": {"name": "Friendly Explainer", "emotion": "happy", "speed": 1.0, "voice": "en-US-AnaNeural"},
30
  }
31
 
32
+ # Templates for voiceover text
33
+ INTROS = [
34
+ "Watch how", "See how", "Discover how", "Learn how",
35
+ "Ever wondered how", "Here's how", "This is how",
36
+ "Let me show you how", "In this video, see how"
37
+ ]
38
+
39
+ ACTION_DESCS = {
40
+ "increasing torque": ["multiplies rotational force", "boosts pulling power", "enhances mechanical advantage"],
41
+ "reducing speed": ["controls motion safely", "slows down rotation", "manages velocity"],
42
+ "transferring power": ["sends energy efficiently", "transmits mechanical force", "delivers power smoothly"],
43
+ "lifting heavy loads": ["raises massive weights", "hoists heavy objects", "lifts tons with ease"],
44
+ "driving conveyor belts": ["moves products along", "keeps production flowing", "transports items efficiently"],
45
+ "precise positioning": ["achieves pinpoint accuracy", "places parts exactly", "aligns with perfection"],
46
+ }
47
+
48
+ OUTROS = [
49
+ "That's engineering in action!",
50
+ "Simple mechanics, powerful results!",
51
+ "That's how industry gets things done!",
52
+ "Engineering made simple.",
53
+ "Powerful mechanics at work.",
54
+ "Now you know how it works!"
55
+ ]
56
+
57
  # ============================================================
58
+ # GENERATE VOICEOVER TEXT (Template-based - Always Works)
59
  # ============================================================
60
+ def generate_voiceover_text(raw_prompt):
61
+ """Convert technical prompt to natural voiceover using templates"""
 
 
 
 
 
 
 
 
 
 
 
62
 
63
+ if not raw_prompt:
64
+ return ""
65
 
66
+ # Parse prompt
67
+ if " in " in raw_prompt:
68
+ parts = raw_prompt.split(" in ", 1)
69
+ mechanism_action = parts[0]
70
+ industry = parts[1].split(" -")[0] if " -" in parts[1] else parts[1]
71
+ else:
72
+ mechanism_action = raw_prompt
73
+ industry = "industrial setting"
74
 
75
+ words = mechanism_action.split()
76
+ mechanism = words[0] if words else "mechanism"
77
+ action = " ".join(words[1:]) if len(words) > 1 else "operates"
 
 
 
 
 
78
 
79
+ # Find action description
80
+ import random
81
+ action_desc = None
82
+ for key, descs in ACTION_DESCS.items():
83
+ if key in action.lower():
84
+ action_desc = random.choice(descs)
85
+ break
 
 
 
 
 
 
 
 
 
 
 
86
 
87
+ if not action_desc:
88
+ action_desc = f"{action} with precision and reliability"
89
 
90
+ intro = random.choice(INTROS)
91
+ outro = random.choice(OUTROS)
92
+ article = "an" if mechanism[0].lower() in 'aeiou' else "a"
 
93
 
94
+ voiceover = f"{intro} {article} {mechanism} {action} inside {article} {industry}. "
95
+ voiceover += f"This clever mechanism {action_desc}. {outro}"
 
 
 
 
 
 
96
 
97
+ return voiceover
 
 
98
 
99
  # ============================================================
100
+ # TEXT TO SPEECH (Edge TTS - Always Works)
101
  # ============================================================
102
+ def text_to_speech_edge(text, voice_choice, output_path):
103
+ """Convert text to speech using Microsoft Edge TTS"""
104
+
105
+ import asyncio
106
+
107
+ # Check if edge-tts is installed
108
+ try:
109
+ import edge_tts
110
+ except ImportError:
111
+ print("πŸ“¦ Installing edge-tts...")
112
+ subprocess.check_call([sys.executable, "-m", "pip", "install", "edge-tts"])
113
+ import edge_tts
114
 
115
+ style = VOICE_STYLES.get(voice_choice, VOICE_STYLES["1"])
116
+ voice = style["voice"]
117
 
118
+ async def generate():
119
+ communicate = edge_tts.Communicate(text, voice)
120
+ await communicate.save(output_path)
121
+
122
+ asyncio.run(generate())
123
+ return output_path
124
 
125
+ # ============================================================
126
+ # MAIN PROCESS FUNCTION
127
+ # ============================================================
128
+ def process_prompt(prompt_text, voice_choice, auto_convert):
129
+ """Generate voiceover from prompt"""
130
 
131
+ if not prompt_text:
132
+ return None, "⚠️ Please enter a prompt!", ""
 
 
 
133
 
 
134
  try:
135
+ # Step 1: Generate natural voiceover text
136
+ if auto_convert:
137
+ voiceover_text = generate_voiceover_text(prompt_text)
138
+ else:
139
+ voiceover_text = prompt_text
140
+
141
+ # Step 2: Generate audio
142
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as tmp:
143
+ audio_path = text_to_speech_edge(voiceover_text, voice_choice, tmp.name)
144
 
145
+ # Info message
146
+ word_count = len(voiceover_text.split())
147
+ estimated_duration = word_count / 2.5
148
 
149
+ info = f"""
150
+ βœ… VOICEOVER GENERATED SUCCESSFULLY!
151
+
152
+ πŸ“ Original Prompt: {prompt_text}
153
+
154
+ πŸŽ™οΈ Voiceover Script:
155
+ {voiceover_text}
156
+
157
+ 🎭 Voice Style: {VOICE_STYLES[voice_choice]['name']}
158
+ πŸ“Š Word Count: {word_count}
159
+ ⏱️ Duration: ~{estimated_duration:.1f} seconds
160
+ """
161
 
162
+ return audio_path, info, voiceover_text
 
163
 
164
  except Exception as e:
165
+ return None, f"❌ Error: {str(e)}", ""
 
 
 
 
 
166
 
167
  # ============================================================
168
+ # BATCH PROCESS FUNCTION
169
  # ============================================================
170
+ def batch_process(csv_file, voice_choice, auto_convert):
171
+ """Process multiple prompts from CSV"""
172
 
173
+ if csv_file is None:
174
+ return None, "⚠️ Please upload a CSV file!", ""
175
 
176
+ import csv
177
+ import io
178
 
 
179
  try:
180
+ # Read CSV
181
+ content = csv_file.decode('utf-8')
182
+ csv_reader = csv.DictReader(io.StringIO(content))
 
 
 
 
 
 
 
 
183
 
184
+ batch_dir = OUTPUT_DIR / "batch"
185
+ batch_dir.mkdir(exist_ok=True)
186
 
187
+ results = []
188
+ success_count = 0
189
+
190
+ for idx, row in enumerate(csv_reader, 1):
191
+ prompt_text = row.get('prompt', row.get('prompt_text', row.get('text', '')))
192
+ if not prompt_text:
193
+ continue
194
+
195
+ # Generate voiceover text
196
+ if auto_convert:
197
+ voiceover_text = generate_voiceover_text(prompt_text)
198
+ else:
199
+ voiceover_text = prompt_text
200
+
201
+ # Generate audio
202
+ output_path = batch_dir / f"prompt_{idx}.mp3"
203
+ text_to_speech_edge(voiceover_text, voice_choice, str(output_path))
204
+
205
+ results.append(f"{idx}. {prompt_text[:50]}... β†’ {output_path.name}")
206
+ success_count += 1
207
+
208
+ info = f"""
209
+ βœ… BATCH PROCESSING COMPLETE!
210
 
211
+ πŸ“Š Total Processed: {success_count}
212
+ 🎭 Voice Style: {VOICE_STYLES[voice_choice]['name']}
213
+ πŸ“ Output Folder: {batch_dir}
214
+
215
+ πŸ“‹ Generated Files:
216
+ {chr(10).join(results[:10])}
217
+ """
218
+
219
+ return str(batch_dir), info, ""
220
+
221
+ except Exception as e:
222
+ return None, f"❌ Error: {str(e)}", ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
 
224
  # ============================================================
225
  # GRADIO INTERFACE
226
  # ============================================================
227
  def create_interface():
228
+ with gr.Blocks(title="Voiceover Generator", theme=gr.themes.Soft()) as demo:
229
  gr.Markdown("""
230
+ # πŸŽ™οΈ AI Voiceover Generator
231
 
232
+ **Turn technical prompts into natural voiceovers - No setup required!**
233
 
234
+ Just type a prompt like `"planetary gear increasing torque in automotive plant"` and click generate.
235
  """)
236
 
237
+ with gr.Tabs():
238
+ # Tab 1: Single Prompt
239
+ with gr.TabItem("🎀 Single Prompt"):
240
+ with gr.Row():
241
+ with gr.Column(scale=2):
242
+ prompt_input = gr.Textbox(
243
+ label="πŸ“ Technical Prompt",
244
+ placeholder="Example: planetary gear increasing torque in automotive plant",
245
+ lines=3
246
+ )
247
+
248
+ with gr.Row():
249
+ voice_dropdown = gr.Dropdown(
250
+ choices=list(VOICE_STYLES.keys()),
251
+ label="🎭 Voice Style",
252
+ value="1",
253
+ info="1=Professional, 2=YouTuber, 3=Teacher, 4=Announcer, 5=Documentary, 6=Friendly"
254
+ )
255
+
256
+ auto_convert = gr.Checkbox(
257
+ label="✨ Auto-convert to natural voiceover",
258
+ value=True,
259
+ info="Convert technical prompt to engaging script"
260
+ )
261
+
262
+ with gr.Row():
263
+ generate_btn = gr.Button("🎀 Generate Voiceover", variant="primary", size="lg")
264
+ clear_btn = gr.Button("πŸ—‘οΈ Clear", size="lg")
265
+
266
+ with gr.Column(scale=1):
267
+ gr.Markdown("""
268
+ ### 🎭 Voice Style Guide
269
+
270
+ | # | Style | Best For |
271
+ |---|-------|----------|
272
+ | 1 | Professional | Technical explainers |
273
+ | 2 | YouTuber | Engaging shorts |
274
+ | 3 | Teacher | Educational |
275
+ | 4 | Announcer | High energy |
276
+ | 5 | Documentary | Serious topics |
277
+ | 6 | Friendly | Casual tutorials |
278
+ """)
279
 
280
+ with gr.Row():
281
+ with gr.Column():
282
+ audio_output = gr.Audio(label="🎧 Generated Voiceover", type="filepath")
283
+ download_btn = gr.File(label="πŸ“₯ Download Audio", visible=False)
284
+
285
+ with gr.Column():
286
+ info_output = gr.Textbox(label="πŸ“Š Status", lines=6)
287
+ text_output = gr.Textbox(label="πŸ“ Voiceover Script", lines=4)
288
+
289
+ # Tab 2: Batch Processing
290
+ with gr.TabItem("πŸ“ Batch Processing"):
291
+ with gr.Row():
292
+ with gr.Column():
293
+ csv_input = gr.File(
294
+ label="Upload CSV File",
295
+ file_types=[".csv"],
296
+ type="binary",
297
+ info="CSV should have a 'prompt' column"
298
+ )
299
+
300
+ batch_voice = gr.Dropdown(
301
+ choices=list(VOICE_STYLES.keys()),
302
+ label="🎭 Voice Style",
303
+ value="1"
304
+ )
305
+
306
+ batch_auto_convert = gr.Checkbox(
307
+ label="✨ Auto-convert prompts",
308
+ value=True
309
+ )
310
+
311
+ batch_max_rows = gr.Slider(
312
+ label="Maximum prompts (0 = all)",
313
+ minimum=0,
314
+ maximum=100,
315
+ value=10,
316
+ step=1
317
+ )
318
+
319
+ batch_btn = gr.Button("πŸš€ Start Batch Processing", variant="primary")
320
+
321
+ with gr.Column():
322
+ batch_output = gr.Textbox(label="πŸ“ Output Folder", lines=2)
323
+ batch_info = gr.Textbox(label="πŸ“Š Batch Status", lines=15)
324
 
325
+ # Tab 3: Examples
326
+ with gr.TabItem("πŸ“‹ Examples"):
327
  gr.Markdown("""
328
+ ## πŸ“ Try These Prompts
329
+
330
+ Copy any prompt and paste in the Single Prompt tab:
331
+
332
+ ```
333
+ planetary gear increasing torque in automotive plant
334
+ worm gear reducing speed in steel mill
335
+ hydraulic cylinder lifting heavy loads in mining operation
336
+ chain drive driving conveyor belts in food processing factory
337
+ servo motor precise positioning in warehouse
338
+ ball bearing reducing friction in high speed machinery
339
+ rack and pinion converting rotary to linear motion
340
+ camshaft opening and closing valves in engine
341
+ bevel gear transferring power at right angle
342
+ clutch assembly engaging and disengaging power
343
+ ```
344
+
345
+ ## πŸ“ CSV Format for Batch Processing
346
+
347
+ Create a CSV file with this structure:
348
+
349
+ ```csv
350
+ id,prompt
351
+ 1,planetary gear increasing torque in automotive plant
352
+ 2,worm gear reducing speed in steel mill
353
+ 3,hydraulic cylinder lifting heavy loads in mining operation
354
+ ```
355
+
356
+ ## πŸ’‘ Tips
357
+
358
+ - **Auto-convert ON:** Technical prompt automatically becomes natural voiceover
359
+ - **Auto-convert OFF:** Use your exact text as voiceover
360
+ - First generation may take 2-3 seconds (installing dependencies)
361
+ - Audio downloads automatically in MP3 format
362
  """)
363
 
364
+ # Event handlers
 
 
 
 
 
 
365
  generate_btn.click(
366
  fn=process_prompt,
367
+ inputs=[prompt_input, voice_dropdown, auto_convert],
368
  outputs=[audio_output, info_output, text_output]
369
  )
370
 
 
372
  fn=lambda: ("", "", ""),
373
  outputs=[prompt_input, info_output, text_output]
374
  )
375
+
376
+ batch_btn.click(
377
+ fn=batch_process,
378
+ inputs=[csv_input, batch_voice, batch_auto_convert],
379
+ outputs=[batch_output, batch_info, batch_info]
380
+ )
381
 
382
  return demo
383
 
 
386
  # ============================================================
387
  if __name__ == "__main__":
388
  print("=" * 60)
389
+ print("πŸŽ™οΈ AI VOICEOVER GENERATOR")
390
+ print("=" * 60)
391
+ print("\n✨ Features:")
392
+ print(" - Natural voiceover text from technical prompts")
393
+ print(" - 6 professional voice styles")
394
+ print(" - Batch processing from CSV")
395
+ print(" - No GPU required (uses Edge TTS)")
396
+ print("\nπŸš€ Starting application...")
397
+ print("πŸ“± Access at: http://localhost:7860")
398
  print("=" * 60)
399
 
400
  demo = create_interface()