Rubentnoda commited on
Commit
de177fa
·
verified ·
1 Parent(s): 1c9a0fb

Upload index.js with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.js +568 -56
index.js CHANGED
@@ -1,76 +1,588 @@
1
- import { pipeline } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.7.6';
 
 
 
 
 
 
 
 
 
 
2
 
3
- // Reference the elements that we will need
4
- const status = document.getElementById('status');
5
- const fileUpload = document.getElementById('upload');
6
- const imageContainer = document.getElementById('container');
7
- const example = document.getElementById('example');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
- const EXAMPLE_URL = 'https://huggingface.co/datasets/Xenova/transformers.js-docs/resolve/main/city-streets.jpg';
 
10
 
11
- // Create a new object detection pipeline
12
- status.textContent = 'Loading model...';
13
- const detector = await pipeline('object-detection', 'Xenova/detr-resnet-50');
14
- status.textContent = 'Ready';
 
15
 
16
- example.addEventListener('click', (e) => {
17
- e.preventDefault();
18
- detect(EXAMPLE_URL);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  });
 
20
 
21
- fileUpload.addEventListener('change', function (e) {
22
- const file = e.target.files[0];
23
- if (!file) {
24
- return;
25
- }
26
 
27
- const reader = new FileReader();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
- // Set up a callback when the file is loaded
30
- reader.onload = e2 => detect(e2.target.result);
 
31
 
32
- reader.readAsDataURL(file);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  });
 
34
 
 
 
 
 
 
35
 
