privateuserh commited on
Commit
0bec59d
·
verified ·
1 Parent(s): 1cbea95

Upload app.js

Browse files
Files changed (1) hide show
  1. app.js +986 -0
app.js ADDED
@@ -0,0 +1,986 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>