7Zeux commited on
Commit
70afb1a
·
verified ·
1 Parent(s): 801ad47

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +536 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Ai Commented Vision
3
- emoji: 🔥
4
  colorFrom: pink
5
- colorTo: yellow
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: ai-commented-vision
3
+ emoji: 🐳
4
  colorFrom: pink
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,536 @@
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>Live Video Commentary Assistant</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
+ <style>
11
+ .video-container {
12
+ position: relative;
13
+ padding-bottom: 56.25%; /* 16:9 */
14
+ height: 0;
15
+ overflow: hidden;
16
+ }
17
+ .video-container video, .video-container iframe {
18
+ position: absolute;
19
+ top: 0;
20
+ left: 0;
21
+ width: 100%;
22
+ height: 100%;
23
+ border-radius: 0.5rem;
24
+ }
25
+ .commentary-bubble {
26
+ position: relative;
27
+ background: rgba(255, 255, 255, 0.1);
28
+ border-radius: 1rem;
29
+ padding: 1rem;
30
+ margin: 0.5rem 0;
31
+ animation: fadeIn 0.3s ease-in-out;
32
+ }
33
+ @keyframes fadeIn {
34
+ from { opacity: 0; transform: translateY(10px); }
35
+ to { opacity: 1; transform: translateY(0); }
36
+ }
37
+ .pulse {
38
+ animation: pulse 2s infinite;
39
+ }
40
+ @keyframes pulse {
41
+ 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
42
+ 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
43
+ 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
44
+ }
45
+ .tone-active {
46
+ border: 2px solid #3b82f6;
47
+ transform: scale(1.05);
48
+ }
49
+ </style>
50
+ </head>
51
+ <body class="bg-gray-900 text-white min-h-screen">
52
+ <div class="container mx-auto px-4 py-8">
53
+ <header class="mb-8">
54
+ <h1 class="text-4xl font-bold text-center mb-2 bg-gradient-to-r from-blue-400 to-purple-600 bg-clip-text text-transparent">
55
+ Live Video Commentary AI
56
+ </h1>
57
+ <p class="text-center text-gray-400 max-w-2xl mx-auto">
58
+ Get real-time AI-generated commentary on your live video feed with customizable tone and attitude
59
+ </p>
60
+ </header>
61
+
62
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
63
+ <!-- Video Input Section -->
64
+ <div class="lg:col-span-2 space-y-4">
65
+ <div class="bg-gray-800 rounded-xl p-4 shadow-lg">
66
+ <div class="flex justify-between items-center mb-4">
67
+ <h2 class="text-xl font-semibold">Video Input</h2>
68
+ <div class="flex space-x-2">
69
+ <button id="startWebcam" class="bg-blue-600 hover:bg-blue-700 px-4 py-2 rounded-lg flex items-center">
70
+ <i class="fas fa-camera mr-2"></i> Webcam
71
+ </button>
72
+ <button id="connectNdi" class="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded-lg flex items-center">
73
+ <i class="fas fa-broadcast-tower mr-2"></i> NDI
74
+ </button>
75
+ <button id="connectRtmp" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-lg flex items-center">
76
+ <i class="fas fa-satellite-dish mr-2"></i> RTMP
77
+ </button>
78
+ </div>
79
+ </div>
80
+
81
+ <div class="video-container bg-gray-900 rounded-lg">
82
+ <video id="liveVideo" autoplay playsinline class="w-full h-full object-cover"></video>
83
+ <div id="rtmpInput" class="hidden p-4">
84
+ <input type="text" id="rtmpUrl" placeholder="rtmp://example.com/live/streamkey"
85
+ class="w-full bg-gray-700 rounded-lg px-4 py-2 mb-2">
86
+ <button id="startRtmp" class="bg-green-600 hover:bg-green-700 px-4 py-2 rounded-lg w-full">
87
+ Connect RTMP
88
+ </button>
89
+ </div>
90
+ <div id="ndiInput" class="hidden p-4">
91
+ <select id="ndiSources" class="w-full bg-gray-700 rounded-lg px-4 py-2 mb-2">
92
+ <option value="">Select NDI Source</option>
93
+ </select>
94
+ <button id="startNdi" class="bg-purple-600 hover:bg-purple-700 px-4 py-2 rounded-lg w-full">
95
+ Connect NDI
96
+ </button>
97
+ </div>
98
+ </div>
99
+
100
+ <div class="mt-4 flex justify-between items-center">
101
+ <div class="flex space-x-2">
102
+ <button id="captureFrame" class="bg-gray-700 hover:bg-gray-600 px-4 py-2 rounded-lg">
103
+ <i class="fas fa-camera-retro mr-2"></i> Capture Frame
104
+ </button>
105
+ <button id="startAnalysis" class="bg-red-600 hover:bg-red-700 px-4 py-2 rounded-lg pulse">
106
+ <i class="fas fa-comment-dots mr-2"></i> Start Commentary
107
+ </button>
108
+ </div>
109
+ <div id="statusIndicator" class="flex items-center text-gray-400">
110
+ <i class="fas fa-circle-notch fa-spin mr-2"></i>
111
+ <span>Waiting for input</span>
112
+ </div>
113
+ </div>
114
+ </div>
115
+
116
+ <!-- Captured Frames -->
117
+ <div class="bg-gray-800 rounded-xl p-4 shadow-lg">
118
+ <h2 class="text-xl font-semibold mb-4">Captured Frames</h2>
119
+ <div id="capturedFrames" class="grid grid-cols-3 gap-2">
120
+ <!-- Frames will be added here -->
121
+ </div>
122
+ </div>
123
+ </div>
124
+
125
+ <!-- Commentary Controls -->
126
+ <div class="space-y-4">
127
+ <div class="bg-gray-800 rounded-xl p-4 shadow-lg">
128
+ <h2 class="text-xl font-semibold mb-4">Commentary Settings</h2>
129
+
130
+ <div class="mb-4">
131
+ <label class="block text-gray-400 mb-2">Tone</label>
132
+ <div class="grid grid-cols-3 gap-2">
133
+ <button data-tone="professional" class="tone-btn bg-gray-700 hover:bg-gray-600 p-2 rounded-lg">
134
+ <i class="fas fa-user-tie mr-1"></i> Professional
135
+ </button>
136
+ <button data-tone="casual" class="tone-btn bg-gray-700 hover:bg-gray-600 p-2 rounded-lg">
137
+ <i class="fas fa-smile mr-1"></i> Casual
138
+ </button>
139
+ <button data-tone="humorous" class="tone-btn bg-gray-700 hover:bg-gray-600 p-2 rounded-lg">
140
+ <i class="fas fa-laugh-squint mr-1"></i> Humorous
141
+ </button>
142
+ </div>
143
+ </div>
144
+
145
+ <div class="mb-4">
146
+ <label class="block text-gray-400 mb-2">Attitude</label>
147
+ <div class="grid grid-cols-3 gap-2">
148
+ <button data-attitude="neutral" class="attitude-btn bg-gray-700 hover:bg-gray-600 p-2 rounded-lg">
149
+ <i class="fas fa-meh mr-1"></i> Neutral
150
+ </button>
151
+ <button data-attitude="positive" class="attitude-btn bg-gray-700 hover:bg-gray-600 p-2 rounded-lg">
152
+ <i class="fas fa-thumbs-up mr-1"></i> Positive
153
+ </button>
154
+ <button data-attitude="critical" class="attitude-btn bg-gray-700 hover:bg-gray-600 p-2 rounded-lg">
155
+ <i class="fas fa-search mr-1"></i> Critical
156
+ </button>
157
+ </div>
158
+ </div>
159
+
160
+ <div class="mb-4">
161
+ <label class="block text-gray-400 mb-2">Commentary Speed</label>
162
+ <input type="range" id="commentarySpeed" min="1" max="10" value="5"
163
+ class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
164
+ <div class="flex justify-between text-xs text-gray-400 mt-1">
165
+ <span>Slow</span>
166
+ <span>Medium</span>
167
+ <span>Fast</span>
168
+ </div>
169
+ </div>
170
+
171
+ <div class="mb-4">
172
+ <label class="block text-gray-400 mb-2">Custom Prompt</label>
173
+ <textarea id="customPrompt" rows="3"
174
+ class="w-full bg-gray-700 rounded-lg px-4 py-2 text-white"
175
+ placeholder="Add specific instructions for the AI..."></textarea>
176
+ </div>
177
+
178
+ <button id="generateCommentary" class="w-full bg-blue-600 hover:bg-blue-700 px-4 py-3 rounded-lg font-medium">
179
+ <i class="fas fa-bolt mr-2"></i> Generate Commentary
180
+ </button>
181
+ </div>
182
+
183
+ <!-- Live Commentary -->
184
+ <div class="bg-gray-800 rounded-xl p-4 shadow-lg h-96 overflow-hidden flex flex-col">
185
+ <div class="flex justify-between items-center mb-4">
186
+ <h2 class="text-xl font-semibold">Live Commentary</h2>
187
+ <button id="clearComments" class="text-gray-400 hover:text-white">
188
+ <i class="fas fa-trash-alt"></i>
189
+ </button>
190
+ </div>
191
+ <div id="commentaryFeed" class="flex-1 overflow-y-auto space-y-2 pr-2">
192
+ <div class="commentary-bubble">
193
+ <div class="text-sm text-gray-400 mb-1">System</div>
194
+ <p>Welcome! Connect a video source and start generating real-time commentary.</p>
195
+ </div>
196
+ </div>
197
+ <div class="mt-2 flex items-center text-gray-400 text-sm">
198
+ <i class="fas fa-info-circle mr-2"></i>
199
+ <span>Analysis updates every 5 seconds</span>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ </div>
205
+
206
+ <script>
207
+ // DOM Elements
208
+ const liveVideo = document.getElementById('liveVideo');
209
+ const startWebcamBtn = document.getElementById('startWebcam');
210
+ const connectNdiBtn = document.getElementById('connectNdi');
211
+ const connectRtmpBtn = document.getElementById('connectRtmp');
212
+ const rtmpInput = document.getElementById('rtmpInput');
213
+ const ndiInput = document.getElementById('ndiInput');
214
+ const rtmpUrl = document.getElementById('rtmpUrl');
215
+ const startRtmpBtn = document.getElementById('startRtmp');
216
+ const ndiSources = document.getElementById('ndiSources');
217
+ const startNdiBtn = document.getElementById('startNdi');
218
+ const captureFrameBtn = document.getElementById('captureFrame');
219
+ const startAnalysisBtn = document.getElementById('startAnalysis');
220
+ const statusIndicator = document.getElementById('statusIndicator');
221
+ const capturedFrames = document.getElementById('capturedFrames');
222
+ const commentaryFeed = document.getElementById('commentaryFeed');
223
+ const generateCommentaryBtn = document.getElementById('generateCommentary');
224
+ const clearCommentsBtn = document.getElementById('clearComments');
225
+ const toneBtns = document.querySelectorAll('.tone-btn');
226
+ const attitudeBtns = document.querySelectorAll('.attitude-btn');
227
+ const commentarySpeed = document.getElementById('commentarySpeed');
228
+ const customPrompt = document.getElementById('customPrompt');
229
+
230
+ // State
231
+ let currentStream = null;
232
+ let analysisInterval = null;
233
+ let selectedTone = 'professional';
234
+ let selectedAttitude = 'neutral';
235
+ let isAnalyzing = false;
236
+ let frameCount = 0;
237
+
238
+ // Tone Selection
239
+ toneBtns.forEach(btn => {
240
+ btn.addEventListener('click', () => {
241
+ toneBtns.forEach(b => b.classList.remove('tone-active'));
242
+ btn.classList.add('tone-active');
243
+ selectedTone = btn.dataset.tone;
244
+ addCommentaryMessage('System', `Tone changed to ${selectedTone}`, 'system');
245
+ });
246
+ });
247
+
248
+ // Attitude Selection
249
+ attitudeBtns.forEach(btn => {
250
+ btn.addEventListener('click', () => {
251
+ attitudeBtns.forEach(b => b.classList.remove('tone-active'));
252
+ btn.classList.add('tone-active');
253
+ selectedAttitude = btn.dataset.attitude;
254
+ addCommentaryMessage('System', `Attitude changed to ${selectedAttitude}`, 'system');
255
+ });
256
+ });
257
+
258
+ // Webcam
259
+ startWebcamBtn.addEventListener('click', async () => {
260
+ try {
261
+ if (currentStream) {
262
+ currentStream.getTracks().forEach(track => track.stop());
263
+ }
264
+
265
+ const stream = await navigator.mediaDevices.getUserMedia({
266
+ video: { width: 1280, height: 720 },
267
+ audio: false
268
+ });
269
+
270
+ liveVideo.srcObject = stream;
271
+ currentStream = stream;
272
+
273
+ rtmpInput.classList.add('hidden');
274
+ ndiInput.classList.add('hidden');
275
+
276
+ updateStatus('Webcam connected', 'text-green-400');
277
+ addCommentaryMessage('System', 'Webcam feed connected successfully', 'system');
278
+
279
+ } catch (err) {
280
+ console.error('Error accessing webcam:', err);
281
+ updateStatus('Webcam error', 'text-red-400');
282
+ addCommentaryMessage('System', `Webcam error: ${err.message}`, 'error');
283
+ }
284
+ });
285
+
286
+ // NDI (Simulated)
287
+ connectNdiBtn.addEventListener('click', () => {
288
+ rtmpInput.classList.add('hidden');
289
+ ndiInput.classList.remove('hidden');
290
+
291
+ // Simulate finding NDI sources
292
+ ndiSources.innerHTML = `
293
+ <option value="">Select NDI Source</option>
294
+ <option value="ndi1">Camera 1 (NDI)</option>
295
+ <option value="ndi2">Camera 2 (NDI)</option>
296
+ <option value="ndi3">Virtual Camera (NDI)</option>
297
+ `;
298
+
299
+ updateStatus('Scanning for NDI sources...', 'text-yellow-400');
300
+ });
301
+
302
+ startNdiBtn.addEventListener('click', () => {
303
+ if (!ndiSources.value) {
304
+ updateStatus('Please select an NDI source', 'text-red-400');
305
+ return;
306
+ }
307
+
308
+ // In a real implementation, you would connect to the NDI source here
309
+ // For demo purposes, we'll just show a placeholder
310
+ liveVideo.srcObject = null;
311
+ liveVideo.poster = 'https://via.placeholder.com/1280x720/333333/666666?text=NDI+Source+Connected';
312
+
313
+ updateStatus(`NDI source connected: ${ndiSources.options[ndiSources.selectedIndex].text}`, 'text-green-400');
314
+ addCommentaryMessage('System', `NDI source connected: ${ndiSources.options[ndiSources.selectedIndex].text}`, 'system');
315
+ });
316
+
317
+ // RTMP
318
+ connectRtmpBtn.addEventListener('click', () => {
319
+ ndiInput.classList.add('hidden');
320
+ rtmpInput.classList.remove('hidden');
321
+ updateStatus('Enter RTMP URL', 'text-yellow-400');
322
+ });
323
+
324
+ startRtmpBtn.addEventListener('click', () => {
325
+ if (!rtmpUrl.value) {
326
+ updateStatus('Please enter an RTMP URL', 'text-red-400');
327
+ return;
328
+ }
329
+
330
+ // In a real implementation, you would connect to the RTMP stream here
331
+ // For demo purposes, we'll just show a placeholder
332
+ liveVideo.srcObject = null;
333
+ liveVideo.poster = 'https://via.placeholder.com/1280x720/333333/666666?text=RTMP+Stream+Connected';
334
+
335
+ updateStatus(`RTMP connected: ${rtmpUrl.value}`, 'text-green-400');
336
+ addCommentaryMessage('System', `RTMP stream connected: ${rtmpUrl.value}`, 'system');
337
+ });
338
+
339
+ // Capture Frame
340
+ captureFrameBtn.addEventListener('click', () => {
341
+ if (!liveVideo.srcObject && !liveVideo.poster) {
342
+ updateStatus('No video source to capture', 'text-red-400');
343
+ return;
344
+ }
345
+
346
+ const canvas = document.createElement('canvas');
347
+ canvas.width = liveVideo.videoWidth || 1280;
348
+ canvas.height = liveVideo.videoHeight || 720;
349
+ const ctx = canvas.getContext('2d');
350
+
351
+ if (liveVideo.srcObject) {
352
+ ctx.drawImage(liveVideo, 0, 0, canvas.width, canvas.height);
353
+ }
354
+
355
+ const frameUrl = canvas.toDataURL('image/jpeg', 0.8);
356
+ frameCount++;
357
+
358
+ capturedFrames.innerHTML += `
359
+ <div class="relative group">
360
+ <img src="${frameUrl}" alt="Captured frame ${frameCount}" class="w-full h-32 object-cover rounded-lg">
361
+ <div class="absolute inset-0 bg-black bg-opacity-50 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity rounded-lg">
362
+ <button class="analyze-frame bg-blue-600 hover:bg-blue-700 p-2 rounded-full" data-frame="${frameUrl}">
363
+ <i class="fas fa-search"></i>
364
+ </button>
365
+ </div>
366
+ <div class="text-xs text-gray-400 mt-1 truncate">Frame ${frameCount}</div>
367
+ </div>
368
+ `;
369
+
370
+ updateStatus(`Frame captured (${frameCount})`, 'text-blue-400');
371
+ addCommentaryMessage('System', `Frame #${frameCount} captured`, 'system');
372
+
373
+ // Add event listeners to new analyze buttons
374
+ document.querySelectorAll('.analyze-frame').forEach(btn => {
375
+ btn.addEventListener('click', () => {
376
+ analyzeFrame(btn.dataset.frame);
377
+ });
378
+ });
379
+ });
380
+
381
+ // Start/Stop Analysis
382
+ startAnalysisBtn.addEventListener('click', () => {
383
+ if (isAnalyzing) {
384
+ stopAnalysis();
385
+ } else {
386
+ startAnalysis();
387
+ }
388
+ });
389
+
390
+ function startAnalysis() {
391
+ if (!liveVideo.srcObject && !liveVideo.poster) {
392
+ updateStatus('No video source to analyze', 'text-red-400');
393
+ return;
394
+ }
395
+
396
+ isAnalyzing = true;
397
+ startAnalysisBtn.innerHTML = '<i class="fas fa-stop mr-2"></i> Stop Commentary';
398
+ startAnalysisBtn.classList.remove('bg-red-600', 'hover:bg-red-700');
399
+ startAnalysisBtn.classList.add('bg-gray-600', 'hover:bg-gray-700');
400
+
401
+ // Simulate analysis every 5 seconds
402
+ analysisInterval = setInterval(() => {
403
+ generateCommentary();
404
+ }, 5000);
405
+
406
+ updateStatus('Analyzing video feed...', 'text-yellow-400');
407
+ addCommentaryMessage('System', 'Starting real-time video analysis', 'system');
408
+ }
409
+
410
+ function stopAnalysis() {
411
+ isAnalyzing = false;
412
+ clearInterval(analysisInterval);
413
+ startAnalysisBtn.innerHTML = '<i class="fas fa-comment-dots mr-2"></i> Start Commentary';
414
+ startAnalysisBtn.classList.remove('bg-gray-600', 'hover:bg-gray-700');
415
+ startAnalysisBtn.classList.add('bg-red-600', 'hover:bg-red-700');
416
+
417
+ updateStatus('Analysis stopped', 'text-gray-400');
418
+ addCommentaryMessage('System', 'Real-time analysis stopped', 'system');
419
+ }
420
+
421
+ // Generate Commentary
422
+ generateCommentaryBtn.addEventListener('click', generateCommentary);
423
+
424
+ function generateCommentary() {
425
+ if (!liveVideo.srcObject && !liveVideo.poster) {
426
+ updateStatus('No video source to analyze', 'text-red-400');
427
+ return;
428
+ }
429
+
430
+ updateStatus('Generating commentary...', 'text-yellow-400');
431
+
432
+ // Simulate API call to generate commentary
433
+ setTimeout(() => {
434
+ const tones = {
435
+ professional: "This professional analysis suggests",
436
+ casual: "Hey, check this out -",
437
+ humorous: "OMG, you won't believe this -"
438
+ };
439
+
440
+ const attitudes = {
441
+ neutral: "the current scene appears to be",
442
+ positive: "the amazing scene shows",
443
+ critical: "there seems to be an issue with"
444
+ };
445
+
446
+ const topics = [
447
+ "optimal lighting conditions with balanced exposure",
448
+ "a slightly overexposed background that could use adjustment",
449
+ "excellent framing and composition",
450
+ "a distracting element in the left third of the frame",
451
+ "good color balance and skin tones",
452
+ "potential white balance issues",
453
+ "strong eye contact with the audience",
454
+ "a need for more dynamic movement",
455
+ "perfect audio levels",
456
+ "some background noise that could be reduced"
457
+ ];
458
+
459
+ const randomTopic = topics[Math.floor(Math.random() * topics.length)];
460
+ const commentary = `${tones[selectedTone]} ${attitudes[selectedAttitude]} ${randomTopic}.`;
461
+
462
+ addCommentaryMessage('AI Assistant', commentary, 'ai');
463
+ updateStatus('Commentary generated', 'text-green-400');
464
+ }, 1500);
465
+ }
466
+
467
+ // Analyze Frame
468
+ function analyzeFrame(frameUrl) {
469
+ updateStatus('Analyzing frame...', 'text-yellow-400');
470
+
471
+ // Simulate frame analysis
472
+ setTimeout(() => {
473
+ const analysisResults = [
474
+ "Frame analysis detected good contrast ratio",
475
+ "The composition follows the rule of thirds",
476
+ "Potential color cast detected in shadows",
477
+ "Sharpness appears slightly soft",
478
+ "No visible noise in the image",
479
+ "White balance appears accurate",
480
+ "Subject is well-lit with no harsh shadows",
481
+ "Background has pleasing bokeh effect"
482
+ ];
483
+
484
+ const randomResult = analysisResults[Math.floor(Math.random() * analysisResults.length)];
485
+ addCommentaryMessage('Frame Analysis', randomResult, 'analysis');
486
+ updateStatus('Frame analysis complete', 'text-green-400');
487
+ }, 2000);
488
+ }
489
+
490
+ // Clear Comments
491
+ clearCommentsBtn.addEventListener('click', () => {
492
+ commentaryFeed.innerHTML = `
493
+ <div class="commentary-bubble">
494
+ <div class="text-sm text-gray-400 mb-1">System</div>
495
+ <p>Commentary feed cleared. New analysis will appear here.</p>
496
+ </div>
497
+ `;
498
+ });
499
+
500
+ // Helper Functions
501
+ function updateStatus(message, colorClass) {
502
+ const icon = statusIndicator.querySelector('i');
503
+ icon.className = colorClass.includes('yellow') ? 'fas fa-circle-notch fa-spin mr-2' :
504
+ colorClass.includes('red') ? 'fas fa-exclamation-circle mr-2' :
505
+ colorClass.includes('green') ? 'fas fa-check-circle mr-2' :
506
+ 'fas fa-info-circle mr-2';
507
+
508
+ statusIndicator.querySelector('span').textContent = message;
509
+ statusIndicator.className = `flex items-center ${colorClass}`;
510
+ }
511
+
512
+ function addCommentaryMessage(sender, message, type) {
513
+ const typeClasses = {
514
+ 'system': 'bg-gray-700',
515
+ 'ai': 'bg-blue-900',
516
+ 'error': 'bg-red-900',
517
+ 'analysis': 'bg-purple-900'
518
+ };
519
+
520
+ const bubble = document.createElement('div');
521
+ bubble.className = `commentary-bubble ${typeClasses[type] || 'bg-gray-800'}`;
522
+ bubble.innerHTML = `
523
+ <div class="text-sm text-gray-300 mb-1">${sender}</div>
524
+ <p>${message}</p>
525
+ `;
526
+
527
+ commentaryFeed.appendChild(bubble);
528
+ commentaryFeed.scrollTop = commentaryFeed.scrollHeight;
529
+ }
530
+
531
+ // Initialize with professional tone selected
532
+ document.querySelector('[data-tone="professional"]').classList.add('tone-active');
533
+ document.querySelector('[data-attitude="neutral"]').classList.add('tone-active');
534
+ </script>
535
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=7Zeux/ai-commented-vision" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
536
+ </html>