// scripts/app.js import { initUI } from './ui.js'; import { initChat } from './chat.js'; import { initVideo } from './video.js'; document.addEventListener('DOMContentLoaded', () => { // To handle dependencies between modules without a complex event system, // we can expose some functions globally. This is a pragmatic approach for this stage. window.showNotification = (title, message) => { // A simplified version for video.js to call const notification = document.getElementById('notification'); document.getElementById('notification-title').textContent = title; document.getElementById('notification-message').textContent = message; notification.classList.remove('hidden'); notification.classList.add('show'); setTimeout(() => notification.classList.remove('show'), 3000); }; window.loadRecommendations = (filter) => { // A simplified version for chat.js to call // In a real app, ui.js would export loadRecommendations for chat.js to import. const container = document.getElementById('recommendations-container'); // ... Add logic to load recommendations based on filter }; // Initialize all modules initUI(); initChat(); initVideo(); console.log("StreamAI application initialized."); }); // Configuration for Cloudflare Workers AI const AI_CONFIG = { accountId: 'oFD0IMs0aV8eKMMMdTEF2zRQmtzvKMH43LX5ZWUJ', gatewayId: 'streamai_gateway', apiToken: 'masked_for_security', // In a real app, this would be handled server-side model: '@cf/meta/llama-2-7b-chat-int8' }; // Sample streaming data with SMPlus VHX added to the top const streamingData = [ { title: "SMPlus Exclusive Series", type: "TV Series", genre: "Drama, Action", platform: "SMPlus VHX", rating: "4.9", year: "2023", description: "An exclusive action-packed drama series only available on SMPlus VHX.", broadcastTime: "2023-12-15T20:00:00" }, { title: "The Grand Adventure", type: "Movie", genre: "Adventure, Comedy", platform: "Netflix", rating: "4.8", year: "2022", description: "A hilarious journey across continents with unexpected twists.", broadcastTime: "2023-12-10T19:30:00" }, { title: "Dark Secrets", type: "TV Series", genre: "Drama, Thriller", platform: "HBO Max", rating: "4.7", year: "2021", description: "A small town's dark past resurfaces with shocking revelations.", broadcastTime: "2023-12-12T21:00:00" }, { title: "Space Explorers", type: "Documentary", genre: "Science, Space", platform: "Disney+", rating: "4.9", year: "2023", description: "The latest discoveries from the frontiers of space exploration.", broadcastTime: "2023-12-14T18:00:00" }, { title: "Romantic Getaway", type: "Movie", genre: "Romance, Comedy", platform: "Amazon Prime", rating: "4.5", year: "2021", description: "Two strangers find love during an unexpected vacation.", broadcastTime: "2023-12-16T20:30:00" }, { title: "Tech Today", type: "News Show", genre: "Technology, News", platform: "SMPlus VHX", rating: "4.6", year: "2023", description: "Daily tech news and gadget reviews from around the world.", broadcastTime: "2023-12-17T09:00:00" }, { title: "Cooking Masters", type: "Reality Show", genre: "Food, Competition", platform: "Netflix", rating: "4.7", year: "2023", description: "Top chefs compete in intense culinary challenges.", broadcastTime: "2023-12-18T20:00:00" }, { title: "History Unearthed", type: "Documentary", genre: "History, Education", platform: "HBO Max", rating: "4.8", year: "2023", description: "Fascinating historical discoveries and their modern implications.", broadcastTime: "2023-12-19T21:00:00" } ]; // Sample RSS feed data const rssFeedData = { all: [ { title: "New Episode: SMPlus Exclusive Series", source: "SMPlus VHX", time: "2 hours ago", excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.", category: "personalized" }, { title: "Trending: The Grand Adventure hits #1", source: "Netflix", time: "5 hours ago", excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.", category: "trending" }, { title: "Breaking: New streaming partnership announced", source: "Streaming News", time: "1 day ago", excerpt: "Major platforms announce new content sharing agreement starting next month.", category: "news" }, { title: "Recommended for you: Space Explorers", source: "Disney+", time: "1 day ago", excerpt: "Based on your interest in science documentaries, we recommend this new series.", category: "personalized" }, { title: "Upcoming: Romantic Getaway special event", source: "Amazon Prime", time: "2 days ago", excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.", category: "trending" } ], news: [ { title: "Breaking: New streaming partnership announced", source: "Streaming News", time: "1 day ago", excerpt: "Major platforms announce new content sharing agreement starting next month.", category: "news" }, { title: "Streaming industry report Q4 2023", source: "Tech Insights", time: "3 days ago", excerpt: "Latest statistics show continued growth in streaming subscriptions worldwide.", category: "news" } ], trending: [ { title: "Trending: The Grand Adventure hits #1", source: "Netflix", time: "5 hours ago", excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.", category: "trending" }, { title: "Upcoming: Romantic Getaway special event", source: "Amazon Prime", time: "2 days ago", excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.", category: "trending" } ], personalized: [ { title: "New Episode: SMPlus Exclusive Series", source: "SMPlus VHX", time: "2 hours ago", excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.", category: "personalized" }, { title: "Recommended for you: Space Explorers", source: "Disney+", time: "1 day ago", excerpt: "Based on your interest in science documentaries, we recommend this new series.", category: "personalized" } ] }; // Video production variables let isRecording = false; let recordingInterval; let recordedClips = []; let mediaRecorder; let audioContext; let audioStream; let videoStream; let currentClipTime = 0; let currentClipInterval; // Bluetooth sharing variables let isBluetoothConnected = false; let bluetoothDevice; let bluetoothServer; let bluetoothService; let bluetoothCharacteristic; let sharedClips = []; let groupMembers = []; let username = "User" + Math.floor(Math.random() * 1000); let currentCategory = "general"; // Initialize chat document.addEventListener('DOMContentLoaded', function() { const sendBtn = document.getElementById('send-btn'); const userInput = document.getElementById('user-input'); const chatMessages = document.getElementById('chat-messages'); const productionButton = document.getElementById('production-button'); const productionPanel = document.getElementById('production-panel'); const startRecordingBtn = document.getElementById('start-recording'); const stopRecordingBtn = document.getElementById('stop-recording'); const generateVideoBtn = document.getElementById('generate-video'); const clearClipsBtn = document.getElementById('clear-clips'); const rssFilterBtns = document.querySelectorAll('.rss-filter-btn'); const recordTab = document.getElementById('record-tab'); const editTab = document.getElementById('edit-tab'); const shareTab = document.getElementById('share-tab'); const rankTab = document.getElementById('rank-tab'); const recordSection = document.getElementById('record-section'); const editSection = document.getElementById('edit-section'); const shareSection = document.getElementById('share-section'); const rankSection = document.getElementById('rank-section'); const bluetoothConnectBtn = document.getElementById('bluetooth-connect'); const shareClipsBtn = document.getElementById('share-clips'); const usernameInput = document.getElementById('username'); const contentCategorySelect = document.getElementById('content-category'); const rankingAlgorithmSelect = document.getElementById('ranking-algorithm'); // Set default username usernameInput.value = username; // Load sample recommendations loadRecommendations(); // Load RSS feed loadRSSFeed('all'); // Send message on button click sendBtn.addEventListener('click', sendMessage); // Send message on Enter key userInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { sendMessage(); } }); // Toggle production panel productionButton.addEventListener('click', function() { productionPanel.classList.toggle('open'); }); // Start recording startRecordingBtn.addEventListener('click', startRecording); // Stop recording stopRecordingBtn.addEventListener('click', stopRecording); // Generate video generateVideoBtn.addEventListener('click', generateShortVideo); // Clear clips clearClipsBtn.addEventListener('click', clearClips); // Filter RSS feed rssFilterBtns.forEach(btn => { btn.addEventListener('click', function() { // Update active button rssFilterBtns.forEach(b => { b.classList.remove('bg-indigo-100', 'text-indigo-700'); b.classList.add('bg-gray-100', 'text-gray-700'); }); this.classList.remove('bg-gray-100', 'text-gray-700'); this.classList.add('bg-indigo-100', 'text-indigo-700'); // Load filtered feed loadRSSFeed(this.dataset.filter); }); }); // Switch between record and edit tabs recordTab.addEventListener('click', function() { recordTab.classList.remove('inactive'); recordTab.classList.add('active'); editTab.classList.remove('active'); editTab.classList.add('inactive'); shareTab.classList.remove('active'); shareTab.classList.add('inactive'); rankTab.classList.remove('active'); rankTab.classList.add('inactive'); recordSection.classList.remove('hidden'); editSection.classList.add('hidden'); shareSection.classList.add('hidden'); rankSection.classList.add('hidden'); }); editTab.addEventListener('click', function() { if (recordedClips.length === 0) { showNotification("No Clips", "Record some clips first to edit them"); return; } editTab.classList.remove('inactive'); editTab.classList.add('active'); recordTab.classList.remove('active'); recordTab.classList.add('inactive'); shareTab.classList.remove('active'); shareTab.classList.add('inactive'); rankTab.classList.remove('active'); rankTab.classList.add('inactive'); recordSection.classList.add('hidden'); editSection.classList.remove('hidden'); shareSection.classList.add('hidden'); rankSection.classList.add('hidden'); }); shareTab.addEventListener('click', function() { shareTab.classList.remove('inactive'); shareTab.classList.add('active'); recordTab.classList.remove('active'); recordTab.classList.add('inactive'); editTab.classList.remove('active'); editTab.classList.add('inactive'); rankTab.classList.remove('active'); rankTab.classList.add('inactive'); recordSection.classList.add('hidden'); editSection.classList.add('hidden'); shareSection.classList.remove('hidden'); rankSection.classList.add('hidden'); }); rankTab.addEventListener('click', function() { if (sharedClips.length === 0) { showNotification("No Shared Clips", "Share some clips first to see rankings"); return; } rankTab.classList.remove('inactive'); rankTab.classList.add('active'); recordTab.classList.remove('active'); recordTab.classList.add('inactive'); editTab.classList.remove('active'); editTab.classList.add('inactive'); shareTab.classList.remove('active'); shareTab.classList.add('inactive'); recordSection.classList.add('hidden'); editSection.classList.add('hidden'); shareSection.classList.add('hidden'); rankSection.classList.remove('hidden'); // Update rankings when tab is opened updateRankings(); }); // Connect to Bluetooth bluetoothConnectBtn.addEventListener('click', connectBluetooth); // Share clips shareClipsBtn.addEventListener('click', shareClipsWithGroup); // Update username when changed usernameInput.addEventListener('change', function() { username = this.value || "User" + Math.floor(Math.random() * 1000); }); // Update category when changed contentCategorySelect.addEventListener('change', function() { currentCategory = this.value; }); // Update rankings when algorithm changes rankingAlgorithmSelect.addEventListener('change', updateRankings); }); // Show notification function showNotification(title, message) { const notification = document.getElementById('notification'); const titleElement = document.getElementById('notification-title'); const messageElement = document.getElementById('notification-message'); titleElement.textContent = title; messageElement.textContent = message; notification.classList.remove('hidden'); notification.classList.add('show'); setTimeout(() => { notification.classList.remove('show'); setTimeout(() => notification.classList.add('hidden'), 300); }, 3000); } // Quick prompt buttons function quickPrompt(prompt) { document.getElementById('user-input').value = prompt; sendMessage(); } // Send message to AI async function sendMessage() { const userInput = document.getElementById('user-input'); const chatMessages = document.getElementById('chat-messages'); if (userInput.value.trim() === '') return; // Add user message to chat const userMessage = document.createElement('div'); userMessage.className = 'chat-bubble user-bubble p-4 w-3/4 ml-auto mb-4 fade-in'; userMessage.innerHTML = `
${userInput.value}
`; chatMessages.appendChild(userMessage); // Show typing indicator const typingIndicator = document.createElement('div'); typingIndicator.className = 'chat-bubble ai-bubble p-4 w-1/2 mb-4'; typingIndicator.innerHTML = '${aiResponse}
`; chatMessages.appendChild(aiMessage); // Update recommendations based on AI response updateRecommendationsFromAI(aiResponse); } catch (error) { // Remove typing indicator chatMessages.removeChild(typingIndicator); // Show error message const errorMessage = document.createElement('div'); errorMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in'; errorMessage.innerHTML = `Sorry, I'm having trouble connecting to the AI service. Please try again later.
`; chatMessages.appendChild(errorMessage); } // Scroll to bottom chatMessages.scrollTop = chatMessages.scrollHeight; } // Query Cloudflare Workers AI async function queryCloudflareAI(prompt) { // In a production environment, this would be handled by a backend service // to keep the API token secure. For this demo, we'll simulate the response. console.log(`[DEBUG] Would call Cloudflare AI with prompt: "${prompt}"`); // Simulate API call delay await new Promise(resolve => setTimeout(resolve, 1500)); // Simulate different responses based on prompt const lowerPrompt = prompt.toLowerCase(); if (lowerPrompt.includes('comedy') || lowerPrompt.includes('funny')) { 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?"; } else if (lowerPrompt.includes('thriller') || lowerPrompt.includes('suspense')) { 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!"; } else if (lowerPrompt.includes('romance') || lowerPrompt.includes('love')) { 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!"; } else if (lowerPrompt.includes('recommend') || lowerPrompt.includes('suggest')) { 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!"; } else { 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?'"; } } // Update recommendations based on AI response function updateRecommendationsFromAI(aiResponse) { let filter = 'all'; if (aiResponse.includes('comedy')) { filter = 'comedy'; } else if (aiResponse.includes('thriller') || aiResponse.includes('suspense')) { filter = 'thriller'; } else if (aiResponse.includes('romance') || aiResponse.includes('love')) { filter = 'romance'; } loadRecommendations(filter); } // Save show and set broadcast reminder function saveShow(title, broadcastTime) { // In a real app, this would save to a database console.log(`Saved show: ${title}`); // Show notification showNotification("Reminder Set!", `We'll notify you when "${title}" is about to broadcast.`); // In a real app, you would schedule a notification for the broadcast time if (broadcastTime) { const broadcastDate = new Date(broadcastTime); const now = new Date(); // Only schedule if broadcast is in the future if (broadcastDate > now) { const timeUntilBroadcast = broadcastDate - now; // Schedule notification 30 minutes before broadcast setTimeout(() => { showNotification("Starting Soon!", `"${title}" will begin broadcasting in 30 minutes!`); }, timeUntilBroadcast - (30 * 60 * 1000)); } } } // Load recommendations function loadRecommendations(filter = 'all') { const container = document.getElementById('recommendations-container'); container.innerHTML = ''; let filteredData = streamingData; if (filter === 'comedy') { filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('comedy')); } else if (filter === 'thriller') { filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('thriller')); } else if (filter === 'romance') { filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('romance')); } filteredData.forEach(item => { const card = document.createElement('div'); card.className = 'stream-card bg-white rounded-lg overflow-hidden shadow-md hover:shadow-xl transition duration-300 fade-in p-4'; card.innerHTML = `${item.type} • ${item.genre} • ${item.year}
${item.description}
`; container.appendChild(card); }); } // Load RSS feed function loadRSSFeed(filter) { const container = document.getElementById('rss-feed'); container.innerHTML = ''; const feedItems = rssFeedData[filter] || rssFeedData.all; feedItems.forEach(item => { const feedItem = document.createElement('div'); feedItem.className = 'rss-item bg-gray-50 p-3 rounded-lg'; feedItem.innerHTML = `${item.excerpt}
`; container.appendChild(feedItem); }); } // Start recording video and audio async function startRecording() { try { // Get user media videoStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: true }); audioStream = await navigator.mediaDevices.getUserMedia({ audio: true }); // Create audio context audioContext = new AudioContext(); const source = audioContext.createMediaStreamSource(audioStream); const destination = audioContext.createMediaStreamDestination(); source.connect(destination); // Combine video and audio streams const combinedStream = new MediaStream([ ...videoStream.getVideoTracks(), ...destination.stream.getAudioTracks() ]); // Create media recorder mediaRecorder = new MediaRecorder(combinedStream, { mimeType: 'video/webm' }); // Update preview with live recording const preview = document.getElementById('clip-preview'); preview.innerHTML = ''; const previewVideo = preview.querySelector('video'); previewVideo.srcObject = combinedStream; // Set recording state isRecording = true; document.getElementById('recording-indicator').classList.remove('hidden'); document.getElementById('start-recording').classList.add('hidden'); document.getElementById('stop-recording').classList.remove('hidden'); // Start progress bar animation currentClipTime = 0; const progressFill = document.getElementById('progress-fill'); progressFill.style.width = '0%'; currentClipInterval = setInterval(() => { currentClipTime += 100; const progressPercent = (currentClipTime / 5000) * 100; progressFill.style.width = `${progressPercent}%`; }, 100); // Start recording in 5-second clips let clipCount = 0; recordingInterval = setInterval(() => { if (clipCount > 0) { // Stop current recording mediaRecorder.stop(); } // Start new recording mediaRecorder.start(); clipCount++; // Reset progress bar currentClipTime = 0; progressFill.style.width = '0%'; // Store clip data when available mediaRecorder.ondataavailable = (e) => { const clip = { blob: e.data, timestamp: new Date().toLocaleTimeString(), url: URL.createObjectURL(e.data), username: username, category: currentCategory, engagementScore: Math.random() * 5, // Simulated metrics qualityScore: 3 + Math.random() * 2, consistencyScore: 3 + Math.random() * 2 }; recordedClips.push(clip); updateClipsList(); }; }, 5000); showNotification("Recording Started", "Recording 5-second clips of your screen and audio"); } catch (error) { console.error("Error starting recording:", error); showNotification("Recording Error", "Could not start recording. Please check permissions."); stopRecording(); } } // Stop recording function stopRecording() { if (mediaRecorder && mediaRecorder.state !== 'inactive') { mediaRecorder.stop(); } clearInterval(recordingInterval); clearInterval(currentClipInterval); isRecording = false; // Stop all tracks if (videoStream) { videoStream.getTracks().forEach(track => track.stop()); } if (audioStream) { audioStream.getTracks().forEach(track => track.stop()); } // Close audio context if (audioContext && audioContext.state !== 'closed') { audioContext.close(); } // Reset preview const preview = document.getElementById('clip-preview'); preview.innerHTML = `Preview will appear here
Clips will appear here...
'; return; } recordedClips.forEach((clip, index) => { const clipItem = document.createElement('div'); clipItem.className = 'clip-item'; clipItem.innerHTML = ` Clip ${index + 1} ${clip.timestamp} `; // Add click handler to preview clip clipItem.addEventListener('click', () => { const preview = document.getElementById('clip-preview'); preview.innerHTML = ''; const previewVideo = preview.querySelector('video'); previewVideo.src = clip.url; }); container.appendChild(clipItem); }); } // Clear all clips function clearClips() { if (recordedClips.length === 0) return; // In a real app, we would properly revoke the object URLs recordedClips.forEach(clip => { if (clip.url) { URL.revokeObjectURL(clip.url); } }); recordedClips = []; updateClipsList(); // Reset preview const preview = document.getElementById('clip-preview'); preview.innerHTML = `Preview will appear here
Shared clips will appear here...
'; return; } sharedClips.forEach((clip, index) => { const clipItem = document.createElement('div'); clipItem.className = 'clip-item'; clipItem.innerHTML = ` Clip ${index + 1} ${clip.sharedBy} ${clip.category} `; // Add click handler to preview clip clipItem.addEventListener('click', () => { const preview = document.getElementById('clip-preview'); preview.innerHTML = ''; const previewVideo = preview.querySelector('video'); previewVideo.src = clip.url; }); container.appendChild(clipItem); }); } // Benchmarking algorithm to rank clips function benchmarkClips() { if (sharedClips.length === 0) return []; // Calculate scores for each clip sharedClips.forEach(clip => { // Composite score is weighted average of all metrics clip.compositeScore = ( clip.engagementScore * 0.4 + clip.qualityScore * 0.3 + clip.consistencyScore * 0.3 ); // Add some randomness to simulate different algorithms clip.engagementScore = Math.min(5, clip.engagementScore + Math.random() * 0.5 - 0.25); clip.qualityScore = Math.min(5, clip.qualityScore + Math.random() * 0.5 - 0.25); clip.consistencyScore = Math.min(5, clip.consistencyScore + Math.random() * 0.5 - 0.25); }); // Sort based on selected algorithm const algorithm = document.getElementById('ranking-algorithm').value; let sortedClips = [...sharedClips]; switch (algorithm) { case 'engagement': sortedClips.sort((a, b) => b.engagementScore - a.engagementScore); break; case 'quality': sortedClips.sort((a, b) => b.qualityScore - a.qualityScore); break; case 'consistency': sortedClips.sort((a, b) => b.consistencyScore - a.consistencyScore); break; case 'composite': default: sortedClips.sort((a, b) => b.compositeScore - a.compositeScore); break; } return sortedClips; } // Update rankings display function updateRankings() { const container = document.getElementById('rankings-container'); container.innerHTML = ''; const rankedClips = benchmarkClips(); if (rankedClips.length === 0) { container.innerHTML = 'Rankings will appear here when available...
'; return; }