36
- // Detect objects in the image
37
- async function detect(img) {
38
- imageContainer.innerHTML = '';
39
- imageContainer.style.backgroundImage = `url(${img})`;
40
 
41
- status.textContent = 'Analysing...';
42
- const output = await detector(img, {
43
- threshold: 0.5,
44
- percentage: true,
45
- });
46
- status.textContent = '';
47
- output.forEach(renderBox);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
- // Render a bounding box and label on the image
51
- function renderBox({ box, label }) {
52
- const { xmax, xmin, ymax, ymin } = box;
53
 
54
- // Generate a random color for the box
55
- const color = '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, 0);
 
 
56
 
57
- // Draw the box
58
- const boxElement = document.createElement('div');
59
- boxElement.className = 'bounding-box';
60
- Object.assign(boxElement.style, {
61
- borderColor: color,
62
- left: 100 * xmin + '%',
63
- top: 100 * ymin + '%',
64
- width: 100 * (xmax - xmin) + '%',
65
- height: 100 * (ymax - ymin) + '%',
66
- })
67
 
68
- // Draw label
69
- const labelElement = document.createElement('span');
70
- labelElement.textContent = label;
71
- labelElement.className = 'bounding-box-label';
72
- labelElement.style.backgroundColor = color;
73
 
74
- boxElement.appendChild(labelElement);
75
- imageContainer.appendChild(boxElement);
 
76
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Multimedia Control System with Transformers.js
2
+ class MultimediaController {
3
+ constructor() {
4
+ this.devices = [];
5
+ this.playlist = [];
6
+ this.currentVideo = null;
7
+ this.isPlaying = false;
8
+ this.progress = 0;
9
+ this.volume = 50;
10
+ this.aiModel = null;
11
+ this.modelLoaded = false;
12
 
13
+ this.initializeDevices();
14
+ this.initializePlaylist();
15
+ this.initializeAI();
16
+ this.startProgressUpdater();
17
+ }
18
+
19
+ initializeDevices() {
20
+ // Initialize simulated TV devices
21
+ const deviceConfigs = [
22
+ { name: "TV - Sala Principal", location: "Sala", type: "4K Smart TV" },
23
+ { name: "TV - Barra", location: "Bar", type: "HD TV" },
24
+ { name: "TV - Terraza", location: "Terraza", type: "Outdoor TV" }
25
+ ];
26
+
27
+ this.devices = deviceConfigs.map((config, index) => ({
28
+ id: index + 1,
29
+ ...config,
30
+ status: 'connected',
31
+ isPlaying: false,
32
+ currentContent: 'Esperando...',
33
+ volume: 50,
34
+ isMuted: false
35
+ }));
36
+
37
+ this.renderDevices();
38
+ this.renderIndividualControls();
39
+ }
40
+
41
+ initializePlaylist() {
42
+ this.playlist = [
43
+ {
44
+ title: "Video Promocional - Restaurante",
45
+ source: "YouTube",
46
+ url: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
47
+ duration: "3:45",
48
+ thumbnail: "https://picsum.photos/seed/promo/60/40"
49
+ },
50
+ {
51
+ title: "Video Corporativo 2024",
52
+ source: "Vimeo",
53
+ url: "https://vimeo.com/123456789",
54
+ duration: "2:30",
55
+ thumbnail: "https://picsum.photos/seed/corp/60/40"
56
+ },
57
+ {
58
+ title: "Video Evento Especial",
59
+ source: "Directo",
60
+ url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
61
+ duration: "5:15",
62
+ thumbnail: "https://picsum.photos/seed/event/60/40"
63
+ }
64
+ ];
65
+
66
+ this.renderPlaylist();
67
+ }
68
+
69
+ async initializeAI() {
70
+ try {
71
+ // Initialize a text classification model for content analysis
72
+ this.updateLoadingStatus('Cargando modelo de clasificación de texto...');
73
+ this.updateLoadingProgress(30);
74
+
75
+ // Using a lightweight model for text classification
76
+ this.aiModel = await pipeline('text-classification', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english');
77
+
78
+ this.updateLoadingProgress(70);
79
+ this.updateLoadingStatus('Modelo cargado exitosamente...');
80
 
81
+ this.modelLoaded = true;
82
+ this.updateLoadingProgress(100);
83
 
84
+ setTimeout(() => {
85
+ document.getElementById('loadingScreen').classList.add('hidden');
86
+ this.updateAIModelStatus('Listo');
87
+ this.showStatus('✅ Sistema AI inicializado', 'success');
88
+ }, 1000);
89
 
90
+ } catch (error) {
91
+ console.error('Error loading AI model:', error);
92
+ this.updateLoadingStatus('Error al cargar el modelo AI');
93
+ setTimeout(() => {
94
+ document.getElementById('loadingScreen').classList.add('hidden');
95
+ this.updateAIModelStatus('Error');
96
+ this.showStatus('⚠️ AI no disponible (modo offline)', 'warning');
97
+ }, 2000);
98
+ }
99
+ }
100
+
101
+ updateLoadingProgress(percent) {
102
+ document.getElementById('loadingProgress').style.width = percent + '%';
103
+ }
104
+
105
+ updateLoadingStatus(status) {
106
+ document.getElementById('loadingStatus').textContent = status;
107
+ }
108
+
109
+ updateAIModelStatus(status) {
110
+ const element = document.getElementById('aiModelStatus');
111
+ element.textContent = status;
112
+ element.className = status === 'Listo' ? 'font-semibold text-green-600' : 'font-semibold text-red-600';
113
+ }
114
+
115
+ renderDevices() {
116
+ const container = document.getElementById('devicesContainer');
117
+ container.innerHTML = '';
118
+
119
+ this.devices.forEach(device => {
120
+ const statusColor = {
121
+ 'connected': 'bg-green-100 text-green-800',
122
+ 'playing': 'bg-blue-100 text-blue-800',
123
+ 'paused': 'bg-yellow-100 text-yellow-800',
124
+ 'offline': 'bg-red-100 text-red-800'
125
+ }[device.status] || 'bg-gray-100 text-gray-800';
126
+
127
+ const statusIcon = {
128
+ 'connected': 'fa-check-circle',
129
+ 'playing': 'fa-play-circle',
130
+ 'paused': 'fa-pause-circle',
131
+ 'offline': 'fa-times-circle'
132
+ }[device.status] || 'fa-question-circle';
133
+
134
+ const deviceCard = `
135
+ <div class="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
136
+ <div class="flex items-center justify-between mb-4">
137
+ <h4 class="font-bold text-lg flex items-center gap-2">
138
+ <i class="fas fa-tv text-purple-600"></i>
139
+ ${device.name}
140
+ </h4>
141
+ <span class="px-3 py-1 rounded-full text-sm ${statusColor}">
142
+ <i class="fas ${statusIcon} mr-1"></i>
143
+ ${device.status}
144
+ </span>
145
+ </div>
146
+ <div class="space-y-2 text-sm">
147
+ <div class="flex justify-between">
148
+ <span class="text-gray-600">Ubicación:</span>
149
+ <span class="font-medium">${device.location}</span>
150
+ </div>
151
+ <div class="flex justify-between">
152
+ <span class="text-gray-600">Tipo:</span>
153
+ <span class="font-medium">${device.type}</span>
154
+ </div>
155
+ <div class="flex justify-between">
156
+ <span class="text-gray-600">Contenido:</span>
157
+ <span class="font-medium truncate ml-2">${device.currentContent}</span>
158
+ </div>
159
+ <div class="flex justify-between">
160
+ <span class="text-gray-600">Volumen:</span>
161
+ <span class="font-medium">${device.volume}% ${device.isMuted ? '🔇' : '🔊'}</span>
162
+ </div>
163
+ </div>
164
+ </div>
165
+ `;
166
+ container.innerHTML += deviceCard;
167
  });
168
+ }
169
 
170
+ renderIndividualControls() {
171
+ const container = document.getElementById('individualControls');
172
+ container.innerHTML = '';
 
 
173
 
174
+ this.devices.forEach(device => {
175
+ const controls = `
176
+ <div class="border border-gray-200 rounded-lg p-4">
177
+ <h4 class="font-semibold mb-3">${device.name}</h4>
178
+ <div class="grid grid-cols-3 gap-2">
179
+ <button onclick="controlDevice(${device.id}, 'play')"
180
+ class="bg-green-600 hover:bg-green-700 text-white p-2 rounded text-sm transition-colors">
181
+ <i class="fas fa-play"></i>
182
+ </button>
183
+ <button onclick="controlDevice(${device.id}, 'pause')"
184
+ class="bg-yellow-600 hover:bg-yellow-700 text-white p-2 rounded text-sm transition-colors">
185
+ <i class="fas fa-pause"></i>
186
+ </button>
187
+ <button onclick="controlDevice(${device.id}, 'stop')"
188
+ class="bg-red-600 hover:bg-red-700 text-white p-2 rounded text-sm transition-colors">
189
+ <i class="fas fa-stop"></i>
190
+ </button>
191
+ <button onclick="controlDevice(${device.id}, 'mute')"
192
+ class="bg-gray-600 hover:bg-gray-700 text-white p-2 rounded text-sm transition-colors">
193
+ <i class="fas fa-volume-mute"></i>
194
+ </button>
195
+ <button onclick="controlDevice(${device.id}, 'volumeUp')"
196
+ class="bg-blue-600 hover:bg-blue-700 text-white p-2 rounded text-sm transition-colors">
197
+ <i class="fas fa-volume-up"></i>
198
+ </button>
199
+ <button onclick="controlDevice(${device.id}, 'volumeDown')"
200
+ class="bg-blue-600 hover:bg-blue-700 text-white p-2 rounded text-sm transition-colors">
201
+ <i class="fas fa-volume-down"></i>
202
+ </button>
203
+ </div>
204
+ </div>
205
+ `;
206
+ container.innerHTML += controls;
207
+ });
208
+ }
209
 
210
+ renderPlaylist() {
211
+ const container = document.getElementById('playlistContainer');
212
+ container.innerHTML = '';
213
 
214
+ this.playlist.forEach((video, index) => {
215
+ const item = `
216
+ <div class="flex items-center gap-3 p-3 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors cursor-pointer"
217
+ onclick="playFromPlaylist(${index})">
218
+ <img src="${video.thumbnail}" alt="${video.title}" class="w-12 h-8 rounded object-cover">
219
+ <div class="flex-1">
220
+ <h5 class="font-semibold text-sm">${video.title}</h5>
221
+ <p class="text-xs text-gray-600">${video.source} • ${video.duration}</p>
222
+ </div>
223
+ <button class="bg-purple-600 hover:bg-purple-700 text-white p-2 rounded text-sm transition-colors">
224
+ <i class="fas fa-play"></i>
225
+ </button>
226
+ </div>
227
+ `;
228
+ container.innerHTML += item;
229
  });
230
+ }
231
 
232
+ async loadVideo(url, title = '') {
233
+ if (!url) {
234
+ this.showStatus('❌ Por favor, introduce una URL válida', 'error');
235
+ return;
236
+ }
237
 
238
+ this.currentVideo = {
239
+ url: url,
240
+ title: title || this.extractVideoTitle(url)
241
+ };
242
 
243
+ // Update all devices
244
+ this.devices.forEach(device => {
245
+ device.currentContent = this.currentVideo.title;
246
+ device.status = 'connected';
247
+ });
248
+
249
+ this.renderDevices();
250
+ this.showStatus(`✅ Video cargado: ${this.currentVideo.title}`, 'success');
251
+ document.getElementById('videoUrl').value = url;
252
+ }
253
+
254
+ extractVideoTitle(url) {
255
+ if (url.includes('youtube.com')) return 'Video de YouTube';
256
+ if (url.includes('vimeo.com')) return 'Video de Vimeo';
257
+ if (url.includes('.mp4')) return 'Video Directo MP4';
258
+ return 'Video Online';
259
+ }
260
+
261
+ playAll() {
262
+ if (!this.currentVideo) {
263
+ this.showStatus('❌ No hay video cargado', 'error');
264
+ return;
265
+ }
266
+
267
+ this.isPlaying = true;
268
+ this.devices.forEach(device => {
269
+ device.status = 'playing';
270
+ device.isPlaying = true;
271
+ });
272
+
273
+ this.renderDevices();
274
+ this.showStatus('▶️ Reproduciendo en todos los dispositivos', 'success');
275
+ this.updateSystemStatus('Reproduciendo');
276
+ }
277
+
278
+ pauseAll() {
279
+ this.isPlaying = false;
280
+ this.devices.forEach(device => {
281
+ device.status = 'connected';
282
+ device.isPlaying = false;
283
+ });
284
+
285
+ this.renderDevices();
286
+ this.showStatus('⏸️ Pausado en todos los dispositivos', 'info');
287
+ this.updateSystemStatus('Pausado');
288
+ }
289
+
290
+ stopAll() {
291
+ this.isPlaying = false;
292
+ this.progress = 0;
293
+ this.devices.forEach(device => {
294
+ device.status = 'connected';
295
+ device.isPlaying = false;
296
+ device.currentContent = 'Esperando...';
297
+ });
298
+
299
+ this.renderDevices();
300
+ this.updateProgress();
301
+ this.showStatus('⏹️ Detenido en todos los dispositivos', 'info');
302
+ this.updateSystemStatus('Detenido');
303
+ }
304
+
305
+ controlDevice(deviceId, action) {
306
+ const device = this.devices.find(d => d.id === deviceId);
307
+ if (!device) return;
308
+
309
+ switch (action) {
310
+ case 'play':
311
+ if (!this.currentVideo) {
312
+ this.showStatus('❌ No hay video cargado', 'error');
313
+ return;
314
+ }
315
+ device.status = 'playing';
316
+ device.isPlaying = true;
317
+ this.showStatus(`▶️ Reproduciendo en ${device.name}`, 'success');
318
+ break;
319
+ case 'pause':
320
+ device.status = 'connected';
321
+ device.isPlaying = false;
322
+ this.showStatus(`⏸️ Pausado en ${device.name}`, 'info');
323
+ break;
324
+ case 'stop':
325
+ device.status = 'connected';
326
+ device.isPlaying = false;
327
+ device.currentContent = 'Esperando...';
328
+ this.showStatus(`⏹️ Detenido en ${device.name}`, 'info');
329
+ break;
330
+ case 'mute':
331
+ device.isMuted = !device.isMuted;
332
+ this.showStatus(`🔇 ${device.isMuted ? 'Silenciado' : 'Activado'} en ${device.name}`, 'info');
333
+ break;
334
+ case 'volumeUp':
335
+ device.volume = Math.min(100, device.volume + 10);
336
+ this.showStatus(`🔊 Volumen ${device.volume}% en ${device.name}`, 'info');
337
+ break;
338
+ case 'volumeDown':
339
+ device.volume = Math.max(0, device.volume - 10);
340
+ this.showStatus(`🔉 Volumen ${device.volume}% en ${device.name}`, 'info');
341
+ break;
342
+ }
343
+
344
+ this.renderDevices();
345
+ }
346
+
347
+ changeVolume(volume) {
348
+ this.volume = parseInt(volume);
349
+ this.devices.forEach(device => {
350
+ device.volume = this.volume;
351
+ });
352
+
353
+ document.getElementById('volumePercentage').textContent = volume + '%';
354
+ this.renderDevices();
355
+ this.showStatus(`🔊 Volumen ajustado a ${volume}%`, 'info');
356
+ }
357
+
358
+ muteAll() {
359
+ const allMuted = this.devices.every(d => d.isMuted);
360
+ this.devices.forEach(device => {
361
+ device.isMuted = !allMuted;
362
+ });
363
+
364
+ this.renderDevices();
365
+ this.showStatus(`🔇 ${allMuted ? 'Activado' : 'Silenciado'} en todos los dispositivos`, 'info');
366
+ }
367
+
368
+ setVolume(volume) {
369
+ document.getElementById('volumeSlider').value = volume;
370
+ this.changeVolume(volume);
371
+ }
372
+
373
+ async analyzeContent() {
374
+ if (!this.modelLoaded) {
375
+ this.showStatus('❌ Modelo AI no disponible', 'error');
376
+ return;
377
+ }
378
+
379
+ if (!this.currentVideo) {
380
+ this.showStatus('❌ No hay video para analizar', 'error');
381
+ return;
382
  }
383
 
384
+ this.showStatus('🧠 Analizando contenido con AI...', 'info');
 
 
385
 
386
+ try {
387
+ // Simulate content analysis
388
+ const analysisText = `El video "${this.currentVideo.title}" es un contenido de alta calidad`;
389
+ const result = await this.aiModel(analysisText);
390
 
391
+ const analysisHTML = `
392
+ <div class="space-y-2">
393
+ <p><strong>Título:</strong> ${this.currentVideo.title}</p>
394
+ <p><strong>URL:</strong> ${this.currentVideo.url}</p>
395
+ <p><strong>Sentimiento:</strong> ${result[0].label} (${(result[0].score * 100).toFixed(1)}%)</p>
396
+ <p><strong>Calidad:</strong> Alta definición detectada</p>
397
+ <p><strong>Categoría:</strong> ${this.detectCategory()}</p>
398
+ <p><strong>Duración recomendada:</strong> ${this.recommendDuration()}</p>
399
+ </div>
400
+ `;
401
 
402
+ document.getElementById('analysisResults').innerHTML = analysisHTML;
403
+ document.getElementById('aiAnalysis').classList.remove('hidden');
404
+ this.showStatus('✅ Análisis completado exitosamente', 'success');
 
 
405
 
406
+ } catch (error) {
407
+ console.error('Analysis error:', error);
408
+ this.showStatus('❌ Error en el análisis AI', 'error');
409
  }
410
+ }
411
+
412
+ detectCategory() {
413
+ if (!this.currentVideo) return 'Desconocida';
414
+ const title = this.currentVideo.title.toLowerCase();
415
+ if (title.includes('promocional')) return 'Marketing/Publicidad';
416
+ if (title.includes('corporativo')) return 'Corporativo';
417
+ if (title.includes('evento')) return 'Eventos';
418
+ return 'General';
419
+ }
420
+
421
+ recommendDuration() {
422
+ if (!this.currentVideo) return 'N/A';
423
+ const category = this.detectCategory();
424
+ const recommendations = {
425
+ 'Marketing/Publicidad': '30-60 segundos',
426
+ 'Corporativo': '2-5 minutos',
427
+ 'Eventos': '3-10 minutos',
428
+ 'General': '1-5 minutos'
429
+ };
430
+ return recommendations[category] || '1-5 minutos';
431
+ }
432
+
433
+ addToPlaylist(url, title = '') {
434
+ if (!url) {
435
+ this.showStatus('❌ Introduce una URL válida', 'error');
436
+ return;
437
+ }
438
+
439
+ const video = {
440
+ title: title || this.extractVideoTitle(url),
441
+ source: this.extractSource(url),
442
+ url: url,
443
+ duration: '--:--',
444
+ thumbnail: `https://picsum.photos/seed/${Date.now()}/60/40`
445
+ };
446
+
447
+ this.playlist.push(video);
448
+ this.renderPlaylist();
449
+ this.showStatus(`✅ "${video.title}" agregado a la playlist`, 'success');
450
+
451
+ // Clear inputs
452
+ document.getElementById('playlistUrl').value = '';
453
+ document.getElementById('playlistTitle').value = '';
454
+ }
455
+
456
+ extractSource(url) {
457
+ if (url.includes('youtube.com')) return 'YouTube';
458
+ if (url.includes('vimeo.com')) return 'Vimeo';
459
+ if (url.includes('.mp4')) return 'Directo';
460
+ return 'Online';
461
+ }
462
+
463
+ playFromPlaylist(index) {
464
+ if (index >= 0 && index < this.playlist.length) { const video=this.playlist[index]; this.loadVideo(video.url,
465
+ video.title); this.playAll(); } } loadSampleVideo(type) { const samples={
466
+ youtube: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ' , vimeo: 'https://vimeo.com/123456789' ,
467
+ direct: 'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' }; const
468
+ url=samples[type]; this.loadVideo(url); } discoverDevices() { this.showStatus('🔍 Buscando dispositivos...', 'info' );
469
+ // Simulate device discovery setTimeout(()=> {
470
+ this.renderDevices();
471
+ this.showStatus(`✅ ${this.devices.length} dispositivos encontrados`, 'success');
472
+ }, 1500);
473
+ }
474
+
475
+ refreshStatus() {
476
+ this.renderDevices();
477
+ this.showStatus('🔄 Estado actualizado', 'info');
478
+ }
479
+
480
+ rewind() {
481
+ this.progress = Math.max(0, this.progress - 10);
482
+ this.updateProgress();
483
+ this.showStatus('⏪ Retrocediendo 10 segundos', 'info');
484
+ }
485
+
486
+ forward() {
487
+ this.progress = Math.min(100, this.progress + 10);
488
+ this.updateProgress();
489
+ this.showStatus('⏩ Adelantando 10 segundos', 'info');
490
+ }
491
+
492
+ updateProgress() {
493
+ const progressBar = document.getElementById('progressBar');
494
+ const currentTime = document.getElementById('currentTime');
495
+
496
+ progressBar.style.width = this.progress + '%';
497
+
498
+ const totalSeconds = 225; // 3:45 in seconds
499
+ const currentSeconds = Math.floor((this.progress / 100) * totalSeconds);
500
+ const minutes = Math.floor(currentSeconds / 60);
501
+ const seconds = currentSeconds % 60;
502
+ currentTime.textContent = `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
503
+ }
504
+
505
+ startProgressUpdater() {
506
+ setInterval(() => {
507
+ if (this.isPlaying && this.progress < 100) { this.progress +=0.5; this.updateProgress(); } }, 1000); }
508
+ showStatus(message, type='info' ) { const statusElement=document.getElementById('statusMessage');
509
+ statusElement.textContent=message; // Update styling based on type
510
+ statusElement.className='mt-3 p-3 rounded-lg text-sm ' ; switch (type) { case 'success' : statusElement.className
511
+ +='bg-green-100 text-green-700' ; break; case 'error' : statusElement.className +='bg-red-100 text-red-700' ; break;
512
+ case 'warning' : statusElement.className +='bg-yellow-100 text-yellow-700' ; break; default: statusElement.className
513
+ +='bg-gray-100 text-gray-700' ; } } updateSystemStatus(status) { const
514
+ element=document.getElementById('systemStatus'); element.textContent=status; element.className='font-semibold ' ;
515
+ switch (status) { case 'Reproduciendo' : element.className +='text-blue-600' ; break; case 'Pausado' :
516
+ element.className +='text-yellow-600' ; break; case 'Detenido' : element.className +='text-gray-600' ; break;
517
+ default: element.className +='text-green-600' ; } } } // Global controller instance let controller; // Initialize
518
+ when DOM is loaded document.addEventListener('DOMContentLoaded', ()=> {
519
+ controller = new MultimediaController();
520
+ });
521
+
522
+ // Global functions for button onclick handlers
523
+ function loadVideo() {
524
+ const url = document.getElementById('videoUrl').value;
525
+ controller.loadVideo(url);
526
+ }
527
+
528
+ function loadSampleVideo(type) {
529
+ controller.loadSampleVideo(type);
530
+ }
531
+
532
+ function playAll() {
533
+ controller.playAll();
534
+ }
535
+
536
+ function pauseAll() {
537
+ controller.pauseAll();
538
+ }
539
+
540
+ function stopAll() {
541
+ controller.stopAll();
542
+ }
543
+
544
+ function rewind() {
545
+ controller.rewind();
546
+ }
547
+
548
+ function forward() {
549
+ controller.forward();
550
+ }
551
+
552
+ function changeVolume(value) {
553
+ controller.changeVolume(value);
554
+ }
555
+
556
+ function muteAll() {
557
+ controller.muteAll();
558
+ }
559
+
560
+ function setVolume(value) {
561
+ controller.setVolume(value);
562
+ }
563
+
564
+ function analyzeContent() {
565
+ controller.analyzeContent();
566
+ }
567
+
568
+ function addToPlaylist() {
569
+ const url = document.getElementById('playlistUrl').value;
570
+ const title = document.getElementById('playlistTitle').value;
571
+ controller.addToPlaylist(url, title);
572
+ }
573
+
574
+ function playFromPlaylist(index) {
575
+ controller.playFromPlaylist(index);
576
+ }
577
+
578
+ function controlDevice(deviceId, action) {
579
+ controller.controlDevice(deviceId, action);
580
+ }
581
+
582
+ function discoverDevices() {
583
+ controller.discoverDevices();
584
+ }
585
+
586
+ function refreshStatus() {
587
+ controller.refreshStatus();
588
+ }