faelfernandes commited on
Commit
f47aedc
·
verified ·
1 Parent(s): 18f3589

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +827 -19
index.html CHANGED
@@ -1,19 +1,827 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="pt-BR">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>CriaVideo AI - Construtor de Anúncios Inteligente</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
10
+
11
+ <style>
12
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=Space+Grotesk:wght@500;700&display=swap');
13
+
14
+ :root {
15
+ --neon-blue: #00f3ff;
16
+ --neon-purple: #bc13fe;
17
+ --dark-bg: #0a0a0f;
18
+ --panel-bg: #13131f;
19
+ }
20
+
21
+ body {
22
+ font-family: 'Inter', sans-serif;
23
+ background-color: var(--dark-bg);
24
+ color: #e2e8f0;
25
+ overflow-x: hidden;
26
+ }
27
+
28
+ h1, h2, h3, .brand-font {
29
+ font-family: 'Space Grotesk', sans-serif;
30
+ }
31
+
32
+ /* Custom Scrollbar */
33
+ ::-webkit-scrollbar {
34
+ width: 8px;
35
+ }
36
+ ::-webkit-scrollbar-track {
37
+ background: var(--dark-bg);
38
+ }
39
+ ::-webkit-scrollbar-thumb {
40
+ background: #334155;
41
+ border-radius: 4px;
42
+ }
43
+ ::-webkit-scrollbar-thumb:hover {
44
+ background: #475569;
45
+ }
46
+
47
+ /* Animations */
48
+ @keyframes gradient-xy {
49
+ 0%, 100% { background-position: 0% 50%; }
50
+ 50% { background-position: 100% 50%; }
51
+ }
52
+
53
+ .animate-gradient {
54
+ background-size: 200% 200%;
55
+ animation: gradient-xy 6s ease infinite;
56
+ }
57
+
58
+ .glass-panel {
59
+ background: rgba(19, 19, 31, 0.7);
60
+ backdrop-filter: blur(12px);
61
+ border: 1px solid rgba(255, 255, 255, 0.08);
62
+ box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3);
63
+ }
64
+
65
+ .step-active {
66
+ border-color: var(--neon-blue);
67
+ box-shadow: 0 0 15px rgba(0, 243, 255, 0.2);
68
+ }
69
+
70
+ .step-completed {
71
+ border-color: #10b981;
72
+ }
73
+
74
+ /* Loading Spinner */
75
+ .loader {
76
+ width: 48px;
77
+ height: 48px;
78
+ border: 5px solid #FFF;
79
+ border-bottom-color: var(--neon-purple);
80
+ border-radius: 50%;
81
+ display: inline-block;
82
+ box-sizing: border-box;
83
+ animation: rotation 1s linear infinite;
84
+ }
85
+
86
+ @keyframes rotation {
87
+ 0% { transform: rotate(0deg); }
88
+ 100% { transform: rotate(360deg); }
89
+ }
90
+
91
+ .typing-cursor::after {
92
+ content: '|';
93
+ animation: blink 1s step-start infinite;
94
+ }
95
+
96
+ @keyframes blink {
97
+ 50% { opacity: 0; }
98
+ }
99
+
100
+ /* Range Slider Styling */
101
+ input[type=range] {
102
+ -webkit-appearance: none;
103
+ background: transparent;
104
+ }
105
+ input[type=range]::-webkit-slider-thumb {
106
+ -webkit-appearance: none;
107
+ height: 16px;
108
+ width: 16px;
109
+ border-radius: 50%;
110
+ background: var(--neon-blue);
111
+ cursor: pointer;
112
+ margin-top: -6px;
113
+ }
114
+ input[type=range]::-webkit-slider-runnable-track {
115
+ width: 100%;
116
+ height: 4px;
117
+ cursor: pointer;
118
+ background: #334155;
119
+ border-radius: 2px;
120
+ }
121
+ </style>
122
+ </head>
123
+ <body class="min-h-screen flex flex-col">
124
+
125
+ <!-- Navbar -->
126
+ <nav class="border-b border-gray-800 bg-black/50 backdrop-blur-md sticky top-0 z-50">
127
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
128
+ <div class="flex items-center justify-between h-16">
129
+ <div class="flex items-center gap-3">
130
+ <div class="w-8 h-8 rounded bg-gradient-to-tr from-cyan-500 to-purple-600 flex items-center justify-center">
131
+ <i class="fa-solid fa-bolt text-white text-sm"></i>
132
+ </div>
133
+ <span class="text-xl font-bold tracking-tight text-white">Cria<span class="text-cyan-400">Video</span> AI</span>
134
+ </div>
135
+ <div class="flex items-center gap-4">
136
+ <button onclick="openSettings()" class="text-gray-400 hover:text-white transition-colors">
137
+ <i class="fa-solid fa-gear"></i> Configurações
138
+ </button>
139
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-xs bg-gray-800 hover:bg-gray-700 px-3 py-1.5 rounded-full text-gray-300 transition-colors border border-gray-700">
140
+ Built with <span class="text-cyan-400 font-semibold">anycoder</span>
141
+ </a>
142
+ </div>
143
+ </div>
144
+ </div>
145
+ </nav>
146
+
147
+ <!-- Main Content -->
148
+ <main class="flex-grow p-6 max-w-7xl mx-auto w-full grid grid-cols-1 lg:grid-cols-12 gap-8">
149
+
150
+ <!-- Left Sidebar: Steps -->
151
+ <div class="lg:col-span-3 space-y-4">
152
+ <div class="glass-panel rounded-xl p-6 sticky top-24">
153
+ <h3 class="text-sm font-semibold text-gray-400 uppercase tracking-wider mb-4">Progresso</h3>
154
+ <div class="space-y-4 relative">
155
+ <!-- Connecting Line -->
156
+ <div class="absolute left-3.5 top-2 bottom-2 w-0.5 bg-gray-800 -z-10"></div>
157
+
158
+ <!-- Step 1 -->
159
+ <div id="step-indicator-1" class="flex items-center gap-3 p-3 rounded-lg border border-transparent bg-gray-800/50 step-active transition-all">
160
+ <div class="w-7 h-7 rounded-full bg-cyan-500/20 text-cyan-400 flex items-center justify-center text-xs font-bold border border-cyan-500/50">1</div>
161
+ <div>
162
+ <p class="font-medium text-sm">Roteiro</p>
163
+ <p class="text-xs text-gray-500">Copy & Story</p>
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Step 2 -->
168
+ <div id="step-indicator-2" class="flex items-center gap-3 p-3 rounded-lg border border-transparent opacity-50 transition-all">
169
+ <div class="w-7 h-7 rounded-full bg-gray-700 text-gray-400 flex items-center justify-center text-xs font-bold border border-gray-600">2</div>
170
+ <div>
171
+ <p class="font-medium text-sm">Narração</p>
172
+ <p class="text-xs text-gray-500">Voz & Áudio</p>
173
+ </div>
174
+ </div>
175
+
176
+ <!-- Step 3 -->
177
+ <div id="step-indicator-3" class="flex items-center gap-3 p-3 rounded-lg border border-transparent opacity-50 transition-all">
178
+ <div class="w-7 h-7 rounded-full bg-gray-700 text-gray-400 flex items-center justify-center text-xs font-bold border border-gray-600">3</div>
179
+ <div>
180
+ <p class="font-medium text-sm">Legendas</p>
181
+ <p class="text-xs text-gray-500">Transcrição</p>
182
+ </div>
183
+ </div>
184
+
185
+ <!-- Step 4 -->
186
+ <div id="step-indicator-4" class="flex items-center gap-3 p-3 rounded-lg border border-transparent opacity-50 transition-all">
187
+ <div class="w-7 h-7 rounded-full bg-gray-700 text-gray-400 flex items-center justify-center text-xs font-bold border border-gray-600">4</div>
188
+ <div>
189
+ <p class="font-medium text-sm">Cenas</p>
190
+ <p class="text-xs text-gray-500">Imagens AI</p>
191
+ </div>
192
+ </div>
193
+
194
+ <!-- Step 5 -->
195
+ <div id="step-indicator-5" class="flex items-center gap-3 p-3 rounded-lg border border-transparent opacity-50 transition-all">
196
+ <div class="w-7 h-7 rounded-full bg-gray-700 text-gray-400 flex items-center justify-center text-xs font-bold border border-gray-600">5</div>
197
+ <div>
198
+ <p class="font-medium text-sm">Render</p>
199
+ <p class="text-xs text-gray-500">Vídeo Final</p>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ <!-- Center: Workspace -->
207
+ <div class="lg:col-span-9 space-y-6">
208
+
209
+ <!-- STEP 1: ROTEIRO -->
210
+ <div id="step-1" class="glass-panel rounded-xl p-8 border-t-4 border-cyan-500">
211
+ <div class="flex justify-between items-start mb-6">
212
+ <div>
213
+ <h2 class="text-2xl font-bold text-white mb-1">1. Criação do Roteiro</h2>
214
+ <p class="text-gray-400 text-sm">Defina o conceito ou deixe a IA criar para você.</p>
215
+ </div>
216
+ <div class="flex gap-2">
217
+ <button onclick="setScriptMode('manual')" id="btn-manual" class="px-4 py-2 rounded-lg bg-gray-700 text-white text-sm hover:bg-gray-600 transition">Manual</button>
218
+ <button onclick="setScriptMode('ai')" id="btn-ai" class="px-4 py-2 rounded-lg bg-cyan-600 text-white text-sm hover:bg-cyan-500 transition shadow-lg shadow-cyan-500/20">Gerar com IA</button>
219
+ </div>
220
+ </div>
221
+
222
+ <!-- AI Generation Inputs -->
223
+ <div id="ai-inputs" class="space-y-4 mb-6 animate-fade-in">
224
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
225
+ <div>
226
+ <label class="block text-xs font-medium text-gray-400 mb-1">Produto/Serviço</label>
227
+ <input type="text" id="product-input" placeholder="Ex: Curso de Marketing Digital" class="w-full bg-gray-900 border border-gray-700 rounded-lg px-4 py-2 text-white focus:border-cyan-500 focus:ring-1 focus:ring-cyan-500 outline-none transition">
228
+ </div>
229
+ <div>
230
+ <label class="block text-xs font-medium text-gray-400 mb-1">Público Alvo</label>
231
+ <input type="text" id="audience-input" placeholder="Ex: Empreendedores iniciantes" class="w-full bg-gray-900 border border-gray-700 rounded-lg px-4 py-2 text-white focus:border-cyan-500 focus:ring-1 focus:ring-cyan-500 outline-none transition">
232
+ </div>
233
+ </div>
234
+ <div>
235
+ <label class="block text-xs font-medium text-gray-400 mb-1">Tom de Voz</label>
236
+ <select id="tone-select" class="w-full bg-gray-900 border border-gray-700 rounded-lg px-4 py-2 text-white focus:border-cyan-500 outline-none">
237
+ <option value="profissional">Profissional & Sério</option>
238
+ <option value="entusiasmado">Entusiasmado & Energético</option>
239
+ <option value="dramatico">Storytelling Dramático</option>
240
+ <option value="vsl">VSL (Video Sales Letter) Direto</option>
241
+ </select>
242
+ </div>
243
+ <div class="flex items-center gap-4">
244
+ <div class="flex-1">
245
+ <label class="block text-xs font-medium text-gray-400 mb-1">Duração Estimada: <span id="duration-display" class="text-cyan-400">30s</span></label>
246
+ <input type="range" id="duration-slider" min="15" max="90" value="30" step="15" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
247
+ </div>
248
+ </div>
249
+ <button onclick="generateScript()" class="w-full bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 text-white font-bold py-3 rounded-lg transition-all transform hover:scale-[1.01] flex items-center justify-center gap-2">
250
+ <i class="fa-solid fa-wand-magic-sparkles"></i> Gerar Roteiro com IA
251
+ </button>
252
+ </div>
253
+
254
+ <!-- Editor -->
255
+ <div class="relative">
256
+ <label class="block text-xs font-medium text-gray-400 mb-1">Roteiro (Copy)</label>
257
+ <textarea id="script-editor" rows="8" class="w-full bg-gray-900 border border-gray-700 rounded-lg p-4 text-white font-mono text-sm focus:border-cyan-500 outline-none resize-none" placeholder="Cole seu roteiro aqui ou gere com IA..."></textarea>
258
+ <div class="absolute bottom-3 right-3 text-xs text-gray-500">
259
+ <span id="char-count">0</span> chars | ~<span id="word-time">0</span>s
260
+ </div>
261
+ </div>
262
+
263
+ <div class="mt-6 flex justify-end">
264
+ <button onclick="nextStep(2)" class="bg-white text-black font-bold py-2 px-6 rounded-lg hover:bg-gray-200 transition flex items-center gap-2">
265
+ Próximo: Narração <i class="fa-solid fa-arrow-right"></i>
266
+ </button>
267
+ </div>
268
+ </div>
269
+
270
+ <!-- STEP 2: NARRAÇÃO -->
271
+ <div id="step-2" class="glass-panel rounded-xl p-8 border-t-4 border-purple-500 hidden">
272
+ <h2 class="text-2xl font-bold text-white mb-6">2. Geração de Narração</h2>
273
+
274
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
275
+ <div class="bg-gray-800/50 p-4 rounded-lg border border-gray-700 hover:border-purple-500 cursor-pointer transition group" onclick="selectVoiceProvider('elevenlabs')">
276
+ <div class="flex items-center justify-between mb-2">
277
+ <span class="font-bold text-purple-400">ElevenLabs</span>
278
+ <i class="fa-solid fa-check-circle text-gray-600 group-hover:text-purple-500"></i>
279
+ </div>
280
+ <p class="text-xs text-gray-400">Qualidade Ultra-Realista. Melhor para VSLs.</p>
281
+ </div>
282
+ <div class="bg-gray-800/50 p-4 rounded-lg border border-gray-700 hover:border-purple-500 cursor-pointer transition group" onclick="selectVoiceProvider('azure')">
283
+ <div class="flex items-center justify-between mb-2">
284
+ <span class="font-bold text-blue-400">Azure TTS</span>
285
+ <i class="fa-solid fa-check-circle text-gray-600 group-hover:text-blue-500"></i>
286
+ </div>
287
+ <p class="text-xs text-gray-400">Estável e rápido. Ótimo custo-benefício.</p>
288
+ </div>
289
+ <div class="bg-gray-800/50 p-4 rounded-lg border border-gray-700 hover:border-purple-500 cursor-pointer transition group" onclick="selectVoiceProvider('minimax')">
290
+ <div class="flex items-center justify-between mb-2">
291
+ <span class="font-bold text-green-400">MiniMax</span>
292
+ <i class="fa-solid fa-check-circle text-gray-600 group-hover:text-green-500"></i>
293
+ </div>
294
+ <p class="text-xs text-gray-400">Modelo novo com boa entonação.</p>
295
+ </div>
296
+ </div>
297
+
298
+ <div class="bg-gray-900 rounded-lg p-6 border border-gray-800">
299
+ <div class="flex items-center justify-between mb-4">
300
+ <h3 class="font-semibold text-white">Processamento de Blocos</h3>
301
+ <span class="text-xs bg-purple-900/50 text-purple-300 px-2 py-1 rounded border border-purple-700">Modo: Chunking Inteligente</span>
302
+ </div>
303
+
304
+ <div id="audio-progress-container" class="space-y-3">
305
+ <!-- Generated via JS -->
306
+ <div class="text-center py-8 text-gray-500">
307
+ <p>Aguardando início...</p>
308
+ </div>
309
+ </div>
310
+
311
+ <div class="mt-6 flex justify-center">
312
+ <button onclick="generateAudio()" id="btn-generate-audio" class="bg-purple-600 hover:bg-purple-500 text-white font-bold py-3 px-8 rounded-full shadow-lg shadow-purple-500/30 transition-all flex items-center gap-2">
313
+ <i class="fa-solid fa-microphone"></i> Sintetizar Voz
314
+ </button>
315
+ </div>
316
+ </div>
317
+
318
+ <div class="mt-6 flex justify-between">
319
+ <button onclick="prevStep(1)" class="text-gray-400 hover:text-white font-medium py-2 px-4">Voltar</button>
320
+ <button onclick="nextStep(3)" id="btn-next-3" class="bg-white text-black font-bold py-2 px-6 rounded-lg hover:bg-gray-200 transition opacity-50 cursor-not-allowed" disabled>
321
+ Próximo: Legendas <i class="fa-solid fa-arrow-right"></i>
322
+ </button>
323
+ </div>
324
+ </div>
325
+
326
+ <!-- STEP 3: LEGENDAS -->
327
+ <div id="step-3" class="glass-panel rounded-xl p-8 border-t-4 border-yellow-500 hidden">
328
+ <h2 class="text-2xl font-bold text-white mb-2">3. Transcrição & Legendas</h2>
329
+ <p class="text-gray-400 text-sm mb-6">Transcrevendo o áudio com precisão de palavra (Whisper).</p>
330
+
331
+ <div class="bg-black rounded-lg p-1 border border-gray-800 relative overflow-hidden h-64 flex items-center justify-center" id="waveform-container">
332
+ <canvas id="audio-visualizer" class="absolute inset-0 w-full h-full"></canvas>
333
+ <div id="transcribing-text" class="relative z-10 text-center hidden">
334
+ <div class="loader mb-4"></div>
335
+ <p class="text-yellow-400 font-mono animate-pulse">Analisando áudio...</p>
336
+ </div>
337
+ </div>
338
+
339
+ <div class="mt-4 bg-gray-900 rounded-lg p-4 border border-gray-800 h-48 overflow-y-auto font-mono text-sm text-gray-300" id="transcription-preview">
340
+ <!-- Preview of SRT/JSON format -->
341
+ <span class="text-gray-600 italic">A transcrição aparecerá aqui...</span>
342
+ </div>
343
+
344
+ <div class="mt-6 flex justify-between">
345
+ <button onclick="prevStep(2)" class="text-gray-400 hover:text-white font-medium py-2 px-4">Voltar</button>
346
+ <button onclick="transcribeAudio()" id="btn-transcribe" class="bg-yellow-600 hover:bg-yellow-500 text-white font-bold py-2 px-6 rounded-lg transition">
347
+ <i class="fa-solid fa-closed-captioning"></i> Gerar Legendas
348
+ </button>
349
+ <button onclick="nextStep(4)" id="btn-next-4" class="hidden bg-white text-black font-bold py-2 px-6 rounded-lg hover:bg-gray-200 transition">
350
+ Próximo: Cenas <i class="fa-solid fa-arrow-right"></i>
351
+ </button>
352
+ </div>
353
+ </div>
354
+
355
+ <!-- STEP 4: IMAGENS -->
356
+ <div id="step-4" class="glass-panel rounded-xl p-8 border-t-4 border-pink-500 hidden">
357
+ <div class="flex justify-between items-center mb-6">
358
+ <div>
359
+ <h2 class="text-2xl font-bold text-white">4. Construção de Cenas</h2>
360
+ <p class="text-gray-400 text-sm">Gerando imagens contextuais a cada 3 segundos.</p>
361
+ </div>
362
+ <div class="text-right">
363
+ <p class="text-xs text-gray-500 uppercase">Modelo Selecionado</p>
364
+ <p class="text-pink-400 font-bold">Nano Banana (SDXL)</p>
365
+ </div>
366
+ </div>
367
+
368
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4" id="scene-grid">
369
+ <!-- Scenes generated here -->
370
+ </div>
371
+
372
+ <div class="mt-8 p-4 bg-gray-800/50 rounded-lg border border-gray-700">
373
+ <h4 class="text-sm font-bold text-white mb-2"><i class="fa-solid fa-lightbulb text-yellow-400"></i> Contexto Visual Atual</h4>
374
+ <p class="text-xs text-gray-400">O sistema analisou o roteiro e determinou o tema: <span id="visual-theme" class="text-cyan-400 font-mono">[Tema não definido]</span>.</p>
375
+ </div>
376
+
377
+ <div class="mt-6 flex justify-between">
378
+ <button onclick="prevStep(3)" class="text-gray-400 hover:text-white font-medium py-2 px-4">Voltar</button>
379
+ <button onclick="nextStep(5)" class="bg-white text-black font-bold py-2 px-6 rounded-lg hover:bg-gray-200 transition">
380
+ Finalizar Vídeo <i class="fa-solid fa-film"></i>
381
+ </button>
382
+ </div>
383
+ </div>
384
+
385
+ <!-- STEP 5: RENDER -->
386
+ <div id="step-5" class="glass-panel rounded-xl p-8 border-t-4 border-green-500 hidden">
387
+ <h2 class="text-2xl font-bold text-white mb-6 text-center">5. Renderização Final</h2>
388
+
389
+ <div class="max-w-2xl mx-auto bg-black rounded-xl overflow-hidden border border-gray-800 shadow-2xl relative aspect-video group">
390
+ <!-- Video Player Simulation -->
391
+ <div id="video-player" class="w-full h-full flex items-center justify-center bg-gray-900 relative">
392
+ <img id="render-preview-img" src="" class="absolute inset-0 w-full h-full object-cover opacity-50 transition-opacity duration-300">
393
+ <div class="absolute inset-0 bg-gradient-to-t from-black/80 to-transparent"></div>
394
+
395
+ <!-- Playback Controls Overlay -->
396
+ <div class="absolute bottom-0 left-0 right-0 p-4 z-20">
397
+ <div class="w-full bg-gray-700 h-1 rounded-full mb-4 overflow-hidden">
398
+ <div id="progress-bar" class="bg-green-500 h-full w-0 transition-all duration-100"></div>
399
+ </div>
400
+ <div class="flex justify-between items-center text-white">
401
+ <div class="flex gap-4">
402
+ <button id="play-pause-btn" class="hover:text-green-400"><i class="fa-solid fa-play"></i></button>
403
+ <span class="text-xs font-mono mt-1">00:00 / <span id="total-duration">00:00</span></span>
404
+ </div>
405
+ <div class="flex gap-4">
406
+ <button class="hover:text-green-400"><i class="fa-solid fa-closed-captioning"></i></button>
407
+ <button class="hover:text-green-400"><i class="fa-solid fa-expand"></i></button>
408
+ </div>
409
+ </div>
410
+ </div>
411
+
412
+ <!-- Subtitle Overlay -->
413
+ <div class="absolute bottom-16 left-0 right-0 text-center px-8 z-10 pointer-events-none">
414
+ <p id="video-subtitle" class="text-white text-lg md:text-2xl font-bold drop-shadow-md stroke-black" style="text-shadow: 2px 2px 0 #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;">
415
+ Legendas aparecerão aqui
416
+ </p>
417
+ </div>
418
+
419
+ <!-- Center Play Button (Initial State) -->
420
+ <button id="center-play-btn" class="absolute z-30 w-16 h-16 bg-white/20 backdrop-blur rounded-full flex items-center justify-center hover:scale-110 transition text-white">
421
+ <i class="fa-solid fa-play text-2xl ml-1"></i>
422
+ </button>
423
+ </div>
424
+ </div>
425
+
426
+ <div class="mt-8 flex justify-center gap-4">
427
+ <button onclick="resetApp()" class="px-6 py-3 rounded-lg border border-gray-600 text-gray-300 hover:bg-gray-800 transition">Novo Projeto</button>
428
+ <button class="px-6 py-3 rounded-lg bg-green-600 hover:bg-green-500 text-white font-bold shadow-lg shadow-green-500/20 transition flex items-center gap-2">
429
+ <i class="fa-solid fa-download"></i> Baixar MP4 (Simulado)
430
+ </button>
431
+ </div>
432
+ </div>
433
+
434
+ </div>
435
+ </main>
436
+
437
+ <!-- Settings Modal -->
438
+ <div id="settings-modal" class="fixed inset-0 bg-black/80 backdrop-blur-sm z-[100] hidden flex items-center justify-center">
439
+ <div class="bg-gray-900 border border-gray-700 rounded-xl w-full max-w-lg p-6 shadow-2xl transform transition-all scale-100">
440
+ <div class="flex justify-between items-center mb-6">
441
+ <h3 class="text-xl font-bold text-white">Configurações de API</h3>
442
+ <button onclick="closeSettings()" class="text-gray-400 hover:text-white"><i class="fa-solid fa-xmark text-xl"></i></button>
443
+ </div>
444
+
445
+ <div class="space-y-4">
446
+ <div>
447
+ <label class="block text-sm font-medium text-gray-300 mb-1">OpenRouter API Key</label>
448
+ <input type="password" id="api-openrouter" class="w-full bg-black border border-gray-700 rounded px-3 py-2 text-white focus:border-cyan-500 outline-none" placeholder="sk-or-...">
449
+ <p class="text-xs text-gray-500 mt-1">Usado para geração de roteiros.</p>
450
+ </div>
451
+
452
+ <div class="border-t border-gray-800 my-4"></div>
453
+
454
+ <div>
455
+ <label class="block text-sm font-medium text-gray-300 mb-1">ElevenLabs API Key</label>
456
+ <input type="password" id="api-eleven" class="w-full bg-black border border-gray-700 rounded px-3 py-2 text-white focus:border-purple-500 outline-none">
457
+ </div>
458
+
459
+ <div>
460
+ <label class="block text-sm font-medium text-gray-300 mb-1">Azure Speech Key</label>
461
+ <input type="password" id="api-azure" class="w-full bg-black border border-gray-700 rounded px-3 py-2 text-white focus:border-blue-500 outline-none">
462
+ </div>
463
+ </div>
464
+
465
+ <div class="mt-8 flex justify-end">
466
+ <button onclick="saveSettings()" class="bg-cyan-600 hover:bg-cyan-500 text-white px-6 py-2 rounded-lg font-medium transition">Salvar Configurações</button>
467
+ </div>
468
+ </div>
469
+ </div>
470
+
471
+ <script>
472
+ // --- STATE MANAGEMENT ---
473
+ const state = {
474
+ step: 1,
475
+ script: "",
476
+ audioUrl: null,
477
+ transcription: [], // Array of { text, start, end }
478
+ scenes: [], // Array of image URLs
479
+ duration: 30,
480
+ isPlaying: false
481
+ };
482
+
483
+ // --- MOCK DATA & UTILS ---
484
+ const MOCK_IMAGES = [
485
+ "https://images.unsplash.com/photo-1557804506-669a67965ba0?w=800&q=80", // Business
486
+ "https://images.unsplash.com/photo-1516321318423-f06f85e504b3?w=800&q=80", // Laptop
487
+ "https://images.unsplash.com/photo-1556761175-5973dc0f32e7?w=800&q=80", // Meeting
488
+ "https://images.unsplash.com/photo-1551288049-bebda4e38f71?w=800&q=80", // Charts
489
+ "https://images.unsplash.com/photo-1507679799987-c73779587ccf?w=800&q=80", // Suit
490
+ "https://images.unsplash.com/photo-1531297424006-dbfa3e92ea1d?w=800&q=80" // Tech
491
+ ];
492
+
493
+ // --- NAVIGATION ---
494
+ function nextStep(step) {
495
+ document.getElementById(`step-${state.step}`).classList.add('hidden');
496
+ document.getElementById(`step-indicator-${state.step}`).classList.remove('step-active');
497
+ document.getElementById(`step-indicator-${state.step}`).classList.add('step-completed');
498
+
499
+ state.step = step;
500
+
501
+ document.getElementById(`step-${state.step}`).classList.remove('hidden');
502
+ document.getElementById(`step-indicator-${state.step}`).classList.remove('opacity-50');
503
+ document.getElementById(`step-indicator-${state.step}`).classList.add('step-active');
504
+
505
+ // Scroll to top
506
+ window.scrollTo({ top: 0, behavior: 'smooth' });
507
+ }
508
+
509
+ function prevStep(step) {
510
+ document.getElementById(`step-${state.step}`).classList.add('hidden');
511
+ document.getElementById(`step-indicator-${state.step}`).classList.add('opacity-50');
512
+ document.getElementById(`step-indicator-${state.step}`).classList.remove('step-active');
513
+
514
+ state.step = step;
515
+
516
+ document.getElementById(`step-${state.step}`).classList.remove('hidden');
517
+ document.getElementById(`step-indicator-${state.step}`).classList.add('step-active');
518
+ }
519
+
520
+ // --- STEP 1: SCRIPT ---
521
+ function setScriptMode(mode) {
522
+ const btnManual = document.getElementById('btn-manual');
523
+ const btnAi = document.getElementById('btn-ai');
524
+ const aiInputs = document.getElementById('ai-inputs');
525
+
526
+ if (mode === 'manual') {
527
+ btnManual.classList.replace('bg-gray-700', 'bg-cyan-600');
528
+ btnAi.classList.replace('bg-cyan-600', 'bg-gray-700');
529
+ aiInputs.classList.add('hidden');
530
+ } else {
531
+ btnAi.classList.replace('bg-gray-700', 'bg-cyan-600');
532
+ btnManual.classList.replace('bg-cyan-600', 'bg-gray-700');
533
+ aiInputs.classList.remove('hidden');
534
+ }
535
+ }
536
+
537
+ // Character counting
538
+ document.getElementById('script-editor').addEventListener('input', (e) => {
539
+ const text = e.target.value;
540
+ document.getElementById('char-count').innerText = text.length;
541
+ // Rough estimate: 130 words per minute. Avg 5 chars per word. ~650 chars per min -> ~11 chars per sec
542
+ const seconds = Math.ceil(text.length / 11);
543
+ document.getElementById('word-time').innerText = seconds;
544
+ });
545
+
546
+ // Duration Slider
547
+ document.getElementById('duration-slider').addEventListener('input', (e) => {
548
+ document.getElementById('duration-display').innerText = e.target.value + 's';
549
+ state.duration = parseInt(e.target.value);
550
+ });
551
+
552
+ async function generateScript() {
553
+ const product = document.getElementById('product-input').value;
554
+ const audience = document.getElementById('audience-input').value;
555
+ const tone = document.getElementById('tone-select').value;
556
+ const btn = document.querySelector('#ai-inputs button');
557
+
558
+ if (!product || !audience) {
559
+ alert("Preencha o produto e público alvo.");
560
+ return;
561
+ }
562
+
563
+ const originalText = btn.innerHTML;
564
+ btn.innerHTML = `<i class="fa-solid fa-circle-notch fa-spin"></i> Gerando...`;
565
+ btn.disabled = true;
566
+
567
+ // Simulate API Call to OpenRouter
568
+ await new Promise(r => setTimeout(r, 2000));
569
+
570
+ // Mock Content Generation
571
+ const hooks = [
572
+ "Você está perdendo dinheiro todos os dias sem perceber.",
573
+ "Imagine acordar com vendas automáticas na sua conta.",
574
+ "O mercado mudou. E quem não adaptar, será engolido."
575
+ ];
576
+
577
+ const bodies = [
578
+ `O ${product} não é apenas mais um curso. É um sistema validado por ${audience} que já faturaram milhões.`,
579
+ `Nós testamos essa estratégia com 500 ${audience}. O resultado? Um aumento de 300% em conversão.`,
580
+ `Pare de tentar adivinhar. Use dados. Use inteligência. Use o ${product}.`
581
+ ];
582
+
583
+ const closes = [
584
+ "Clique no botão abaixo agora e transforme seu resultado.",
585
+ "As vagas estão acabando. Garanta a sua agora.",
586
+ "Seu futuro começa com uma decisão. Decida agora."
587
+ ];
588
+
589
+ const generatedScript = `${hooks[Math.floor(Math.random()*hooks.length)]}\n\n${bodies[Math.floor(Math.random()*bodies.length)]}\n\n${closes[Math.floor(Math.random()*closes.length)]}`;
590
+
591
+ // Typewriter effect
592
+ const editor = document.getElementById('script-editor');
593
+ editor.value = "";
594
+ let i = 0;
595
+ const type = () => {
596
+ if (i < generatedScript.length) {
597
+ editor.value += generatedScript.charAt(i);
598
+ i++;
599
+ setTimeout(type, 20);
600
+ } else {
601
+ btn.innerHTML = originalText;
602
+ btn.disabled = false;
603
+ // Trigger input event to update counters
604
+ editor.dispatchEvent(new Event('input'));
605
+ }
606
+ };
607
+ type();
608
+ }
609
+
610
+ // --- STEP 2: AUDIO ---
611
+ function selectVoiceProvider(provider) {
612
+ // Visual selection logic
613
+ const cards = document.querySelectorAll('#step-2 .grid > div');
614
+ cards.forEach(c => c.classList.remove('border-purple-500', 'bg-gray-800'));
615
+ cards.forEach(c => c.classList.add('border-gray-700', 'bg-gray-800/50'));
616
+
617
+ // Find clicked (simplified for demo)
618
+ event.currentTarget.classList.remove('border-gray-700', 'bg-gray-800/50');
619
+ event.currentTarget.classList.add('border-purple-500', 'bg-gray-800');
620
+ }
621
+
622
+ async function generateAudio() {
623
+ const container = document.getElementById('audio-progress-container');
624
+ const btn = document.getElementById('btn-generate-audio');
625
+ const nextBtn = document.getElementById('btn-next-3');
626
+
627
+ btn.innerHTML = `<i class="fa-solid fa-circle-notch fa-spin"></i> Processando...`;
628
+ btn.disabled = true;
629
+
630
+ // Simulate chunking
631
+ container.innerHTML = '';
632
+ const chunks = 4;
633
+
634
+ for (let i = 1; i <= chunks; i++) {
635
+ const div = document.createElement('div');
636
+ div.className = "flex items-center gap-3 bg-gray-800 p-3 rounded border border-gray-700";
637
+ div.innerHTML = `
638
+ <div class="w-2 h-2 rounded-full bg-yellow-500 animate-pulse"></div>
639
+ <div class="flex-1">
640
+ <div class="flex justify-between text-xs mb-1">
641
+ <span class="text-gray-300">Bloco de Áudio ${i}/${chunks}</span>
642
+ <span class="text-gray-500">Processando...</span>
643
+ </div>
644
+ <div class="w-full bg-gray-700 h-1.5 rounded-full overflow-hidden">
645
+ <div class="bg-purple-500 h-full w-0 transition-all duration-1000" style="width: 0%" id="prog-${i}"></div>
646
+ </div>
647
+ </div>
648
+ `;
649
+ container.appendChild(div);
650
+
651
+ // Simulate processing time per chunk
652
+ await new Promise(r => setTimeout(r, 800));
653
+ document.getElementById(`prog-${i}`).style.width = '100%';
654
+ div.querySelector('.text-gray-500').innerText = "Concluído";
655
+ div.querySelector('.bg-yellow-500').classList.replace('bg-yellow-500', 'bg-green-500');
656
+ div.querySelector('.bg-yellow-500').classList.remove('animate-pulse');
657
+ }
658
+
659
+ // Finalize
660
+ btn.innerHTML = `<i class="fa-solid fa-check"></i> Áudio Gerado`;
661
+ btn.classList.replace('bg-purple-600', 'bg-green-600');
662
+
663
+ nextBtn.disabled = false;
664
+ nextBtn.classList.remove('opacity-50', 'cursor-not-allowed');
665
+
666
+ // Create a dummy audio URL (Tone.js synth for demo)
667
+ // In a real app, this would be the concatenated Blob URL
668
+ }
669
+
670
+ // --- STEP 3: TRANSCRIPTION ---
671
+ async function transcribeAudio() {
672
+ const btn = document.getElementById('btn-transcribe');
673
+ const nextBtn = document.getElementById('btn-next-4');
674
+ const loader = document.getElementById('transcribing-text');
675
+ const preview = document.getElementById('transcription-preview');
676
+
677
+ btn.classList.add('hidden');
678
+ loader.classList.remove('hidden');
679
+ loader.classList.add('flex', 'flex-col', 'items-center');
680
+
681
+ // Simulate Whisper API delay
682
+ await new Promise(r => setTimeout(r, 2500));
683
+
684
+ // Generate mock transcription data
685
+ const scriptText = document.getElementById('script-editor').value;
686
+ const words = scriptText.split(' ');
687
+ const wordDuration = 0.4; // seconds per word approx
688
+
689
+ let currentTime = 0;
690
+ let srtContent = "";
691
+
692
+ words.forEach((word, index) => {
693
+ const start = currentTime;
694
+ const end = currentTime + wordDuration + (Math.random() * 0.2);
695
+
696
+ state.transcription.push({
697
+ word: word,
698
+ start: start,
699
+ end: end
700
+ });
701
+
702
+ srtContent += `<div class="mb-1"><span class="text-yellow-500">${start.toFixed(2)}s</span> ${word}</div>`;
703
+ currentTime = end;
704
+ });
705
+
706
+ loader.classList.add('hidden');
707
+ loader.classList.remove('flex');
708
+ preview.innerHTML = srtContent;
709
+
710
+ nextBtn.classList.remove('hidden');
711
+ }
712
+
713
+ // --- STEP 4: IMAGES ---
714
+ // Pre-load step 4 visuals when entering
715
+ function initStep4() {
716
+ const grid = document.getElementById('scene-grid');
717
+ grid.innerHTML = '';
718
+
719
+ // Determine number of scenes based on duration (every 3 seconds)
720
+ const numScenes = Math.ceil(state.duration / 3);
721
+ const theme = document.getElementById('product-input').value || "Marketing Digital";
722
+ document.getElementById('visual-theme').innerText = theme;
723
+
724
+ for(let i=0; i<numScenes; i++) {
725
+ const div = document.createElement('div');
726
+ div.className = "relative group aspect-video bg-gray-800 rounded-lg overflow-hidden border border-gray-700";
727
+ div.innerHTML = `
728
+ <div class="absolute inset-0 flex items-center justify-center bg-gray-900 z-10" id="img-loader-${i}">
729
+ <i class="fa-solid fa-image text-gray-600 text-2xl animate-bounce"></i>
730
+ </div>
731
+ <img id="scene-img-${i}" src="" class="w-full h-full object-cover opacity-0 transition-opacity duration-500">
732
+ <div class="absolute bottom-0 left-0 right-0 bg-black/70 p-2 translate-y-full group-hover:translate-y-0 transition-transform">
733
+ <button class="text-xs text-white hover:text-cyan-400 w-full text-left"><i class="fa-solid fa-rotate"></i> Regenerar</button>
734
+ <button class="text-xs text-white hover:text-red-400 w-full text-left"><i class="fa-solid fa-trash"></i> Remover</button>
735
+ </div>
736
+ <div class="absolute top-2 right-2 bg-black/50 text-white text-xs px-2 py-1 rounded backdrop-blur">
737
+ 00:${(i*3).toString().padStart(2,'0')}
738
+ </div>
739
+ `;
740
+ grid.appendChild(div);
741
+
742
+ // Simulate Image Gen Delay
743
+ setTimeout(() => {
744
+ const img = document.getElementById(`scene-img-${i}`);
745
+ const loader = document.getElementById(`img-loader-${i}`);
746
+ // Pick random mock image
747
+ img.src = MOCK_IMAGES[Math.floor(Math.random() * MOCK_IMAGES.length)];
748
+ img.onload = () => {
749
+ img.classList.remove('opacity-0');
750
+ loader.classList.add('hidden');
751
+ };
752
+ state.scenes.push(img.src);
753
+ }, 500 + (i * 800));
754
+ }
755
+ }
756
+
757
+ // Hook step 4 init
758
+ const observer = new MutationObserver((mutations) => {
759
+ mutations.forEach((mutation) => {
760
+ if (mutation.target.id === 'step-4' && !mutation.target.classList.contains('hidden')) {
761
+ initStep4();
762
+ }
763
+ });
764
+ });
765
+ observer.observe(document.getElementById('step-4'), { attributes: true, attributeFilter: ['class'] });
766
+
767
+
768
+ // --- STEP 5: PLAYER LOGIC ---
769
+ let playbackInterval;
770
+
771
+ // Hook step 5 init
772
+ const observer5 = new MutationObserver((mutations) => {
773
+ mutations.forEach((mutation) => {
774
+ if (mutation.target.id === 'step-5' && !mutation.target.classList.contains('hidden')) {
775
+ initPlayer();
776
+ }
777
+ });
778
+ });
779
+ observer5.observe(document.getElementById('step-5'), { attributes: true, attributeFilter: ['class'] });
780
+
781
+ function initPlayer() {
782
+ const playBtn = document.getElementById('center-play-btn');
783
+ const playPauseBtn = document.getElementById('play-pause-btn');
784
+
785
+ playBtn.onclick = togglePlay;
786
+ playPauseBtn.onclick = togglePlay;
787
+
788
+ // Set total duration
789
+ const mins = Math.floor(state.duration / 60);
790
+ const secs = state.duration % 60;
791
+ document.getElementById('total-duration').innerText = `${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;
792
+
793
+ // Set first image
794
+ if(state.scenes.length > 0) {
795
+ document.getElementById('render-preview-img').src = state.scenes[0];
796
+ }
797
+ }
798
+
799
+ function togglePlay() {
800
+ state.isPlaying = !state.isPlaying;
801
+ const icon = state.isPlaying ? '<i class="fa-solid fa-pause"></i>' : '<i class="fa-solid fa-play"></i>';
802
+ document.getElementById('play-pause-btn').innerHTML = icon;
803
+ document.getElementById('center-play-btn').style.opacity = state.isPlaying ? '0' : '1';
804
+ document.getElementById('center-play-btn').style.pointerEvents = state.isPlaying ? 'none' : 'auto';
805
+
806
+ if (state.isPlaying) {
807
+ let currentTime = 0;
808
+ const progressBar = document.getElementById('progress-bar');
809
+ const subtitleEl = document.getElementById('video-subtitle');
810
+ const imgEl = document.getElementById('render-preview-img');
811
+
812
+ playbackInterval = setInterval(() => {
813
+ currentTime += 0.1;
814
+
815
+ // Update Progress Bar
816
+ const pct = (currentTime / state.duration) * 100;
817
+ progressBar.style.width = `${pct}%`;
818
+
819
+ // Update Subtitles
820
+ // Find current word
821
+ const currentWordObj = state.transcription.find(t => t.start <= currentTime && t.end >= currentTime);
822
+ if (currentWordObj) {
823
+ subtitleEl.innerText = currentWordObj.word;
824
+ } else {
825
+ // Simple logic to show phrase if word precision fails (mock)
826
+ // In a real app, we map time to phrase
827
+ }