razvanab commited on
Commit
bd67099
verified
1 Parent(s): 0c8e4b2

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +879 -19
index.html CHANGED
@@ -1,19 +1,879 @@
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="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>OllamaStream - Local AI Media Tools</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+ <style>
12
+ :root {
13
+ --bg-dark: #0f0f13;
14
+ --bg-panel: #18181f;
15
+ --bg-panel-hover: #22222b;
16
+ --primary: #6366f1;
17
+ --primary-glow: rgba(99, 102, 241, 0.4);
18
+ --accent: #10b981;
19
+ --text-main: #ffffff;
20
+ --text-muted: #9ca3af;
21
+ --border: #2d2d3a;
22
+ --glass: rgba(24, 24, 31, 0.7);
23
+ --glass-border: rgba(255, 255, 255, 0.08);
24
+ --danger: #ef4444;
25
+ }
26
+
27
+ * {
28
+ box-sizing: border-box;
29
+ margin: 0;
30
+ padding: 0;
31
+ outline: none;
32
+ }
33
+
34
+ body {
35
+ font-family: 'Inter', sans-serif;
36
+ background-color: var(--bg-dark);
37
+ color: var(--text-main);
38
+ min-height: 100vh;
39
+ display: flex;
40
+ flex-direction: column;
41
+ overflow-x: hidden;
42
+ }
43
+
44
+ /* --- Header --- */
45
+ header {
46
+ height: 70px;
47
+ border-bottom: 1px solid var(--border);
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: space-between;
51
+ padding: 0 2rem;
52
+ background: var(--glass);
53
+ backdrop-filter: blur(12px);
54
+ position: sticky;
55
+ top: 0;
56
+ z-index: 100;
57
+ }
58
+
59
+ .logo {
60
+ display: flex;
61
+ align-items: center;
62
+ gap: 12px;
63
+ font-weight: 700;
64
+ font-size: 1.2rem;
65
+ background: linear-gradient(135deg, #fff 0%, #a5b4fc 100%);
66
+ -webkit-background-clip: text;
67
+ -webkit-text-fill-color: transparent;
68
+ }
69
+
70
+ .logo i {
71
+ background: var(--primary);
72
+ -webkit-background-clip: text;
73
+ -webkit-text-fill-color: transparent;
74
+ font-size: 1.5rem;
75
+ }
76
+
77
+ .status-indicator {
78
+ display: flex;
79
+ align-items: center;
80
+ gap: 8px;
81
+ font-size: 0.85rem;
82
+ color: var(--text-muted);
83
+ background: rgba(255,255,255,0.05);
84
+ padding: 6px 12px;
85
+ border-radius: 20px;
86
+ }
87
+
88
+ .status-dot {
89
+ width: 8px;
90
+ height: 8px;
91
+ border-radius: 50%;
92
+ background-color: var(--accent);
93
+ box-shadow: 0 0 8px var(--accent);
94
+ animation: pulse 2s infinite;
95
+ }
96
+
97
+ .built-with {
98
+ font-size: 0.85rem;
99
+ color: var(--text-muted);
100
+ text-decoration: none;
101
+ border: 1px solid var(--border);
102
+ padding: 6px 12px;
103
+ border-radius: 6px;
104
+ transition: all 0.3s ease;
105
+ }
106
+
107
+ .built-with:hover {
108
+ border-color: var(--primary);
109
+ color: var(--primary);
110
+ }
111
+
112
+ /* --- Layout --- */
113
+ .app-container {
114
+ display: flex;
115
+ flex: 1;
116
+ height: calc(100vh - 70px);
117
+ }
118
+
119
+ /* --- Sidebar --- */
120
+ aside {
121
+ width: 260px;
122
+ background: var(--bg-panel);
123
+ border-right: 1px solid var(--border);
124
+ padding: 2rem 1rem;
125
+ display: flex;
126
+ flex-direction: column;
127
+ gap: 0.5rem;
128
+ }
129
+
130
+ .nav-item {
131
+ display: flex;
132
+ align-items: center;
133
+ gap: 12px;
134
+ padding: 12px 16px;
135
+ border-radius: 10px;
136
+ color: var(--text-muted);
137
+ cursor: pointer;
138
+ transition: all 0.2s ease;
139
+ font-weight: 500;
140
+ }
141
+
142
+ .nav-item:hover {
143
+ background: var(--bg-panel-hover);
144
+ color: var(--text-main);
145
+ }
146
+
147
+ .nav-item.active {
148
+ background: rgba(99, 102, 241, 0.1);
149
+ color: var(--primary);
150
+ border: 1px solid rgba(99, 102, 241, 0.2);
151
+ }
152
+
153
+ .nav-item i {
154
+ width: 20px;
155
+ text-align: center;
156
+ }
157
+
158
+ /* --- Main Content --- */
159
+ main {
160
+ flex: 1;
161
+ padding: 2rem;
162
+ overflow-y: auto;
163
+ position: relative;
164
+ }
165
+
166
+ .panel-header {
167
+ margin-bottom: 2rem;
168
+ }
169
+
170
+ .panel-header h1 {
171
+ font-size: 2rem;
172
+ margin-bottom: 0.5rem;
173
+ }
174
+
175
+ .panel-header p {
176
+ color: var(--text-muted);
177
+ }
178
+
179
+ /* --- Drag Drop Zone --- */
180
+ .upload-zone {
181
+ border: 2px dashed var(--border);
182
+ border-radius: 16px;
183
+ padding: 4rem 2rem;
184
+ text-align: center;
185
+ background: rgba(255,255,255,0.02);
186
+ transition: all 0.3s ease;
187
+ cursor: pointer;
188
+ position: relative;
189
+ overflow: hidden;
190
+ }
191
+
192
+ .upload-zone:hover, .upload-zone.drag-over {
193
+ border-color: var(--primary);
194
+ background: rgba(99, 102, 241, 0.05);
195
+ }
196
+
197
+ .upload-zone i {
198
+ font-size: 3rem;
199
+ color: var(--primary);
200
+ margin-bottom: 1rem;
201
+ }
202
+
203
+ .upload-zone h3 {
204
+ margin-bottom: 0.5rem;
205
+ }
206
+
207
+ .upload-zone p {
208
+ color: var(--text-muted);
209
+ font-size: 0.9rem;
210
+ }
211
+
212
+ .file-info {
213
+ margin-top: 1rem;
214
+ display: none;
215
+ background: var(--bg-panel);
216
+ padding: 1rem;
217
+ border-radius: 8px;
218
+ border: 1px solid var(--border);
219
+ align-items: center;
220
+ justify-content: space-between;
221
+ }
222
+
223
+ .file-details {
224
+ display: flex;
225
+ align-items: center;
226
+ gap: 10px;
227
+ }
228
+
229
+ /* --- Controls Grid --- */
230
+ .controls-grid {
231
+ display: grid;
232
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
233
+ gap: 1.5rem;
234
+ margin-top: 2rem;
235
+ }
236
+
237
+ .input-group label {
238
+ display: block;
239
+ margin-bottom: 0.5rem;
240
+ font-size: 0.9rem;
241
+ color: var(--text-muted);
242
+ }
243
+
244
+ .form-select, .form-input {
245
+ width: 100%;
246
+ background: var(--bg-panel);
247
+ border: 1px solid var(--border);
248
+ color: var(--text-main);
249
+ padding: 12px;
250
+ border-radius: 8px;
251
+ font-family: inherit;
252
+ transition: border-color 0.2s;
253
+ }
254
+
255
+ .form-select:focus, .form-input:focus {
256
+ border-color: var(--primary);
257
+ }
258
+
259
+ /* --- Action Button --- */
260
+ .action-btn {
261
+ margin-top: 2rem;
262
+ width: 100%;
263
+ padding: 16px;
264
+ background: linear-gradient(90deg, var(--primary) 0%, #4f46e5 100%);
265
+ color: white;
266
+ border: none;
267
+ border-radius: 12px;
268
+ font-size: 1rem;
269
+ font-weight: 600;
270
+ cursor: pointer;
271
+ transition: all 0.3s ease;
272
+ display: flex;
273
+ align-items: center;
274
+ justify-content: center;
275
+ gap: 10px;
276
+ box-shadow: 0 4px 20px var(--primary-glow);
277
+ }
278
+
279
+ .action-btn:hover {
280
+ transform: translateY(-2px);
281
+ box-shadow: 0 8px 30px var(--primary-glow);
282
+ }
283
+
284
+ .action-btn:disabled {
285
+ opacity: 0.5;
286
+ cursor: not-allowed;
287
+ transform: none;
288
+ }
289
+
290
+ /* --- Results Section --- */
291
+ .results-area {
292
+ margin-top: 3rem;
293
+ display: none;
294
+ animation: fadeIn 0.5s ease;
295
+ }
296
+
297
+ .result-card {
298
+ background: var(--bg-panel);
299
+ border: 1px solid var(--border);
300
+ border-radius: 12px;
301
+ overflow: hidden;
302
+ }
303
+
304
+ .result-header {
305
+ padding: 1rem 1.5rem;
306
+ border-bottom: 1px solid var(--border);
307
+ display: flex;
308
+ justify-content: space-between;
309
+ align-items: center;
310
+ }
311
+
312
+ .result-content {
313
+ padding: 1.5rem;
314
+ min-height: 200px;
315
+ max-height: 400px;
316
+ overflow-y: auto;
317
+ font-family: 'JetBrains Mono', monospace;
318
+ font-size: 0.9rem;
319
+ line-height: 1.6;
320
+ color: var(--text-muted);
321
+ }
322
+
323
+ /* Audio Player Styling */
324
+ .audio-player-wrapper {
325
+ padding: 2rem;
326
+ display: flex;
327
+ flex-direction: column;
328
+ align-items: center;
329
+ gap: 1rem;
330
+ }
331
+
332
+ .waveform {
333
+ width: 100%;
334
+ height: 60px;
335
+ background: rgba(255,255,255,0.03);
336
+ border-radius: 8px;
337
+ position: relative;
338
+ overflow: hidden;
339
+ display: flex;
340
+ align-items: center;
341
+ justify-content: center;
342
+ gap: 3px;
343
+ }
344
+
345
+ .bar {
346
+ width: 4px;
347
+ background: var(--primary);
348
+ border-radius: 2px;
349
+ animation: wave 1s ease-in-out infinite;
350
+ }
351
+
352
+ /* Progress Bar */
353
+ .progress-container {
354
+ width: 100%;
355
+ background-color: rgba(255,255,255,0.1);
356
+ border-radius: 10px;
357
+ margin-top: 1rem;
358
+ height: 6px;
359
+ overflow: hidden;
360
+ display: none;
361
+ }
362
+
363
+ .progress-bar {
364
+ width: 0%;
365
+ height: 100%;
366
+ background: var(--primary);
367
+ border-radius: 10px;
368
+ transition: width 0.3s linear;
369
+ }
370
+
371
+ .status-text {
372
+ margin-top: 10px;
373
+ font-size: 0.9rem;
374
+ color: var(--text-muted);
375
+ text-align: center;
376
+ min-height: 1.5em;
377
+ }
378
+
379
+ /* Animations */
380
+ @keyframes pulse {
381
+ 0% { opacity: 1; }
382
+ 50% { opacity: 0.5; }
383
+ 100% { opacity: 1; }
384
+ }
385
+
386
+ @keyframes wave {
387
+ 0%, 100% { height: 10%; }
388
+ 50% { height: 100%; }
389
+ }
390
+
391
+ @keyframes fadeIn {
392
+ from { opacity: 0; transform: translateY(10px); }
393
+ to { opacity: 1; transform: translateY(0); }
394
+ }
395
+
396
+ /* Responsive */
397
+ @media (max-width: 768px) {
398
+ .app-container {
399
+ flex-direction: column;
400
+ height: auto;
401
+ }
402
+
403
+ aside {
404
+ width: 100%;
405
+ flex-direction: row;
406
+ overflow-x: auto;
407
+ padding: 1rem;
408
+ border-bottom: 1px solid var(--border);
409
+ border-right: none;
410
+ }
411
+
412
+ .nav-item {
413
+ white-space: nowrap;
414
+ }
415
+
416
+ header {
417
+ padding: 0 1rem;
418
+ }
419
+
420
+ .built-with {
421
+ display: none; /* Hide on mobile for cleaner UI */
422
+ }
423
+ }
424
+ </style>
425
+ </head>
426
+ <body>
427
+
428
+ <header>
429
+ <div class="logo">
430
+ <i class="fa-solid fa-microphone-lines"></i>
431
+ OllamaStream
432
+ </div>
433
+ <div class="status-indicator">
434
+ <div class="status-dot"></div>
435
+ <span>Ollama Server Connected (Local)</span>
436
+ </div>
437
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">Built with anycoder</a>
438
+ </header>
439
+
440
+ <div class="app-container">
441
+ <aside>
442
+ <div class="nav-item active" onclick="switchTab('transcribe')">
443
+ <i class="fa-solid fa-file-audio"></i>
444
+ Transcribe
445
+ </div>
446
+ <div class="nav-item" onclick="switchTab('translate')">
447
+ <i class="fa-solid fa-language"></i>
448
+ Translate
449
+ </div>
450
+ <div class="nav-item" onclick="switchTab('dub')">
451
+ <i class="fa-solid fa-wand-magic-sparkles"></i>
452
+ Dub Video
453
+ </div>
454
+ </aside>
455
+
456
+ <main id="main-content">
457
+ <!-- Dynamic Content will be injected here -->
458
+ </main>
459
+ </div>
460
+
461
+ <script>
462
+ // Application State
463
+ const state = {
464
+ activeTab: 'transcribe',
465
+ uploadedFile: null,
466
+ isProcessing: false,
467
+ progress: 0
468
+ };
469
+
470
+ // Available Models (Simulated)
471
+ const models = {
472
+ transcribe: ['whisper', 'whisper-large', 'faster-whisper'],
473
+ llm: ['llama3', 'mistral', 'gemma']
474
+ };
475
+
476
+ // DOM Elements
477
+ const mainContent = document.getElementById('main-content');
478
+
479
+ // Initial Render
480
+ renderInterface();
481
+
482
+ // --- Functions ---
483
+
484
+ function switchTab(tabName) {
485
+ state.activeTab = tabName;
486
+ state.uploadedFile = null; // Reset file on tab switch for simplicity
487
+ state.progress = 0;
488
+
489
+ // Update UI tabs
490
+ document.querySelectorAll('.nav-item').forEach(el => el.classList.remove('active'));
491
+ event.currentTarget.classList.add('active');
492
+
493
+ renderInterface();
494
+ }
495
+
496
+ function renderInterface() {
497
+ let html = '';
498
+
499
+ if (state.activeTab === 'transcribe') {
500
+ html = getTranscribeTemplate();
501
+ } else if (state.activeTab === 'translate') {
502
+ html = getTranslateTemplate();
503
+ } else if (state.activeTab === 'dub') {
504
+ html = getDubTemplate();
505
+ }
506
+
507
+ mainContent.innerHTML = html;
508
+
509
+ // Re-attach listeners after render
510
+ attachListeners();
511
+ }
512
+
513
+ // --- Templates ---
514
+
515
+ function getTranscribeTemplate() {
516
+ return `
517
+ <div class="panel-header">
518
+ <h1>Audio Transcription</h1>
519
+ <p>Convert audio to text using local Whisper models via Ollama.</p>
520
+ </div>
521
+
522
+ <div class="upload-zone" id="drop-zone">
523
+ <i class="fa-solid fa-cloud-arrow-up"></i>
524
+ <h3>Drag & Drop Audio File</h3>
525
+ <p>MP3, WAV, M4A, FLAC supported</p>
526
+ <input type="file" id="file-input" hidden accept="audio/*">
527
+ </div>
528
+
529
+ <div class="file-info" id="file-info">
530
+ <div class="file-details">
531
+ <i class="fa-solid fa-file-audio" style="color: var(--primary)"></i>
532
+ <div>
533
+ <div id="filename" style="font-weight:500">filename.mp3</div>
534
+ <div id="filesize" style="font-size:0.8rem; color:var(--text-muted)">2.4 MB</div>
535
+ </div>
536
+ </div>
537
+ <i class="fa-solid fa-xmark" style="cursor:pointer; color:var(--text-muted)" onclick="removeFile()"></i>
538
+ </div>
539
+
540
+ <div class="controls-grid">
541
+ <div class="input-group">
542
+ <label>Model</label>
543
+ <select class="form-select" id="model-select">
544
+ ${models.transcribe.map(m => `<option value="${m}">${m}</option>`).join('')}
545
+ </select>
546
+ </div>
547
+ <div class="input-group">
548
+ <label>Language (Optional)</label>
549
+ <select class="form-select">
550
+ <option value="">Auto Detect</option>
551
+ <option value="en">English</option>
552
+ <option value="es">Spanish</option>
553
+ <option value="fr">French</option>
554
+ <option value="de">German</option>
555
+ </select>
556
+ </div>
557
+ </div>
558
+
559
+ <button class="action-btn" id="process-btn" onclick="processTask('transcribe')">
560
+ <i class="fa-solid fa-play"></i> Start Transcription
561
+ </button>
562
+
563
+ ${getProgressTemplate()}
564
+ ${getResultsTemplate('transcription')}
565
+ `;
566
+ }
567
+
568
+ function getTranslateTemplate() {
569
+ return `
570
+ <div class="panel-header">
571
+ <h1>Smart Translate</h1>
572
+ <p>Translate content using Llama 3 for context-aware accuracy.</p>
573
+ </div>
574
+
575
+ <div class="upload-zone" id="drop-zone">
576
+ <i class="fa-solid fa-file-import"></i>
577
+ <h3>Drag Audio or Text File</h3>
578
+ <p>Supports SRT, VTT, TXT, and Audio files</p>
579
+ <input type="file" id="file-input" hidden>
580
+ </div>
581
+
582
+ <div class="file-info" id="file-info">
583
+ <div class="file-details">
584
+ <i class="fa-solid fa-file" style="color: var(--accent)"></i>
585
+ <div>
586
+ <div id="filename" style="font-weight:500">interview.srt</div>
587
+ </div>
588
+ </div>
589
+ <i class="fa-solid fa-xmark" style="cursor:pointer; color:var(--text-muted)" onclick="removeFile()"></i>
590
+ </div>
591
+
592
+ <div class="controls-grid">
593
+ <div class="input-group">
594
+ <label>Source Language</label>
595
+ <select class="form-select">
596
+ <option value="auto">Auto Detect</option>
597
+ <option value="en">English</option>
598
+ <option value="es">Spanish</option>
599
+ </select>
600
+ </div>
601
+ <div class="input-group">
602
+ <label>Target Language</label>
603
+ <select class="form-select">
604
+ <option value="es">Spanish</option>
605
+ <option value="fr">French</option>
606
+ <option value="de">German</option>
607
+ <option value="jp">Japanese</option>
608
+ </select>
609
+ </div>
610
+ <div class="input-group">
611
+ <label>AI Model</label>
612
+ <select class="form-select">
613
+ ${models.llm.map(m => `<option value="${m}">${m}</option>`).join('')}
614
+ </select>
615
+ </div>
616
+ </div>
617
+
618
+ <button class="action-btn" id="process-btn" onclick="processTask('translate')">
619
+ <i class="fa-solid fa-language"></i> Translate Content
620
+ </button>
621
+
622
+ ${getProgressTemplate()}
623
+ ${getResultsTemplate('translation')}
624
+ `;
625
+ }
626
+
627
+ function getDubTemplate() {
628
+ return `
629
+ <div class="panel-header">
630
+ <h1>AI Dubbing</h1>
631
+ <p>Replace original voice track with a generated AI voice clone.</p>
632
+ </div>
633
+
634
+ <div class="upload-zone" id="drop-zone">
635
+ <i class="fa-solid fa-video"></i>
636
+ <h3>Drag Video File</h3>
637
+ <p>MP4, MOV, WEBM supported</p>
638
+ <input type="file" id="file-input" hidden accept="video/*">
639
+ </div>
640
+
641
+ <div class="file-info" id="file-info">
642
+ <div class="file-details">
643
+ <i class="fa-solid fa-film" style="color: var(--danger)"></i>
644
+ <div>
645
+ <div id="filename" style="font-weight:500">presentation.mp4</div>
646
+ <div id="filesize" style="font-size:0.8rem; color:var(--text-muted)">15 MB</div>
647
+ </div>
648
+ </div>
649
+ <i class="fa-solid fa-xmark" style="cursor:pointer; color:var(--text-muted)" onclick="removeFile()"></i>
650
+ </div>
651
+
652
+ <div class="controls-grid">
653
+ <div class="input-group">
654
+ <label>Target Language</label>
655
+ <select class="form-select">
656
+ <option value="en-US">English (US)</option>
657
+ <option value="en-UK">English (UK)</option>
658
+ <option value="es-ES">Spanish (Spain)</option>
659
+ <option value="jp-JP">Japanese</option>
660
+ </select>
661
+ </div>
662
+ <div class="input-group">
663
+ <label>Voice Model</label>
664
+ <select class="form-select">
665
+ <option value="alloy">Alloy (Neutral)</option>
666
+ <option value="echo">Echo (British)</option>
667
+ <option value="shimmer">Shimmer (Female)</option>
668
+ </select>
669
+ </div>
670
+ </div>
671
+
672
+ <button class="action-btn" id="process-btn" onclick="processTask('dub')">
673
+ <i class="fa-solid fa-wand-magic-sparkles"></i> Generate Dub
674
+ </button>
675
+
676
+ ${getProgressTemplate()}
677
+ ${getResultsTemplate('dub')}
678
+ `;
679
+ }
680
+
681
+ function getProgressTemplate() {
682
+ return `
683
+ <div class="progress-container" id="progress-container">
684
+ <div class="progress-bar" id="progress-bar"></div>
685
+ </div>
686
+ <div class="status-text" id="status-text"></div>
687
+ `;
688
+ }
689
+
690
+ function getResultsTemplate(type) {
691
+ let content = '';
692
+ if (type === 'transcription') {
693
+ content = `<p style="color:var(--text-muted); text-align:center; padding:2rem;">No transcription generated yet.</p>`;
694
+ } else if (type === 'translation') {
695
+ content = `<p style="color:var(--text-muted); text-align:center; padding:2rem;">No translation generated yet.</p>`;
696
+ } else if (type === 'dub') {
697
+ content = `
698
+ <div class="audio-player-wrapper">
699
+ <div class="waveform" id="waveform">
700
+ <!-- Generated via JS -->
701
+ </div>
702
+ <audio controls style="width: 100%; margin-top:1rem;">
703
+ <source src="" type="audio/mpeg">
704
+ Your browser does not support the audio element.
705
+ </audio>
706
+ <div style="display:flex; gap:10px; margin-top:10px;">
707
+ <button class="form-select" style="width:auto; cursor:pointer;">Download MP3</button>
708
+ <button class="form-select" style="width:auto; cursor:pointer;">Download Video</button>
709
+ </div>
710
+ </div>
711
+ `;
712
+ }
713
+
714
+ return `
715
+ <div class="results-area" id="results-area">
716
+ <div class="result-card">
717
+ <div class="result-header">
718
+ <span style="font-weight:600">Output Result</span>
719
+ <button class="form-select" style="width:auto; padding:4px 8px; cursor:pointer; font-size:0.8rem;">Copy / Save</button>
720
+ </div>
721
+ <div class="result-content" id="result-content">
722
+ ${content}
723
+ </div>
724
+ </div>
725
+ </div>
726
+ `;
727
+ }
728
+
729
+ // --- Logic ---
730
+
731
+ function attachListeners() {
732
+ const dropZone = document.getElementById('drop-zone');
733
+ const fileInput = document.getElementById('file-input');
734
+
735
+ if(dropZone) {
736
+ dropZone.addEventListener('click', () => fileInput.click());
737
+
738
+ dropZone.addEventListener('dragover', (e) => {
739
+ e.preventDefault();
740
+ dropZone.classList.add('drag-over');
741
+ });
742
+
743
+ dropZone.addEventListener('dragleave', () => {
744
+ dropZone.classList.remove('drag-over');
745
+ });
746
+
747
+ dropZone.addEventListener('drop', (e) => {
748
+ e.preventDefault();
749
+ dropZone.classList.remove('drag-over');
750
+ if (e.dataTransfer.files.length) {
751
+ handleFile(e.dataTransfer.files[0]);
752
+ }
753
+ });
754
+ }
755
+
756
+ if(fileInput) {
757
+ fileInput.addEventListener('change', (e) => {
758
+ if (e.target.files.length) {
759
+ handleFile(e.target.files[0]);
760
+ }
761
+ });
762
+ }
763
+ }
764
+
765
+ function handleFile(file) {
766
+ state.uploadedFile = file;
767
+
768
+ // Update UI
769
+ document.getElementById('filename').innerText = file.name;
770
+ document.getElementById('filesize') ? document.getElementById('filesize').innerText = (file.size / 1024 / 1024).toFixed(2) + ' MB' : null;
771
+
772
+ document.querySelector('.upload-zone').style.display = 'none';
773
+ document.getElementById('file-info').style.display = 'flex';
774
+ }
775
+
776
+ function removeFile() {
777
+ state.uploadedFile = null;
778
+ document.querySelector('.upload-zone').style.display = 'block';
779
+ document.getElementById('file-info').style.display = 'none';
780
+ document.getElementById('file-input').value = '';
781
+ }
782
+
783
+ function processTask(task) {
784
+ if (!state.uploadedFile) {
785
+ alert('Please upload a file first.');
786
+ return;
787
+ }
788
+
789
+ const btn = document.getElementById('process-btn');
790
+ const progressContainer = document.getElementById('progress-container');
791
+ const progressBar = document.getElementById('progress-bar');
792
+ const statusText = document.getElementById('status-text');
793
+ const resultsArea = document.getElementById('results-area');
794
+
795
+ // Reset UI
796
+ btn.disabled = true;
797
+ btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Processing...';
798
+ progressContainer.style.display = 'block';
799
+ resultsArea.style.display = 'none';
800
+ state.progress = 0;
801
+
802
+ // Simulation Loop
803
+ const interval = setInterval(() => {
804
+ state.progress += Math.random() * 10;
805
+
806
+ if (state.progress > 100) state.progress = 100;
807
+
808
+ progressBar.style.width = state.progress + '%';
809
+
810
+ if (state.progress < 30) {
811
+ statusText.innerText = `Loading model context...`;
812
+ } else if (state.progress < 70) {
813
+ statusText.innerText = `Analyzing ${state.uploadedFile.name}...`;
814
+ } else if (state.progress < 95) {
815
+ statusText.innerText = task === 'dub' ? 'Synthesizing audio track...' : 'Generating text...';
816
+ } else {
817
+ statusText.innerText = 'Finalizing...';
818
+ }
819
+
820
+ if (state.progress >= 100) {
821
+ clearInterval(interval);
822
+ finishTask(task);
823
+ }
824
+ }, 300);
825
+ }
826
+
827
+ function finishTask(task) {
828
+ const btn = document.getElementById('process-btn');
829
+ const statusText = document.getElementById('status-text');
830
+ const resultsArea = document.getElementById('results-area');
831
+ const resultContent = document.getElementById('result-content');
832
+
833
+ btn.disabled = false;
834
+ btn.innerHTML = `<i class="fa-solid fa-check"></i> Done`;
835
+ statusText.innerText = 'Processing Complete';
836
+
837
+ resultsArea.style.display = 'block';
838
+
839
+ // Simulate results based on task
840
+ if (task === 'transcribe') {
841
+ resultContent.innerHTML = `
842
+ <div style="margin-bottom:10px; border-bottom:1px solid #333; padding-bottom:10px;">
843
+ <span style="color:var(--primary); font-size:0.8rem;">00:00:05</span>
844
+ <p>Welcome to the demonstration of the new AI system.</p>
845
+ </div>
846
+ <div style="margin-bottom:10px; border-bottom:1px solid #333; padding-bottom:10px;">
847
+ <span style="color:var(--primary); font-size:0.8rem;">00:00:12</span>
848
+ <p>Today we are going to explore how local models can process media.</p>
849
+ </div>
850
+ <div>
851
+ <span style="color:var(--primary); font-size:0.8rem;">00:00:20</span>
852
+ <p>This is a simulated transcription for the UI demo.</p>
853
+ </div>
854
+ `;
855
+ } else if (task === 'translate') {
856
+ resultContent.innerHTML = `
857
+ <p><strong>Original:</strong> This is a simulated transcript.</p>
858
+ <br>
859
+ <p><strong>Translated (Spanish):</strong> Esta es una transcripci贸n simulada.</p>
860
+ `;
861
+ } else if (task === 'dub') {
862
+ // Generate fake waveform bars
863
+ const waveform = document.getElementById('waveform');
864
+ if(waveform) {
865
+ for(let i=0; i<30; i++) {
866
+ let h = Math.floor(Math.random() * 40) + 10;
867
+ let bar = document.createElement('div');
868
+ bar.className = 'bar';
869
+ bar.style.height = h + '%';
870
+ bar.style.animationDelay = (i * 0.1) + 's';
871
+ waveform.appendChild(bar);
872
+ }
873
+ }
874
+ // Result content is already in template, just make sure it shows
875
+ }
876
+ }
877
+ </script>
878
+ </body>
879
+ </html>