privateuserh commited on
Commit
47208dd
·
verified ·
1 Parent(s): b27cb15

Update scripts/app.js

Browse files
Files changed (1) hide show
  1. scripts/app.js +1019 -985
scripts/app.js CHANGED
@@ -1,986 +1,1020 @@
1
- <script>
2
- // Configuration for Cloudflare Workers AI
3
- const AI_CONFIG = {
4
- accountId: 'oFD0IMs0aV8eKMMMdTEF2zRQmtzvKMH43LX5ZWUJ',
5
- gatewayId: 'streamai_gateway',
6
- apiToken: 'masked_for_security', // In a real app, this would be handled server-side
7
- model: '@cf/meta/llama-2-7b-chat-int8'
8
- };
9
-
10
-
11
- // Sample streaming data with SMPlus VHX added to the top
12
- const streamingData = [
13
- {
14
- title: "SMPlus Exclusive Series",
15
- type: "TV Series",
16
- genre: "Drama, Action",
17
- platform: "SMPlus VHX",
18
- rating: "4.9",
19
- year: "2023",
20
- description: "An exclusive action-packed drama series only available on SMPlus VHX.",
21
- broadcastTime: "2023-12-15T20:00:00"
22
- },
23
- {
24
- title: "The Grand Adventure",
25
- type: "Movie",
26
- genre: "Adventure, Comedy",
27
- platform: "Netflix",
28
- rating: "4.8",
29
- year: "2022",
30
- description: "A hilarious journey across continents with unexpected twists.",
31
- broadcastTime: "2023-12-10T19:30:00"
32
- },
33
- {
34
- title: "Dark Secrets",
35
- type: "TV Series",
36
- genre: "Drama, Thriller",
37
- platform: "HBO Max",
38
- rating: "4.7",
39
- year: "2021",
40
- description: "A small town's dark past resurfaces with shocking revelations.",
41
- broadcastTime: "2023-12-12T21:00:00"
42
- },
43
- {
44
- title: "Space Explorers",
45
- type: "Documentary",
46
- genre: "Science, Space",
47
- platform: "Disney+",
48
- rating: "4.9",
49
- year: "2023",
50
- description: "The latest discoveries from the frontiers of space exploration.",
51
- broadcastTime: "2023-12-14T18:00:00"
52
- },
53
- {
54
- title: "Romantic Getaway",
55
- type: "Movie",
56
- genre: "Romance, Comedy",
57
- platform: "Amazon Prime",
58
- rating: "4.5",
59
- year: "2021",
60
- description: "Two strangers find love during an unexpected vacation.",
61
- broadcastTime: "2023-12-16T20:30:00"
62
- },
63
- {
64
- title: "Tech Today",
65
- type: "News Show",
66
- genre: "Technology, News",
67
- platform: "SMPlus VHX",
68
- rating: "4.6",
69
- year: "2023",
70
- description: "Daily tech news and gadget reviews from around the world.",
71
- broadcastTime: "2023-12-17T09:00:00"
72
- },
73
- {
74
- title: "Cooking Masters",
75
- type: "Reality Show",
76
- genre: "Food, Competition",
77
- platform: "Netflix",
78
- rating: "4.7",
79
- year: "2023",
80
- description: "Top chefs compete in intense culinary challenges.",
81
- broadcastTime: "2023-12-18T20:00:00"
82
- },
83
- {
84
- title: "History Unearthed",
85
- type: "Documentary",
86
- genre: "History, Education",
87
- platform: "HBO Max",
88
- rating: "4.8",
89
- year: "2023",
90
- description: "Fascinating historical discoveries and their modern implications.",
91
- broadcastTime: "2023-12-19T21:00:00"
92
- }
93
- ];
94
- // Sample RSS feed data
95
- const rssFeedData = {
96
- all: [
97
- {
98
- title: "New Episode: SMPlus Exclusive Series",
99
- source: "SMPlus VHX",
100
- time: "2 hours ago",
101
- excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.",
102
- category: "personalized"
103
- },
104
- {
105
- title: "Trending: The Grand Adventure hits #1",
106
- source: "Netflix",
107
- time: "5 hours ago",
108
- excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.",
109
- category: "trending"
110
- },
111
- {
112
- title: "Breaking: New streaming partnership announced",
113
- source: "Streaming News",
114
- time: "1 day ago",
115
- excerpt: "Major platforms announce new content sharing agreement starting next month.",
116
- category: "news"
117
- },
118
- {
119
- title: "Recommended for you: Space Explorers",
120
- source: "Disney+",
121
- time: "1 day ago",
122
- excerpt: "Based on your interest in science documentaries, we recommend this new series.",
123
- category: "personalized"
124
- },
125
- {
126
- title: "Upcoming: Romantic Getaway special event",
127
- source: "Amazon Prime",
128
- time: "2 days ago",
129
- excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.",
130
- category: "trending"
131
- }
132
- ],
133
- news: [
134
- {
135
- title: "Breaking: New streaming partnership announced",
136
- source: "Streaming News",
137
- time: "1 day ago",
138
- excerpt: "Major platforms announce new content sharing agreement starting next month.",
139
- category: "news"
140
- },
141
- {
142
- title: "Streaming industry report Q4 2023",
143
- source: "Tech Insights",
144
- time: "3 days ago",
145
- excerpt: "Latest statistics show continued growth in streaming subscriptions worldwide.",
146
- category: "news"
147
- }
148
- ],
149
- trending: [
150
- {
151
- title: "Trending: The Grand Adventure hits #1",
152
- source: "Netflix",
153
- time: "5 hours ago",
154
- excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.",
155
- category: "trending"
156
- },
157
- {
158
- title: "Upcoming: Romantic Getaway special event",
159
- source: "Amazon Prime",
160
- time: "2 days ago",
161
- excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.",
162
- category: "trending"
163
- }
164
- ],
165
- personalized: [
166
- {
167
- title: "New Episode: SMPlus Exclusive Series",
168
- source: "SMPlus VHX",
169
- time: "2 hours ago",
170
- excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.",
171
- category: "personalized"
172
- },
173
- {
174
- title: "Recommended for you: Space Explorers",
175
- source: "Disney+",
176
- time: "1 day ago",
177
- excerpt: "Based on your interest in science documentaries, we recommend this new series.",
178
- category: "personalized"
179
- }
180
- ]
181
- };
182
- // Video production variables
183
- let isRecording = false;
184
- let recordingInterval;
185
- let recordedClips = [];
186
- let mediaRecorder;
187
- let audioContext;
188
- let audioStream;
189
- let videoStream;
190
- let currentClipTime = 0;
191
- let currentClipInterval;
192
-
193
-
194
- // Bluetooth sharing variables
195
- let isBluetoothConnected = false;
196
- let bluetoothDevice;
197
- let bluetoothServer;
198
- let bluetoothService;
199
- let bluetoothCharacteristic;
200
- let sharedClips = [];
201
- let groupMembers = [];
202
- let username = "User" + Math.floor(Math.random() * 1000);
203
- let currentCategory = "general";
204
-
205
-
206
- // Initialize chat
207
- document.addEventListener('DOMContentLoaded', function() {
208
- const sendBtn = document.getElementById('send-btn');
209
- const userInput = document.getElementById('user-input');
210
- const chatMessages = document.getElementById('chat-messages');
211
- const productionButton = document.getElementById('production-button');
212
- const productionPanel = document.getElementById('production-panel');
213
- const startRecordingBtn = document.getElementById('start-recording');
214
- const stopRecordingBtn = document.getElementById('stop-recording');
215
- const generateVideoBtn = document.getElementById('generate-video');
216
- const clearClipsBtn = document.getElementById('clear-clips');
217
- const rssFilterBtns = document.querySelectorAll('.rss-filter-btn');
218
- const recordTab = document.getElementById('record-tab');
219
- const editTab = document.getElementById('edit-tab');
220
- const shareTab = document.getElementById('share-tab');
221
- const rankTab = document.getElementById('rank-tab');
222
- const recordSection = document.getElementById('record-section');
223
- const editSection = document.getElementById('edit-section');
224
- const shareSection = document.getElementById('share-section');
225
- const rankSection = document.getElementById('rank-section');
226
- const bluetoothConnectBtn = document.getElementById('bluetooth-connect');
227
- const shareClipsBtn = document.getElementById('share-clips');
228
- const usernameInput = document.getElementById('username');
229
- const contentCategorySelect = document.getElementById('content-category');
230
- const rankingAlgorithmSelect = document.getElementById('ranking-algorithm');
231
-
232
- // Set default username
233
- usernameInput.value = username;
234
-
235
- // Load sample recommendations
236
- loadRecommendations();
237
-
238
- // Load RSS feed
239
- loadRSSFeed('all');
240
-
241
- // Send message on button click
242
- sendBtn.addEventListener('click', sendMessage);
243
-
244
- // Send message on Enter key
245
- userInput.addEventListener('keypress', function(e) {
246
- if (e.key === 'Enter') {
247
- sendMessage();
248
- }
249
- });
250
- // Toggle production panel
251
- productionButton.addEventListener('click', function() {
252
- productionPanel.classList.toggle('open');
253
- });
254
-
255
- // Start recording
256
- startRecordingBtn.addEventListener('click', startRecording);
257
-
258
- // Stop recording
259
- stopRecordingBtn.addEventListener('click', stopRecording);
260
-
261
- // Generate video
262
- generateVideoBtn.addEventListener('click', generateShortVideo);
263
-
264
- // Clear clips
265
- clearClipsBtn.addEventListener('click', clearClips);
266
-
267
- // Filter RSS feed
268
- rssFilterBtns.forEach(btn => {
269
- btn.addEventListener('click', function() {
270
- // Update active button
271
- rssFilterBtns.forEach(b => {
272
- b.classList.remove('bg-indigo-100', 'text-indigo-700');
273
- b.classList.add('bg-gray-100', 'text-gray-700');
274
- });
275
- this.classList.remove('bg-gray-100', 'text-gray-700');
276
- this.classList.add('bg-indigo-100', 'text-indigo-700');
277
-
278
- // Load filtered feed
279
- loadRSSFeed(this.dataset.filter);
280
- });
281
- });
282
-
283
- // Switch between record and edit tabs
284
- recordTab.addEventListener('click', function() {
285
- recordTab.classList.remove('inactive');
286
- recordTab.classList.add('active');
287
- editTab.classList.remove('active');
288
- editTab.classList.add('inactive');
289
- shareTab.classList.remove('active');
290
- shareTab.classList.add('inactive');
291
- rankTab.classList.remove('active');
292
- rankTab.classList.add('inactive');
293
- recordSection.classList.remove('hidden');
294
- editSection.classList.add('hidden');
295
- shareSection.classList.add('hidden');
296
- rankSection.classList.add('hidden');
297
- });
298
-
299
- editTab.addEventListener('click', function() {
300
- if (recordedClips.length === 0) {
301
- showNotification("No Clips", "Record some clips first to edit them");
302
- return;
303
- }
304
-
305
- editTab.classList.remove('inactive');
306
- editTab.classList.add('active');
307
- recordTab.classList.remove('active');
308
- recordTab.classList.add('inactive');
309
- shareTab.classList.remove('active');
310
- shareTab.classList.add('inactive');
311
- rankTab.classList.remove('active');
312
- rankTab.classList.add('inactive');
313
- recordSection.classList.add('hidden');
314
- editSection.classList.remove('hidden');
315
- shareSection.classList.add('hidden');
316
- rankSection.classList.add('hidden');
317
- });
318
- shareTab.addEventListener('click', function() {
319
- shareTab.classList.remove('inactive');
320
- shareTab.classList.add('active');
321
- recordTab.classList.remove('active');
322
- recordTab.classList.add('inactive');
323
- editTab.classList.remove('active');
324
- editTab.classList.add('inactive');
325
- rankTab.classList.remove('active');
326
- rankTab.classList.add('inactive');
327
- recordSection.classList.add('hidden');
328
- editSection.classList.add('hidden');
329
- shareSection.classList.remove('hidden');
330
- rankSection.classList.add('hidden');
331
- });
332
-
333
- rankTab.addEventListener('click', function() {
334
- if (sharedClips.length === 0) {
335
- showNotification("No Shared Clips", "Share some clips first to see rankings");
336
- return;
337
- }
338
-
339
- rankTab.classList.remove('inactive');
340
- rankTab.classList.add('active');
341
- recordTab.classList.remove('active');
342
- recordTab.classList.add('inactive');
343
- editTab.classList.remove('active');
344
- editTab.classList.add('inactive');
345
- shareTab.classList.remove('active');
346
- shareTab.classList.add('inactive');
347
- recordSection.classList.add('hidden');
348
- editSection.classList.add('hidden');
349
- shareSection.classList.add('hidden');
350
- rankSection.classList.remove('hidden');
351
-
352
- // Update rankings when tab is opened
353
- updateRankings();
354
- });
355
-
356
- // Connect to Bluetooth
357
- bluetoothConnectBtn.addEventListener('click', connectBluetooth);
358
-
359
- // Share clips
360
- shareClipsBtn.addEventListener('click', shareClipsWithGroup);
361
-
362
- // Update username when changed
363
- usernameInput.addEventListener('change', function() {
364
- username = this.value || "User" + Math.floor(Math.random() * 1000);
365
- });
366
-
367
- // Update category when changed
368
- contentCategorySelect.addEventListener('change', function() {
369
- currentCategory = this.value;
370
- });
371
-
372
- // Update rankings when algorithm changes
373
- rankingAlgorithmSelect.addEventListener('change', updateRankings);
374
- });
375
-
376
-
377
- // Show notification
378
- function showNotification(title, message) {
379
- const notification = document.getElementById('notification');
380
- const titleElement = document.getElementById('notification-title');
381
- const messageElement = document.getElementById('notification-message');
382
-
383
- titleElement.textContent = title;
384
- messageElement.textContent = message;
385
-
386
- notification.classList.remove('hidden');
387
- notification.classList.add('show');
388
-
389
- setTimeout(() => {
390
- notification.classList.remove('show');
391
- setTimeout(() => notification.classList.add('hidden'), 300);
392
- }, 3000);
393
- }
394
- // Quick prompt buttons
395
- function quickPrompt(prompt) {
396
- document.getElementById('user-input').value = prompt;
397
- sendMessage();
398
- }
399
-
400
-
401
- // Send message to AI
402
- async function sendMessage() {
403
- const userInput = document.getElementById('user-input');
404
- const chatMessages = document.getElementById('chat-messages');
405
-
406
- if (userInput.value.trim() === '') return;
407
-
408
- // Add user message to chat
409
- const userMessage = document.createElement('div');
410
- userMessage.className = 'chat-bubble user-bubble p-4 w-3/4 ml-auto mb-4 fade-in';
411
- userMessage.innerHTML = `<p>${userInput.value}</p>`;
412
- chatMessages.appendChild(userMessage);
413
-
414
- // Show typing indicator
415
- const typingIndicator = document.createElement('div');
416
- typingIndicator.className = 'chat-bubble ai-bubble p-4 w-1/2 mb-4';
417
- typingIndicator.innerHTML = '<div class="typing-indicator"><span></span><span></span><span></span></div>';
418
- chatMessages.appendChild(typingIndicator);
419
-
420
- // Scroll to bottom
421
- chatMessages.scrollTop = chatMessages.scrollHeight;
422
-
423
- // Save user message
424
- const userMessageText = userInput.value;
425
- userInput.value = '';
426
-
427
- try {
428
- // Call Cloudflare Workers AI
429
- const aiResponse = await queryCloudflareAI(userMessageText);
430
-
431
- // Remove typing indicator
432
- chatMessages.removeChild(typingIndicator);
433
-
434
- // Add AI response to chat
435
- const aiMessage = document.createElement('div');
436
- aiMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in';
437
- aiMessage.innerHTML = `<p>${aiResponse}</p>`;
438
- chatMessages.appendChild(aiMessage);
439
-
440
- // Update recommendations based on AI response
441
- updateRecommendationsFromAI(aiResponse);
442
-
443
- } catch (error) {
444
- // Remove typing indicator
445
- chatMessages.removeChild(typingIndicator);
446
-
447
- // Show error message
448
- const errorMessage = document.createElement('div');
449
- errorMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in';
450
- errorMessage.innerHTML = `<p class="text-red-500">Sorry, I'm having trouble connecting to the AI service. Please try again later.</p>`;
451
- chatMessages.appendChild(errorMessage);
452
- }
453
-
454
- // Scroll to bottom
455
- chatMessages.scrollTop = chatMessages.scrollHeight;
456
- }
457
- // Query Cloudflare Workers AI
458
- async function queryCloudflareAI(prompt) {
459
- // In a production environment, this would be handled by a backend service
460
- // to keep the API token secure. For this demo, we'll simulate the response.
461
-
462
- console.log(`[DEBUG] Would call Cloudflare AI with prompt: "${prompt}"`);
463
-
464
- // Simulate API call delay
465
- await new Promise(resolve => setTimeout(resolve, 1500));
466
-
467
- // Simulate different responses based on prompt
468
- const lowerPrompt = prompt.toLowerCase();
469
-
470
- if (lowerPrompt.includes('comedy') || lowerPrompt.includes('funny')) {
471
- return "I'd recommend these comedy options that should give you a good laugh:\n\n1. 'The Grand Adventure' (Netflix) - A hilarious journey with unexpected twists\n2. 'Office Shenanigans' (Hulu) - Workplace comedy at its finest\n\nComedy can really lift your mood! Would you like more suggestions?";
472
- }
473
- else if (lowerPrompt.includes('thriller') || lowerPrompt.includes('suspense')) {
474
- return "For thrilling content that will keep you on the edge of your seat, consider:\n\n1. 'Dark Secrets' (HBO Max) - A town's dark past resurfaces\n2. 'Midnight Caller' (Amazon Prime) - A psychological thriller about a mysterious phone call\n\nThese should provide plenty of suspense!";
475
- }
476
- else if (lowerPrompt.includes('romance') || lowerPrompt.includes('love')) {
477
- return "Romantic stories can be so heartwarming! Here are my top picks:\n\n1. 'Romantic Getaway' (Amazon Prime) - Two strangers find love on vacation\n2. 'Love in Paris' (Netflix) - A classic romantic tale set in the City of Love\n\nLet me know if you'd like something more specific!";
478
- }
479
- else if (lowerPrompt.includes('recommend') || lowerPrompt.includes('suggest')) {
480
- return "Based on your request, I'd recommend these excellent streaming options:\n\n1. 'SMPlus Exclusive Series' (SMPlus VHX) - Action-packed drama series\n2. 'Space Explorers' (Disney+) - Fascinating documentary about space\n\nI've updated the recommendations section with more options for you!";
481
- }
482
- else {
483
- return "I'm here to help you find the perfect streaming content! Could you tell me more about what you're looking for? For example, you could say 'recommend a sci-fi movie' or 'what should I watch if I feel like laughing?'";
484
- }
485
- }
486
-
487
-
488
- // Update recommendations based on AI response
489
- function updateRecommendationsFromAI(aiResponse) {
490
- let filter = 'all';
491
-
492
- if (aiResponse.includes('comedy')) {
493
- filter = 'comedy';
494
- } else if (aiResponse.includes('thriller') || aiResponse.includes('suspense')) {
495
- filter = 'thriller';
496
- } else if (aiResponse.includes('romance') || aiResponse.includes('love')) {
497
- filter = 'romance';
498
- }
499
-
500
- loadRecommendations(filter);
501
- }
502
-
503
-
504
- // Save show and set broadcast reminder
505
- function saveShow(title, broadcastTime) {
506
- // In a real app, this would save to a database
507
- console.log(`Saved show: ${title}`);
508
-
509
- // Show notification
510
- showNotification("Reminder Set!", `We'll notify you when "${title}" is about to broadcast.`);
511
-
512
- // In a real app, you would schedule a notification for the broadcast time
513
- if (broadcastTime) {
514
- const broadcastDate = new Date(broadcastTime);
515
- const now = new Date();
516
-
517
- // Only schedule if broadcast is in the future
518
- if (broadcastDate > now) {
519
- const timeUntilBroadcast = broadcastDate - now;
520
-
521
- // Schedule notification 30 minutes before broadcast
522
- setTimeout(() => {
523
- showNotification("Starting Soon!", `"${title}" will begin broadcasting in 30 minutes!`);
524
- }, timeUntilBroadcast - (30 * 60 * 1000));
525
- }
526
- }
527
- }
528
- // Load recommendations
529
- function loadRecommendations(filter = 'all') {
530
- const container = document.getElementById('recommendations-container');
531
- container.innerHTML = '';
532
-
533
- let filteredData = streamingData;
534
-
535
- if (filter === 'comedy') {
536
- filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('comedy'));
537
- } else if (filter === 'thriller') {
538
- filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('thriller'));
539
- } else if (filter === 'romance') {
540
- filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('romance'));
541
- }
542
-
543
- filteredData.forEach(item => {
544
- const card = document.createElement('div');
545
- card.className = 'stream-card bg-white rounded-lg overflow-hidden shadow-md hover:shadow-xl transition duration-300 fade-in p-4';
546
- card.innerHTML = `
547
- <div class="mb-3">
548
- <div class="flex justify-between items-start">
549
- <h3 class="font-bold text-base">${item.title}</h3>
550
- <span class="bg-yellow-100 text-yellow-800 text-xs px-2 py-1 rounded-full flex items-center">
551
- <i class="fas fa-star text-yellow-500 mr-1 text-xs"></i> ${item.rating}
552
- </span>
553
- </div>
554
- <p class="text-gray-600 text-xs mb-1">${item.type} • ${item.genre} • ${item.year}</p>
555
- <div class="text-xs text-indigo-600 mb-2">${item.platform}</div>
556
- </div>
557
- <p class="text-gray-700 text-sm mb-4">${item.description}</p>
558
- <div class="flex justify-between items-center">
559
- <button class="text-indigo-600 hover:text-indigo-800 text-xs font-medium" onclick="saveShow('${item.title}', '${item.broadcastTime}')">
560
- <i class="far fa-bookmark mr-1"></i> Save
561
- </button>
562
- <a href="${item.platform === 'SMPlus VHX' ? 'https://smplus.vhx.tv' : '#'}" target="_blank" class="bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-1 rounded-full text-xs font-medium transition">
563
- <i class="fas fa-play mr-1"></i> Watch
564
- </a>
565
- </div>
566
- `;
567
- container.appendChild(card);
568
- });
569
- }
570
-
571
-
572
- // Load RSS feed
573
- function loadRSSFeed(filter) {
574
- const container = document.getElementById('rss-feed');
575
- container.innerHTML = '';
576
-
577
- const feedItems = rssFeedData[filter] || rssFeedData.all;
578
-
579
- feedItems.forEach(item => {
580
- const feedItem = document.createElement('div');
581
- feedItem.className = 'rss-item bg-gray-50 p-3 rounded-lg';
582
- feedItem.innerHTML = `
583
- <h4 class="font-medium text-sm mb-1">${item.title}</h4>
584
- <div class="flex items-center text-xs text-gray-500 mb-2">
585
- <span>${item.source}</span>
586
- <span class="mx-2">•</span>
587
- <span>${item.time}</span>
588
- </div>
589
- <p class="text-xs text-gray-700">${item.excerpt}</p>
590
- `;
591
- container.appendChild(feedItem);
592
- });
593
- }
594
- // Start recording video and audio
595
- async function startRecording() {
596
- try {
597
- // Get user media
598
- videoStream = await navigator.mediaDevices.getDisplayMedia({
599
- video: true,
600
- audio: true
601
- });
602
-
603
- audioStream = await navigator.mediaDevices.getUserMedia({
604
- audio: true
605
- });
606
-
607
- // Create audio context
608
- audioContext = new AudioContext();
609
- const source = audioContext.createMediaStreamSource(audioStream);
610
- const destination = audioContext.createMediaStreamDestination();
611
- source.connect(destination);
612
-
613
- // Combine video and audio streams
614
- const combinedStream = new MediaStream([
615
- ...videoStream.getVideoTracks(),
616
- ...destination.stream.getAudioTracks()
617
- ]);
618
-
619
- // Create media recorder
620
- mediaRecorder = new MediaRecorder(combinedStream, {
621
- mimeType: 'video/webm'
622
- });
623
-
624
- // Update preview with live recording
625
- const preview = document.getElementById('clip-preview');
626
- preview.innerHTML = '<video autoplay muted></video>';
627
- const previewVideo = preview.querySelector('video');
628
- previewVideo.srcObject = combinedStream;
629
-
630
- // Set recording state
631
- isRecording = true;
632
- document.getElementById('recording-indicator').classList.remove('hidden');
633
- document.getElementById('start-recording').classList.add('hidden');
634
- document.getElementById('stop-recording').classList.remove('hidden');
635
-
636
- // Start progress bar animation
637
- currentClipTime = 0;
638
- const progressFill = document.getElementById('progress-fill');
639
- progressFill.style.width = '0%';
640
-
641
- currentClipInterval = setInterval(() => {
642
- currentClipTime += 100;
643
- const progressPercent = (currentClipTime / 5000) * 100;
644
- progressFill.style.width = `${progressPercent}%`;
645
- }, 100);
646
-
647
- // Start recording in 5-second clips
648
- let clipCount = 0;
649
- recordingInterval = setInterval(() => {
650
- if (clipCount > 0) {
651
- // Stop current recording
652
- mediaRecorder.stop();
653
- }
654
-
655
- // Start new recording
656
- mediaRecorder.start();
657
- clipCount++;
658
-
659
- // Reset progress bar
660
- currentClipTime = 0;
661
- progressFill.style.width = '0%';
662
-
663
- // Store clip data when available
664
- mediaRecorder.ondataavailable = (e) => {
665
- const clip = {
666
- blob: e.data,
667
- timestamp: new Date().toLocaleTimeString(),
668
- url: URL.createObjectURL(e.data),
669
- username: username,
670
- category: currentCategory,
671
- engagementScore: Math.random() * 5, // Simulated metrics
672
- qualityScore: 3 + Math.random() * 2,
673
- consistencyScore: 3 + Math.random() * 2
674
- };
675
- recordedClips.push(clip);
676
- updateClipsList();
677
- };
678
- }, 5000);
679
-
680
- showNotification("Recording Started", "Recording 5-second clips of your screen and audio");
681
-
682
- } catch (error) {
683
- console.error("Error starting recording:", error);
684
- showNotification("Recording Error", "Could not start recording. Please check permissions.");
685
- stopRecording();
686
- }
687
- }
688
- // Stop recording
689
- function stopRecording() {
690
- if (mediaRecorder && mediaRecorder.state !== 'inactive') {
691
- mediaRecorder.stop();
692
- }
693
-
694
- clearInterval(recordingInterval);
695
- clearInterval(currentClipInterval);
696
- isRecording = false;
697
-
698
- // Stop all tracks
699
- if (videoStream) {
700
- videoStream.getTracks().forEach(track => track.stop());
701
- }
702
- if (audioStream) {
703
- audioStream.getTracks().forEach(track => track.stop());
704
- }
705
-
706
- // Close audio context
707
- if (audioContext && audioContext.state !== 'closed') {
708
- audioContext.close();
709
- }
710
-
711
- // Reset preview
712
- const preview = document.getElementById('clip-preview');
713
- preview.innerHTML = `
714
- <div class="clip-preview-placeholder">
715
- <i class="fas fa-video"></i>
716
- <p>Preview will appear here</p>
717
- </div>
718
- `;
719
-
720
- // Reset progress bar
721
- document.getElementById('progress-fill').style.width = '0%';
722
-
723
- // Update UI
724
- document.getElementById('recording-indicator').classList.add('hidden');
725
- document.getElementById('start-recording').classList.remove('hidden');
726
- document.getElementById('stop-recording').classList.add('hidden');
727
-
728
- showNotification("Recording Stopped", `Captured ${recordedClips.length} clips ready for production`);
729
- }
730
-
731
-
732
- // Update clips list
733
- function updateClipsList() {
734
- const container = document.getElementById('clips-container');
735
- container.innerHTML = '';
736
-
737
- if (recordedClips.length === 0) {
738
- container.innerHTML = '<p class="text-gray-500 text-sm">Clips will appear here...</p>';
739
- return;
740
- }
741
-
742
- recordedClips.forEach((clip, index) => {
743
- const clipItem = document.createElement('div');
744
- clipItem.className = 'clip-item';
745
- clipItem.innerHTML = `
746
- <i class="fas fa-video text-gray-500 mr-2"></i>
747
- <span class="text-sm flex-1">Clip ${index + 1}</span>
748
- <span class="text-xs text-gray-500">${clip.timestamp}</span>
749
- `;
750
-
751
- // Add click handler to preview clip
752
- clipItem.addEventListener('click', () => {
753
- const preview = document.getElementById('clip-preview');
754
- preview.innerHTML = '<video controls></video>';
755
- const previewVideo = preview.querySelector('video');
756
- previewVideo.src = clip.url;
757
- });
758
-
759
- container.appendChild(clipItem);
760
- });
761
- }
762
- // Clear all clips
763
- function clearClips() {
764
- if (recordedClips.length === 0) return;
765
-
766
- // In a real app, we would properly revoke the object URLs
767
- recordedClips.forEach(clip => {
768
- if (clip.url) {
769
- URL.revokeObjectURL(clip.url);
770
- }
771
- });
772
-
773
- recordedClips = [];
774
- updateClipsList();
775
-
776
- // Reset preview
777
- const preview = document.getElementById('clip-preview');
778
- preview.innerHTML = `
779
- <div class="clip-preview-placeholder">
780
- <i class="fas fa-video"></i>
781
- <p>Preview will appear here</p>
782
- </div>
783
- `;
784
-
785
- showNotification("Clips Cleared", "All recorded clips have been removed");
786
- }
787
-
788
-
789
- // Generate short video from clips
790
- function generateShortVideo() {
791
- if (recordedClips.length === 0) {
792
- showNotification("No Clips", "Please record some clips first");
793
- return;
794
- }
795
-
796
- // In a real app, this would use a video editing library to combine clips
797
- // and sync with audio. For this demo, we'll simulate the process.
798
-
799
- showNotification("Video Generation", "Processing your clips into a short video...");
800
-
801
- setTimeout(() => {
802
- // Simulate processing time
803
- const videoUrl = URL.createObjectURL(new Blob(["Simulated video content"], { type: 'video/mp4' }));
804
-
805
- // Create download link
806
- const a = document.createElement('a');
807
- a.href = videoUrl;
808
- a.download = 'streamai-short.mp4';
809
- a.click();
810
-
811
- showNotification("Video Ready", "Your short video has been generated and downloaded");
812
- }, 3000);
813
- }
814
-
815
-
816
- // Connect to Bluetooth devices
817
- async function connectBluetooth() {
818
- try {
819
- // Request Bluetooth device
820
- bluetoothDevice = await navigator.bluetooth.requestDevice({
821
- acceptAllDevices: true,
822
- optionalServices: ['generic_access']
823
- });
824
-
825
- // Connect to the GATT Server
826
- bluetoothServer = await bluetoothDevice.gatt.connect();
827
-
828
- // Get the service
829
- bluetoothService = await bluetoothServer.getPrimaryService('generic_access');
830
-
831
- // Get the characteristic
832
- bluetoothCharacteristic = await bluetoothService.getCharacteristic('device_name');
833
-
834
- // Update connection status
835
- isBluetoothConnected = true;
836
- const statusElement = document.getElementById('connection-status');
837
- statusElement.innerHTML = `
838
- <i class="fas fa-circle bluetooth-connected mr-1"></i>
839
- <span>Connected to ${bluetoothDevice.name || 'device'}</span>
840
- `;
841
-
842
- // Simulate discovering group members
843
- groupMembers = [
844
- { name: "User" + Math.floor(Math.random() * 1000), device: "Device 1" },
845
- { name: "User" + Math.floor(Math.random() * 1000), device: "Device 2" },
846
- { name: "User" + Math.floor(Math.random() * 1000), device: "Device 3" }
847
- ];
848
- showNotification("Bluetooth Connected", `Connected to ${bluetoothDevice.name}. Found ${groupMembers.length} group members.`);
849
-
850
- } catch (error) {
851
- console.error("Bluetooth connection error:", error);
852
- isBluetoothConnected = false;
853
- document.getElementById('connection-status').innerHTML = `
854
- <i class="fas fa-circle bluetooth-disconnected mr-1"></i>
855
- <span>Not connected</span>
856
- `;
857
- showNotification("Connection Failed", "Could not connect to Bluetooth device");
858
- }
859
- }
860
-
861
-
862
- // Share clips with Bluetooth group
863
- async function shareClipsWithGroup() {
864
- if (!isBluetoothConnected) {
865
- showNotification("Not Connected", "Please connect to Bluetooth first");
866
- return;
867
- }
868
-
869
- if (recordedClips.length === 0) {
870
- showNotification("No Clips", "Please record some clips first");
871
- return;
872
- }
873
-
874
- // In a real app, this would actually send the clips over Bluetooth
875
- // For this demo, we'll simulate the sharing process
876
-
877
- showNotification("Sharing Clips", "Sharing your clips with the group...");
878
-
879
- // Simulate sharing delay
880
- setTimeout(() => {
881
- // Add metadata to clips and add to shared clips
882
- recordedClips.forEach(clip => {
883
- const sharedClip = {
884
- ...clip,
885
- sharedBy: username,
886
- category: currentCategory,
887
- timestamp: new Date().toLocaleTimeString(),
888
- likes: Math.floor(Math.random() * 10),
889
- views: Math.floor(Math.random() * 50)
890
- };
891
- sharedClips.push(sharedClip);
892
- });
893
-
894
- // Update shared clips display
895
- updateSharedClipsList();
896
-
897
- showNotification("Clips Shared", `Shared ${recordedClips.length} clips with ${groupMembers.length} group members`);
898
- }, 2000);
899
- }
900
- // Update shared clips list
901
- function updateSharedClipsList() {
902
- const container = document.getElementById('shared-clips-container');
903
- container.innerHTML = '';
904
-
905
- if (sharedClips.length === 0) {
906
- container.innerHTML = '<p class="text-gray-500 text-sm">Shared clips will appear here...</p>';
907
- return;
908
- }
909
-
910
- sharedClips.forEach((clip, index) => {
911
- const clipItem = document.createElement('div');
912
- clipItem.className = 'clip-item';
913
- clipItem.innerHTML = `
914
- <i class="fas fa-video text-gray-500 mr-2"></i>
915
- <span class="text-sm flex-1">Clip ${index + 1}</span>
916
- <span class="user-tag">${clip.sharedBy}</span>
917
- <span class="category-tag">${clip.category}</span>
918
- `;
919
-
920
- // Add click handler to preview clip
921
- clipItem.addEventListener('click', () => {
922
- const preview = document.getElementById('clip-preview');
923
- preview.innerHTML = '<video controls></video>';
924
- const previewVideo = preview.querySelector('video');
925
- previewVideo.src = clip.url;
926
- });
927
-
928
- container.appendChild(clipItem);
929
- });
930
- }
931
-
932
-
933
- // Benchmarking algorithm to rank clips
934
- function benchmarkClips() {
935
- if (sharedClips.length === 0) return [];
936
-
937
- // Calculate scores for each clip
938
- sharedClips.forEach(clip => {
939
- // Composite score is weighted average of all metrics
940
- clip.compositeScore = (
941
- clip.engagementScore * 0.4 +
942
- clip.qualityScore * 0.3 +
943
- clip.consistencyScore * 0.3
944
- );
945
-
946
- // Add some randomness to simulate different algorithms
947
- clip.engagementScore = Math.min(5, clip.engagementScore + Math.random() * 0.5 - 0.25);
948
- clip.qualityScore = Math.min(5, clip.qualityScore + Math.random() * 0.5 - 0.25);
949
- clip.consistencyScore = Math.min(5, clip.consistencyScore + Math.random() * 0.5 - 0.25);
950
- });
951
-
952
- // Sort based on selected algorithm
953
- const algorithm = document.getElementById('ranking-algorithm').value;
954
- let sortedClips = [...sharedClips];
955
-
956
- switch (algorithm) {
957
- case 'engagement':
958
- sortedClips.sort((a, b) => b.engagementScore - a.engagementScore);
959
- break;
960
- case 'quality':
961
- sortedClips.sort((a, b) => b.qualityScore - a.qualityScore);
962
- break;
963
- case 'consistency':
964
- sortedClips.sort((a, b) => b.consistencyScore - a.consistencyScore);
965
- break;
966
- case 'composite':
967
- default:
968
- sortedClips.sort((a, b) => b.compositeScore - a.compositeScore);
969
- break;
970
- }
971
- return sortedClips;
972
- }
973
-
974
-
975
- // Update rankings display
976
- function updateRankings() {
977
- const container = document.getElementById('rankings-container');
978
- container.innerHTML = '';
979
-
980
- const rankedClips = benchmarkClips();
981
-
982
- if (rankedClips.length === 0) {
983
- container.innerHTML = '<p class="text-gray-500 text-sm">Rankings will appear here when available...</p>';
984
- return;
985
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
986
  </script>
 
1
+ <script>
2
+ // scripts/app.js
3
+
4
+ import { initUI } from './ui.js';
5
+ import { initChat } from './chat.js';
6
+ import { initVideo } from './video.js';
7
+
8
+ document.addEventListener('DOMContentLoaded', () => {
9
+ // To handle dependencies between modules without a complex event system,
10
+ // we can expose some functions globally. This is a pragmatic approach for this stage.
11
+ window.showNotification = (title, message) => {
12
+ // A simplified version for video.js to call
13
+ const notification = document.getElementById('notification');
14
+ document.getElementById('notification-title').textContent = title;
15
+ document.getElementById('notification-message').textContent = message;
16
+ notification.classList.remove('hidden');
17
+ notification.classList.add('show');
18
+ setTimeout(() => notification.classList.remove('show'), 3000);
19
+ };
20
+
21
+ window.loadRecommendations = (filter) => {
22
+ // A simplified version for chat.js to call
23
+ // In a real app, ui.js would export loadRecommendations for chat.js to import.
24
+ const container = document.getElementById('recommendations-container');
25
+ // ... Add logic to load recommendations based on filter
26
+ };
27
+
28
+
29
+ // Initialize all modules
30
+ initUI();
31
+ initChat();
32
+ initVideo();
33
+
34
+ console.log("StreamAI application initialized.");
35
+ });
36
+ // Configuration for Cloudflare Workers AI
37
+ const AI_CONFIG = {
38
+ accountId: 'oFD0IMs0aV8eKMMMdTEF2zRQmtzvKMH43LX5ZWUJ',
39
+ gatewayId: 'streamai_gateway',
40
+ apiToken: 'masked_for_security', // In a real app, this would be handled server-side
41
+ model: '@cf/meta/llama-2-7b-chat-int8'
42
+ };
43
+
44
+
45
+ // Sample streaming data with SMPlus VHX added to the top
46
+ const streamingData = [
47
+ {
48
+ title: "SMPlus Exclusive Series",
49
+ type: "TV Series",
50
+ genre: "Drama, Action",
51
+ platform: "SMPlus VHX",
52
+ rating: "4.9",
53
+ year: "2023",
54
+ description: "An exclusive action-packed drama series only available on SMPlus VHX.",
55
+ broadcastTime: "2023-12-15T20:00:00"
56
+ },
57
+ {
58
+ title: "The Grand Adventure",
59
+ type: "Movie",
60
+ genre: "Adventure, Comedy",
61
+ platform: "Netflix",
62
+ rating: "4.8",
63
+ year: "2022",
64
+ description: "A hilarious journey across continents with unexpected twists.",
65
+ broadcastTime: "2023-12-10T19:30:00"
66
+ },
67
+ {
68
+ title: "Dark Secrets",
69
+ type: "TV Series",
70
+ genre: "Drama, Thriller",
71
+ platform: "HBO Max",
72
+ rating: "4.7",
73
+ year: "2021",
74
+ description: "A small town's dark past resurfaces with shocking revelations.",
75
+ broadcastTime: "2023-12-12T21:00:00"
76
+ },
77
+ {
78
+ title: "Space Explorers",
79
+ type: "Documentary",
80
+ genre: "Science, Space",
81
+ platform: "Disney+",
82
+ rating: "4.9",
83
+ year: "2023",
84
+ description: "The latest discoveries from the frontiers of space exploration.",
85
+ broadcastTime: "2023-12-14T18:00:00"
86
+ },
87
+ {
88
+ title: "Romantic Getaway",
89
+ type: "Movie",
90
+ genre: "Romance, Comedy",
91
+ platform: "Amazon Prime",
92
+ rating: "4.5",
93
+ year: "2021",
94
+ description: "Two strangers find love during an unexpected vacation.",
95
+ broadcastTime: "2023-12-16T20:30:00"
96
+ },
97
+ {
98
+ title: "Tech Today",
99
+ type: "News Show",
100
+ genre: "Technology, News",
101
+ platform: "SMPlus VHX",
102
+ rating: "4.6",
103
+ year: "2023",
104
+ description: "Daily tech news and gadget reviews from around the world.",
105
+ broadcastTime: "2023-12-17T09:00:00"
106
+ },
107
+ {
108
+ title: "Cooking Masters",
109
+ type: "Reality Show",
110
+ genre: "Food, Competition",
111
+ platform: "Netflix",
112
+ rating: "4.7",
113
+ year: "2023",
114
+ description: "Top chefs compete in intense culinary challenges.",
115
+ broadcastTime: "2023-12-18T20:00:00"
116
+ },
117
+ {
118
+ title: "History Unearthed",
119
+ type: "Documentary",
120
+ genre: "History, Education",
121
+ platform: "HBO Max",
122
+ rating: "4.8",
123
+ year: "2023",
124
+ description: "Fascinating historical discoveries and their modern implications.",
125
+ broadcastTime: "2023-12-19T21:00:00"
126
+ }
127
+ ];
128
+ // Sample RSS feed data
129
+ const rssFeedData = {
130
+ all: [
131
+ {
132
+ title: "New Episode: SMPlus Exclusive Series",
133
+ source: "SMPlus VHX",
134
+ time: "2 hours ago",
135
+ excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.",
136
+ category: "personalized"
137
+ },
138
+ {
139
+ title: "Trending: The Grand Adventure hits #1",
140
+ source: "Netflix",
141
+ time: "5 hours ago",
142
+ excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.",
143
+ category: "trending"
144
+ },
145
+ {
146
+ title: "Breaking: New streaming partnership announced",
147
+ source: "Streaming News",
148
+ time: "1 day ago",
149
+ excerpt: "Major platforms announce new content sharing agreement starting next month.",
150
+ category: "news"
151
+ },
152
+ {
153
+ title: "Recommended for you: Space Explorers",
154
+ source: "Disney+",
155
+ time: "1 day ago",
156
+ excerpt: "Based on your interest in science documentaries, we recommend this new series.",
157
+ category: "personalized"
158
+ },
159
+ {
160
+ title: "Upcoming: Romantic Getaway special event",
161
+ source: "Amazon Prime",
162
+ time: "2 days ago",
163
+ excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.",
164
+ category: "trending"
165
+ }
166
+ ],
167
+ news: [
168
+ {
169
+ title: "Breaking: New streaming partnership announced",
170
+ source: "Streaming News",
171
+ time: "1 day ago",
172
+ excerpt: "Major platforms announce new content sharing agreement starting next month.",
173
+ category: "news"
174
+ },
175
+ {
176
+ title: "Streaming industry report Q4 2023",
177
+ source: "Tech Insights",
178
+ time: "3 days ago",
179
+ excerpt: "Latest statistics show continued growth in streaming subscriptions worldwide.",
180
+ category: "news"
181
+ }
182
+ ],
183
+ trending: [
184
+ {
185
+ title: "Trending: The Grand Adventure hits #1",
186
+ source: "Netflix",
187
+ time: "5 hours ago",
188
+ excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.",
189
+ category: "trending"
190
+ },
191
+ {
192
+ title: "Upcoming: Romantic Getaway special event",
193
+ source: "Amazon Prime",
194
+ time: "2 days ago",
195
+ excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.",
196
+ category: "trending"
197
+ }
198
+ ],
199
+ personalized: [
200
+ {
201
+ title: "New Episode: SMPlus Exclusive Series",
202
+ source: "SMPlus VHX",
203
+ time: "2 hours ago",
204
+ excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.",
205
+ category: "personalized"
206
+ },
207
+ {
208
+ title: "Recommended for you: Space Explorers",
209
+ source: "Disney+",
210
+ time: "1 day ago",
211
+ excerpt: "Based on your interest in science documentaries, we recommend this new series.",
212
+ category: "personalized"
213
+ }
214
+ ]
215
+ };
216
+ // Video production variables
217
+ let isRecording = false;
218
+ let recordingInterval;
219
+ let recordedClips = [];
220
+ let mediaRecorder;
221
+ let audioContext;
222
+ let audioStream;
223
+ let videoStream;
224
+ let currentClipTime = 0;
225
+ let currentClipInterval;
226
+
227
+
228
+ // Bluetooth sharing variables
229
+ let isBluetoothConnected = false;
230
+ let bluetoothDevice;
231
+ let bluetoothServer;
232
+ let bluetoothService;
233
+ let bluetoothCharacteristic;
234
+ let sharedClips = [];
235
+ let groupMembers = [];
236
+ let username = "User" + Math.floor(Math.random() * 1000);
237
+ let currentCategory = "general";
238
+
239
+
240
+ // Initialize chat
241
+ document.addEventListener('DOMContentLoaded', function() {
242
+ const sendBtn = document.getElementById('send-btn');
243
+ const userInput = document.getElementById('user-input');
244
+ const chatMessages = document.getElementById('chat-messages');
245
+ const productionButton = document.getElementById('production-button');
246
+ const productionPanel = document.getElementById('production-panel');
247
+ const startRecordingBtn = document.getElementById('start-recording');
248
+ const stopRecordingBtn = document.getElementById('stop-recording');
249
+ const generateVideoBtn = document.getElementById('generate-video');
250
+ const clearClipsBtn = document.getElementById('clear-clips');
251
+ const rssFilterBtns = document.querySelectorAll('.rss-filter-btn');
252
+ const recordTab = document.getElementById('record-tab');
253
+ const editTab = document.getElementById('edit-tab');
254
+ const shareTab = document.getElementById('share-tab');
255
+ const rankTab = document.getElementById('rank-tab');
256
+ const recordSection = document.getElementById('record-section');
257
+ const editSection = document.getElementById('edit-section');
258
+ const shareSection = document.getElementById('share-section');
259
+ const rankSection = document.getElementById('rank-section');
260
+ const bluetoothConnectBtn = document.getElementById('bluetooth-connect');
261
+ const shareClipsBtn = document.getElementById('share-clips');
262
+ const usernameInput = document.getElementById('username');
263
+ const contentCategorySelect = document.getElementById('content-category');
264
+ const rankingAlgorithmSelect = document.getElementById('ranking-algorithm');
265
+
266
+ // Set default username
267
+ usernameInput.value = username;
268
+
269
+ // Load sample recommendations
270
+ loadRecommendations();
271
+
272
+ // Load RSS feed
273
+ loadRSSFeed('all');
274
+
275
+ // Send message on button click
276
+ sendBtn.addEventListener('click', sendMessage);
277
+
278
+ // Send message on Enter key
279
+ userInput.addEventListener('keypress', function(e) {
280
+ if (e.key === 'Enter') {
281
+ sendMessage();
282
+ }
283
+ });
284
+ // Toggle production panel
285
+ productionButton.addEventListener('click', function() {
286
+ productionPanel.classList.toggle('open');
287
+ });
288
+
289
+ // Start recording
290
+ startRecordingBtn.addEventListener('click', startRecording);
291
+
292
+ // Stop recording
293
+ stopRecordingBtn.addEventListener('click', stopRecording);
294
+
295
+ // Generate video
296
+ generateVideoBtn.addEventListener('click', generateShortVideo);
297
+
298
+ // Clear clips
299
+ clearClipsBtn.addEventListener('click', clearClips);
300
+
301
+ // Filter RSS feed
302
+ rssFilterBtns.forEach(btn => {
303
+ btn.addEventListener('click', function() {
304
+ // Update active button
305
+ rssFilterBtns.forEach(b => {
306
+ b.classList.remove('bg-indigo-100', 'text-indigo-700');
307
+ b.classList.add('bg-gray-100', 'text-gray-700');
308
+ });
309
+ this.classList.remove('bg-gray-100', 'text-gray-700');
310
+ this.classList.add('bg-indigo-100', 'text-indigo-700');
311
+
312
+ // Load filtered feed
313
+ loadRSSFeed(this.dataset.filter);
314
+ });
315
+ });
316
+
317
+ // Switch between record and edit tabs
318
+ recordTab.addEventListener('click', function() {
319
+ recordTab.classList.remove('inactive');
320
+ recordTab.classList.add('active');
321
+ editTab.classList.remove('active');
322
+ editTab.classList.add('inactive');
323
+ shareTab.classList.remove('active');
324
+ shareTab.classList.add('inactive');
325
+ rankTab.classList.remove('active');
326
+ rankTab.classList.add('inactive');
327
+ recordSection.classList.remove('hidden');
328
+ editSection.classList.add('hidden');
329
+ shareSection.classList.add('hidden');
330
+ rankSection.classList.add('hidden');
331
+ });
332
+
333
+ editTab.addEventListener('click', function() {
334
+ if (recordedClips.length === 0) {
335
+ showNotification("No Clips", "Record some clips first to edit them");
336
+ return;
337
+ }
338
+
339
+ editTab.classList.remove('inactive');
340
+ editTab.classList.add('active');
341
+ recordTab.classList.remove('active');
342
+ recordTab.classList.add('inactive');
343
+ shareTab.classList.remove('active');
344
+ shareTab.classList.add('inactive');
345
+ rankTab.classList.remove('active');
346
+ rankTab.classList.add('inactive');
347
+ recordSection.classList.add('hidden');
348
+ editSection.classList.remove('hidden');
349
+ shareSection.classList.add('hidden');
350
+ rankSection.classList.add('hidden');
351
+ });
352
+ shareTab.addEventListener('click', function() {
353
+ shareTab.classList.remove('inactive');
354
+ shareTab.classList.add('active');
355
+ recordTab.classList.remove('active');
356
+ recordTab.classList.add('inactive');
357
+ editTab.classList.remove('active');
358
+ editTab.classList.add('inactive');
359
+ rankTab.classList.remove('active');
360
+ rankTab.classList.add('inactive');
361
+ recordSection.classList.add('hidden');
362
+ editSection.classList.add('hidden');
363
+ shareSection.classList.remove('hidden');
364
+ rankSection.classList.add('hidden');
365
+ });
366
+
367
+ rankTab.addEventListener('click', function() {
368
+ if (sharedClips.length === 0) {
369
+ showNotification("No Shared Clips", "Share some clips first to see rankings");
370
+ return;
371
+ }
372
+
373
+ rankTab.classList.remove('inactive');
374
+ rankTab.classList.add('active');
375
+ recordTab.classList.remove('active');
376
+ recordTab.classList.add('inactive');
377
+ editTab.classList.remove('active');
378
+ editTab.classList.add('inactive');
379
+ shareTab.classList.remove('active');
380
+ shareTab.classList.add('inactive');
381
+ recordSection.classList.add('hidden');
382
+ editSection.classList.add('hidden');
383
+ shareSection.classList.add('hidden');
384
+ rankSection.classList.remove('hidden');
385
+
386
+ // Update rankings when tab is opened
387
+ updateRankings();
388
+ });
389
+
390
+ // Connect to Bluetooth
391
+ bluetoothConnectBtn.addEventListener('click', connectBluetooth);
392
+
393
+ // Share clips
394
+ shareClipsBtn.addEventListener('click', shareClipsWithGroup);
395
+
396
+ // Update username when changed
397
+ usernameInput.addEventListener('change', function() {
398
+ username = this.value || "User" + Math.floor(Math.random() * 1000);
399
+ });
400
+
401
+ // Update category when changed
402
+ contentCategorySelect.addEventListener('change', function() {
403
+ currentCategory = this.value;
404
+ });
405
+
406
+ // Update rankings when algorithm changes
407
+ rankingAlgorithmSelect.addEventListener('change', updateRankings);
408
+ });
409
+
410
+
411
+ // Show notification
412
+ function showNotification(title, message) {
413
+ const notification = document.getElementById('notification');
414
+ const titleElement = document.getElementById('notification-title');
415
+ const messageElement = document.getElementById('notification-message');
416
+
417
+ titleElement.textContent = title;
418
+ messageElement.textContent = message;
419
+
420
+ notification.classList.remove('hidden');
421
+ notification.classList.add('show');
422
+
423
+ setTimeout(() => {
424
+ notification.classList.remove('show');
425
+ setTimeout(() => notification.classList.add('hidden'), 300);
426
+ }, 3000);
427
+ }
428
+ // Quick prompt buttons
429
+ function quickPrompt(prompt) {
430
+ document.getElementById('user-input').value = prompt;
431
+ sendMessage();
432
+ }
433
+
434
+
435
+ // Send message to AI
436
+ async function sendMessage() {
437
+ const userInput = document.getElementById('user-input');
438
+ const chatMessages = document.getElementById('chat-messages');
439
+
440
+ if (userInput.value.trim() === '') return;
441
+
442
+ // Add user message to chat
443
+ const userMessage = document.createElement('div');
444
+ userMessage.className = 'chat-bubble user-bubble p-4 w-3/4 ml-auto mb-4 fade-in';
445
+ userMessage.innerHTML = `<p>${userInput.value}</p>`;
446
+ chatMessages.appendChild(userMessage);
447
+
448
+ // Show typing indicator
449
+ const typingIndicator = document.createElement('div');
450
+ typingIndicator.className = 'chat-bubble ai-bubble p-4 w-1/2 mb-4';
451
+ typingIndicator.innerHTML = '<div class="typing-indicator"><span></span><span></span><span></span></div>';
452
+ chatMessages.appendChild(typingIndicator);
453
+
454
+ // Scroll to bottom
455
+ chatMessages.scrollTop = chatMessages.scrollHeight;
456
+
457
+ // Save user message
458
+ const userMessageText = userInput.value;
459
+ userInput.value = '';
460
+
461
+ try {
462
+ // Call Cloudflare Workers AI
463
+ const aiResponse = await queryCloudflareAI(userMessageText);
464
+
465
+ // Remove typing indicator
466
+ chatMessages.removeChild(typingIndicator);
467
+
468
+ // Add AI response to chat
469
+ const aiMessage = document.createElement('div');
470
+ aiMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in';
471
+ aiMessage.innerHTML = `<p>${aiResponse}</p>`;
472
+ chatMessages.appendChild(aiMessage);
473
+
474
+ // Update recommendations based on AI response
475
+ updateRecommendationsFromAI(aiResponse);
476
+
477
+ } catch (error) {
478
+ // Remove typing indicator
479
+ chatMessages.removeChild(typingIndicator);
480
+
481
+ // Show error message
482
+ const errorMessage = document.createElement('div');
483
+ errorMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in';
484
+ errorMessage.innerHTML = `<p class="text-red-500">Sorry, I'm having trouble connecting to the AI service. Please try again later.</p>`;
485
+ chatMessages.appendChild(errorMessage);
486
+ }
487
+
488
+ // Scroll to bottom
489
+ chatMessages.scrollTop = chatMessages.scrollHeight;
490
+ }
491
+ // Query Cloudflare Workers AI
492
+ async function queryCloudflareAI(prompt) {
493
+ // In a production environment, this would be handled by a backend service
494
+ // to keep the API token secure. For this demo, we'll simulate the response.
495
+
496
+ console.log(`[DEBUG] Would call Cloudflare AI with prompt: "${prompt}"`);
497
+
498
+ // Simulate API call delay
499
+ await new Promise(resolve => setTimeout(resolve, 1500));
500
+
501
+ // Simulate different responses based on prompt
502
+ const lowerPrompt = prompt.toLowerCase();
503
+
504
+ if (lowerPrompt.includes('comedy') || lowerPrompt.includes('funny')) {
505
+ return "I'd recommend these comedy options that should give you a good laugh:\n\n1. 'The Grand Adventure' (Netflix) - A hilarious journey with unexpected twists\n2. 'Office Shenanigans' (Hulu) - Workplace comedy at its finest\n\nComedy can really lift your mood! Would you like more suggestions?";
506
+ }
507
+ else if (lowerPrompt.includes('thriller') || lowerPrompt.includes('suspense')) {
508
+ return "For thrilling content that will keep you on the edge of your seat, consider:\n\n1. 'Dark Secrets' (HBO Max) - A town's dark past resurfaces\n2. 'Midnight Caller' (Amazon Prime) - A psychological thriller about a mysterious phone call\n\nThese should provide plenty of suspense!";
509
+ }
510
+ else if (lowerPrompt.includes('romance') || lowerPrompt.includes('love')) {
511
+ return "Romantic stories can be so heartwarming! Here are my top picks:\n\n1. 'Romantic Getaway' (Amazon Prime) - Two strangers find love on vacation\n2. 'Love in Paris' (Netflix) - A classic romantic tale set in the City of Love\n\nLet me know if you'd like something more specific!";
512
+ }
513
+ else if (lowerPrompt.includes('recommend') || lowerPrompt.includes('suggest')) {
514
+ return "Based on your request, I'd recommend these excellent streaming options:\n\n1. 'SMPlus Exclusive Series' (SMPlus VHX) - Action-packed drama series\n2. 'Space Explorers' (Disney+) - Fascinating documentary about space\n\nI've updated the recommendations section with more options for you!";
515
+ }
516
+ else {
517
+ return "I'm here to help you find the perfect streaming content! Could you tell me more about what you're looking for? For example, you could say 'recommend a sci-fi movie' or 'what should I watch if I feel like laughing?'";
518
+ }
519
+ }
520
+
521
+
522
+ // Update recommendations based on AI response
523
+ function updateRecommendationsFromAI(aiResponse) {
524
+ let filter = 'all';
525
+
526
+ if (aiResponse.includes('comedy')) {
527
+ filter = 'comedy';
528
+ } else if (aiResponse.includes('thriller') || aiResponse.includes('suspense')) {
529
+ filter = 'thriller';
530
+ } else if (aiResponse.includes('romance') || aiResponse.includes('love')) {
531
+ filter = 'romance';
532
+ }
533
+
534
+ loadRecommendations(filter);
535
+ }
536
+
537
+
538
+ // Save show and set broadcast reminder
539
+ function saveShow(title, broadcastTime) {
540
+ // In a real app, this would save to a database
541
+ console.log(`Saved show: ${title}`);
542
+
543
+ // Show notification
544
+ showNotification("Reminder Set!", `We'll notify you when "${title}" is about to broadcast.`);
545
+
546
+ // In a real app, you would schedule a notification for the broadcast time
547
+ if (broadcastTime) {
548
+ const broadcastDate = new Date(broadcastTime);
549
+ const now = new Date();
550
+
551
+ // Only schedule if broadcast is in the future
552
+ if (broadcastDate > now) {
553
+ const timeUntilBroadcast = broadcastDate - now;
554
+
555
+ // Schedule notification 30 minutes before broadcast
556
+ setTimeout(() => {
557
+ showNotification("Starting Soon!", `"${title}" will begin broadcasting in 30 minutes!`);
558
+ }, timeUntilBroadcast - (30 * 60 * 1000));
559
+ }
560
+ }
561
+ }
562
+ // Load recommendations
563
+ function loadRecommendations(filter = 'all') {
564
+ const container = document.getElementById('recommendations-container');
565
+ container.innerHTML = '';
566
+
567
+ let filteredData = streamingData;
568
+
569
+ if (filter === 'comedy') {
570
+ filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('comedy'));
571
+ } else if (filter === 'thriller') {
572
+ filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('thriller'));
573
+ } else if (filter === 'romance') {
574
+ filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('romance'));
575
+ }
576
+
577
+ filteredData.forEach(item => {
578
+ const card = document.createElement('div');
579
+ card.className = 'stream-card bg-white rounded-lg overflow-hidden shadow-md hover:shadow-xl transition duration-300 fade-in p-4';
580
+ card.innerHTML = `
581
+ <div class="mb-3">
582
+ <div class="flex justify-between items-start">
583
+ <h3 class="font-bold text-base">${item.title}</h3>
584
+ <span class="bg-yellow-100 text-yellow-800 text-xs px-2 py-1 rounded-full flex items-center">
585
+ <i class="fas fa-star text-yellow-500 mr-1 text-xs"></i> ${item.rating}
586
+ </span>
587
+ </div>
588
+ <p class="text-gray-600 text-xs mb-1">${item.type} • ${item.genre} • ${item.year}</p>
589
+ <div class="text-xs text-indigo-600 mb-2">${item.platform}</div>
590
+ </div>
591
+ <p class="text-gray-700 text-sm mb-4">${item.description}</p>
592
+ <div class="flex justify-between items-center">
593
+ <button class="text-indigo-600 hover:text-indigo-800 text-xs font-medium" onclick="saveShow('${item.title}', '${item.broadcastTime}')">
594
+ <i class="far fa-bookmark mr-1"></i> Save
595
+ </button>
596
+ <a href="${item.platform === 'SMPlus VHX' ? 'https://smplus.vhx.tv' : '#'}" target="_blank" class="bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-1 rounded-full text-xs font-medium transition">
597
+ <i class="fas fa-play mr-1"></i> Watch
598
+ </a>
599
+ </div>
600
+ `;
601
+ container.appendChild(card);
602
+ });
603
+ }
604
+
605
+
606
+ // Load RSS feed
607
+ function loadRSSFeed(filter) {
608
+ const container = document.getElementById('rss-feed');
609
+ container.innerHTML = '';
610
+
611
+ const feedItems = rssFeedData[filter] || rssFeedData.all;
612
+
613
+ feedItems.forEach(item => {
614
+ const feedItem = document.createElement('div');
615
+ feedItem.className = 'rss-item bg-gray-50 p-3 rounded-lg';
616
+ feedItem.innerHTML = `
617
+ <h4 class="font-medium text-sm mb-1">${item.title}</h4>
618
+ <div class="flex items-center text-xs text-gray-500 mb-2">
619
+ <span>${item.source}</span>
620
+ <span class="mx-2">•</span>
621
+ <span>${item.time}</span>
622
+ </div>
623
+ <p class="text-xs text-gray-700">${item.excerpt}</p>
624
+ `;
625
+ container.appendChild(feedItem);
626
+ });
627
+ }
628
+ // Start recording video and audio
629
+ async function startRecording() {
630
+ try {
631
+ // Get user media
632
+ videoStream = await navigator.mediaDevices.getDisplayMedia({
633
+ video: true,
634
+ audio: true
635
+ });
636
+
637
+ audioStream = await navigator.mediaDevices.getUserMedia({
638
+ audio: true
639
+ });
640
+
641
+ // Create audio context
642
+ audioContext = new AudioContext();
643
+ const source = audioContext.createMediaStreamSource(audioStream);
644
+ const destination = audioContext.createMediaStreamDestination();
645
+ source.connect(destination);
646
+
647
+ // Combine video and audio streams
648
+ const combinedStream = new MediaStream([
649
+ ...videoStream.getVideoTracks(),
650
+ ...destination.stream.getAudioTracks()
651
+ ]);
652
+
653
+ // Create media recorder
654
+ mediaRecorder = new MediaRecorder(combinedStream, {
655
+ mimeType: 'video/webm'
656
+ });
657
+
658
+ // Update preview with live recording
659
+ const preview = document.getElementById('clip-preview');
660
+ preview.innerHTML = '<video autoplay muted></video>';
661
+ const previewVideo = preview.querySelector('video');
662
+ previewVideo.srcObject = combinedStream;
663
+
664
+ // Set recording state
665
+ isRecording = true;
666
+ document.getElementById('recording-indicator').classList.remove('hidden');
667
+ document.getElementById('start-recording').classList.add('hidden');
668
+ document.getElementById('stop-recording').classList.remove('hidden');
669
+
670
+ // Start progress bar animation
671
+ currentClipTime = 0;
672
+ const progressFill = document.getElementById('progress-fill');
673
+ progressFill.style.width = '0%';
674
+
675
+ currentClipInterval = setInterval(() => {
676
+ currentClipTime += 100;
677
+ const progressPercent = (currentClipTime / 5000) * 100;
678
+ progressFill.style.width = `${progressPercent}%`;
679
+ }, 100);
680
+
681
+ // Start recording in 5-second clips
682
+ let clipCount = 0;
683
+ recordingInterval = setInterval(() => {
684
+ if (clipCount > 0) {
685
+ // Stop current recording
686
+ mediaRecorder.stop();
687
+ }
688
+
689
+ // Start new recording
690
+ mediaRecorder.start();
691
+ clipCount++;
692
+
693
+ // Reset progress bar
694
+ currentClipTime = 0;
695
+ progressFill.style.width = '0%';
696
+
697
+ // Store clip data when available
698
+ mediaRecorder.ondataavailable = (e) => {
699
+ const clip = {
700
+ blob: e.data,
701
+ timestamp: new Date().toLocaleTimeString(),
702
+ url: URL.createObjectURL(e.data),
703
+ username: username,
704
+ category: currentCategory,
705
+ engagementScore: Math.random() * 5, // Simulated metrics
706
+ qualityScore: 3 + Math.random() * 2,
707
+ consistencyScore: 3 + Math.random() * 2
708
+ };
709
+ recordedClips.push(clip);
710
+ updateClipsList();
711
+ };
712
+ }, 5000);
713
+
714
+ showNotification("Recording Started", "Recording 5-second clips of your screen and audio");
715
+
716
+ } catch (error) {
717
+ console.error("Error starting recording:", error);
718
+ showNotification("Recording Error", "Could not start recording. Please check permissions.");
719
+ stopRecording();
720
+ }
721
+ }
722
+ // Stop recording
723
+ function stopRecording() {
724
+ if (mediaRecorder && mediaRecorder.state !== 'inactive') {
725
+ mediaRecorder.stop();
726
+ }
727
+
728
+ clearInterval(recordingInterval);
729
+ clearInterval(currentClipInterval);
730
+ isRecording = false;
731
+
732
+ // Stop all tracks
733
+ if (videoStream) {
734
+ videoStream.getTracks().forEach(track => track.stop());
735
+ }
736
+ if (audioStream) {
737
+ audioStream.getTracks().forEach(track => track.stop());
738
+ }
739
+
740
+ // Close audio context
741
+ if (audioContext && audioContext.state !== 'closed') {
742
+ audioContext.close();
743
+ }
744
+
745
+ // Reset preview
746
+ const preview = document.getElementById('clip-preview');
747
+ preview.innerHTML = `
748
+ <div class="clip-preview-placeholder">
749
+ <i class="fas fa-video"></i>
750
+ <p>Preview will appear here</p>
751
+ </div>
752
+ `;
753
+
754
+ // Reset progress bar
755
+ document.getElementById('progress-fill').style.width = '0%';
756
+
757
+ // Update UI
758
+ document.getElementById('recording-indicator').classList.add('hidden');
759
+ document.getElementById('start-recording').classList.remove('hidden');
760
+ document.getElementById('stop-recording').classList.add('hidden');
761
+
762
+ showNotification("Recording Stopped", `Captured ${recordedClips.length} clips ready for production`);
763
+ }
764
+
765
+
766
+ // Update clips list
767
+ function updateClipsList() {
768
+ const container = document.getElementById('clips-container');
769
+ container.innerHTML = '';
770
+
771
+ if (recordedClips.length === 0) {
772
+ container.innerHTML = '<p class="text-gray-500 text-sm">Clips will appear here...</p>';
773
+ return;
774
+ }
775
+
776
+ recordedClips.forEach((clip, index) => {
777
+ const clipItem = document.createElement('div');
778
+ clipItem.className = 'clip-item';
779
+ clipItem.innerHTML = `
780
+ <i class="fas fa-video text-gray-500 mr-2"></i>
781
+ <span class="text-sm flex-1">Clip ${index + 1}</span>
782
+ <span class="text-xs text-gray-500">${clip.timestamp}</span>
783
+ `;
784
+
785
+ // Add click handler to preview clip
786
+ clipItem.addEventListener('click', () => {
787
+ const preview = document.getElementById('clip-preview');
788
+ preview.innerHTML = '<video controls></video>';
789
+ const previewVideo = preview.querySelector('video');
790
+ previewVideo.src = clip.url;
791
+ });
792
+
793
+ container.appendChild(clipItem);
794
+ });
795
+ }
796
+ // Clear all clips
797
+ function clearClips() {
798
+ if (recordedClips.length === 0) return;
799
+
800
+ // In a real app, we would properly revoke the object URLs
801
+ recordedClips.forEach(clip => {
802
+ if (clip.url) {
803
+ URL.revokeObjectURL(clip.url);
804
+ }
805
+ });
806
+
807
+ recordedClips = [];
808
+ updateClipsList();
809
+
810
+ // Reset preview
811
+ const preview = document.getElementById('clip-preview');
812
+ preview.innerHTML = `
813
+ <div class="clip-preview-placeholder">
814
+ <i class="fas fa-video"></i>
815
+ <p>Preview will appear here</p>
816
+ </div>
817
+ `;
818
+
819
+ showNotification("Clips Cleared", "All recorded clips have been removed");
820
+ }
821
+
822
+
823
+ // Generate short video from clips
824
+ function generateShortVideo() {
825
+ if (recordedClips.length === 0) {
826
+ showNotification("No Clips", "Please record some clips first");
827
+ return;
828
+ }
829
+
830
+ // In a real app, this would use a video editing library to combine clips
831
+ // and sync with audio. For this demo, we'll simulate the process.
832
+
833
+ showNotification("Video Generation", "Processing your clips into a short video...");
834
+
835
+ setTimeout(() => {
836
+ // Simulate processing time
837
+ const videoUrl = URL.createObjectURL(new Blob(["Simulated video content"], { type: 'video/mp4' }));
838
+
839
+ // Create download link
840
+ const a = document.createElement('a');
841
+ a.href = videoUrl;
842
+ a.download = 'streamai-short.mp4';
843
+ a.click();
844
+
845
+ showNotification("Video Ready", "Your short video has been generated and downloaded");
846
+ }, 3000);
847
+ }
848
+
849
+
850
+ // Connect to Bluetooth devices
851
+ async function connectBluetooth() {
852
+ try {
853
+ // Request Bluetooth device
854
+ bluetoothDevice = await navigator.bluetooth.requestDevice({
855
+ acceptAllDevices: true,
856
+ optionalServices: ['generic_access']
857
+ });
858
+
859
+ // Connect to the GATT Server
860
+ bluetoothServer = await bluetoothDevice.gatt.connect();
861
+
862
+ // Get the service
863
+ bluetoothService = await bluetoothServer.getPrimaryService('generic_access');
864
+
865
+ // Get the characteristic
866
+ bluetoothCharacteristic = await bluetoothService.getCharacteristic('device_name');
867
+
868
+ // Update connection status
869
+ isBluetoothConnected = true;
870
+ const statusElement = document.getElementById('connection-status');
871
+ statusElement.innerHTML = `
872
+ <i class="fas fa-circle bluetooth-connected mr-1"></i>
873
+ <span>Connected to ${bluetoothDevice.name || 'device'}</span>
874
+ `;
875
+
876
+ // Simulate discovering group members
877
+ groupMembers = [
878
+ { name: "User" + Math.floor(Math.random() * 1000), device: "Device 1" },
879
+ { name: "User" + Math.floor(Math.random() * 1000), device: "Device 2" },
880
+ { name: "User" + Math.floor(Math.random() * 1000), device: "Device 3" }
881
+ ];
882
+ showNotification("Bluetooth Connected", `Connected to ${bluetoothDevice.name}. Found ${groupMembers.length} group members.`);
883
+
884
+ } catch (error) {
885
+ console.error("Bluetooth connection error:", error);
886
+ isBluetoothConnected = false;
887
+ document.getElementById('connection-status').innerHTML = `
888
+ <i class="fas fa-circle bluetooth-disconnected mr-1"></i>
889
+ <span>Not connected</span>
890
+ `;
891
+ showNotification("Connection Failed", "Could not connect to Bluetooth device");
892
+ }
893
+ }
894
+
895
+
896
+ // Share clips with Bluetooth group
897
+ async function shareClipsWithGroup() {
898
+ if (!isBluetoothConnected) {
899
+ showNotification("Not Connected", "Please connect to Bluetooth first");
900
+ return;
901
+ }
902
+
903
+ if (recordedClips.length === 0) {
904
+ showNotification("No Clips", "Please record some clips first");
905
+ return;
906
+ }
907
+
908
+ // In a real app, this would actually send the clips over Bluetooth
909
+ // For this demo, we'll simulate the sharing process
910
+
911
+ showNotification("Sharing Clips", "Sharing your clips with the group...");
912
+
913
+ // Simulate sharing delay
914
+ setTimeout(() => {
915
+ // Add metadata to clips and add to shared clips
916
+ recordedClips.forEach(clip => {
917
+ const sharedClip = {
918
+ ...clip,
919
+ sharedBy: username,
920
+ category: currentCategory,
921
+ timestamp: new Date().toLocaleTimeString(),
922
+ likes: Math.floor(Math.random() * 10),
923
+ views: Math.floor(Math.random() * 50)
924
+ };
925
+ sharedClips.push(sharedClip);
926
+ });
927
+
928
+ // Update shared clips display
929
+ updateSharedClipsList();
930
+
931
+ showNotification("Clips Shared", `Shared ${recordedClips.length} clips with ${groupMembers.length} group members`);
932
+ }, 2000);
933
+ }
934
+ // Update shared clips list
935
+ function updateSharedClipsList() {
936
+ const container = document.getElementById('shared-clips-container');
937
+ container.innerHTML = '';
938
+
939
+ if (sharedClips.length === 0) {
940
+ container.innerHTML = '<p class="text-gray-500 text-sm">Shared clips will appear here...</p>';
941
+ return;
942
+ }
943
+
944
+ sharedClips.forEach((clip, index) => {
945
+ const clipItem = document.createElement('div');
946
+ clipItem.className = 'clip-item';
947
+ clipItem.innerHTML = `
948
+ <i class="fas fa-video text-gray-500 mr-2"></i>
949
+ <span class="text-sm flex-1">Clip ${index + 1}</span>
950
+ <span class="user-tag">${clip.sharedBy}</span>
951
+ <span class="category-tag">${clip.category}</span>
952
+ `;
953
+
954
+ // Add click handler to preview clip
955
+ clipItem.addEventListener('click', () => {
956
+ const preview = document.getElementById('clip-preview');
957
+ preview.innerHTML = '<video controls></video>';
958
+ const previewVideo = preview.querySelector('video');
959
+ previewVideo.src = clip.url;
960
+ });
961
+
962
+ container.appendChild(clipItem);
963
+ });
964
+ }
965
+
966
+
967
+ // Benchmarking algorithm to rank clips
968
+ function benchmarkClips() {
969
+ if (sharedClips.length === 0) return [];
970
+
971
+ // Calculate scores for each clip
972
+ sharedClips.forEach(clip => {
973
+ // Composite score is weighted average of all metrics
974
+ clip.compositeScore = (
975
+ clip.engagementScore * 0.4 +
976
+ clip.qualityScore * 0.3 +
977
+ clip.consistencyScore * 0.3
978
+ );
979
+
980
+ // Add some randomness to simulate different algorithms
981
+ clip.engagementScore = Math.min(5, clip.engagementScore + Math.random() * 0.5 - 0.25);
982
+ clip.qualityScore = Math.min(5, clip.qualityScore + Math.random() * 0.5 - 0.25);
983
+ clip.consistencyScore = Math.min(5, clip.consistencyScore + Math.random() * 0.5 - 0.25);
984
+ });
985
+
986
+ // Sort based on selected algorithm
987
+ const algorithm = document.getElementById('ranking-algorithm').value;
988
+ let sortedClips = [...sharedClips];
989
+
990
+ switch (algorithm) {
991
+ case 'engagement':
992
+ sortedClips.sort((a, b) => b.engagementScore - a.engagementScore);
993
+ break;
994
+ case 'quality':
995
+ sortedClips.sort((a, b) => b.qualityScore - a.qualityScore);
996
+ break;
997
+ case 'consistency':
998
+ sortedClips.sort((a, b) => b.consistencyScore - a.consistencyScore);
999
+ break;
1000
+ case 'composite':
1001
+ default:
1002
+ sortedClips.sort((a, b) => b.compositeScore - a.compositeScore);
1003
+ break;
1004
+ }
1005
+ return sortedClips;
1006
+ }
1007
+
1008
+
1009
+ // Update rankings display
1010
+ function updateRankings() {
1011
+ const container = document.getElementById('rankings-container');
1012
+ container.innerHTML = '';
1013
+
1014
+ const rankedClips = benchmarkClips();
1015
+
1016
+ if (rankedClips.length === 0) {
1017
+ container.innerHTML = '<p class="text-gray-500 text-sm">Rankings will appear here when available...</p>';
1018
+ return;
1019
+ }
1020
  </script>