Spaces:
Paused
Paused
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>StreamAI - Personalized Streaming Recommendations</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @keyframes float { | |
| 0% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| 100% { transform: translateY(0px); } | |
| } | |
| .floating { | |
| animation: float 6s ease-in-out infinite; | |
| } | |
| .gradient-bg { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| } | |
| .chat-bubble { | |
| border-radius: 20px; | |
| position: relative; | |
| max-width: 80%; | |
| } | |
| .user-bubble { | |
| background-color: #4f46e5; | |
| color: white; | |
| margin-left: auto; | |
| border-bottom-right-radius: 5px; | |
| } | |
| .ai-bubble { | |
| background-color: #f3f4f6; | |
| color: #1f2937; | |
| margin-right: auto; | |
| border-bottom-left-radius: 5px; | |
| } | |
| .typing-indicator span { | |
| display: inline-block; | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background-color: #9ca3af; | |
| margin: 0 2px; | |
| } | |
| .typing-indicator span:nth-child(1) { | |
| animation: bounce 1s infinite; | |
| } | |
| .typing-indicator span:nth-child(2) { | |
| animation: bounce 1s infinite 0.2s; | |
| } | |
| .typing-indicator span:nth-child(3) { | |
| animation: bounce 1s infinite 0.4s; | |
| } | |
| @keyframes bounce { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-5px); } | |
| } | |
| .stream-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.5s ease-in; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| .notification { | |
| position: fixed; | |
| bottom: 20px; | |
| right: 20px; | |
| background: #4f46e5; | |
| color: white; | |
| padding: 15px 25px; | |
| border-radius: 8px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| transform: translateX(120%); | |
| transition: transform 0.3s ease-out; | |
| z-index: 1000; | |
| } | |
| .notification.show { | |
| transform: translateX(0); | |
| } | |
| .production-button { | |
| position: fixed; | |
| bottom: 30px; | |
| right: 30px; | |
| width: 60px; | |
| height: 60px; | |
| border-radius: 50%; | |
| background: linear-gradient(135deg, #ff5e62 0%, #ff9966 100%); | |
| color: white; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| box-shadow: 0 10px 25px rgba(255, 94, 98, 0.3); | |
| cursor: pointer; | |
| z-index: 999; | |
| transition: all 0.3s ease; | |
| } | |
| .production-button:hover { | |
| transform: scale(1.1); | |
| box-shadow: 0 15px 30px rgba(255, 94, 98, 0.4); | |
| } | |
| .production-panel { | |
| position: fixed; | |
| bottom: 110px; | |
| right: 30px; | |
| width: 350px; | |
| background: white; | |
| border-radius: 12px; | |
| box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1); | |
| padding: 20px; | |
| z-index: 998; | |
| transform: translateY(20px); | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: all 0.3s ease; | |
| } | |
| .production-panel.open { | |
| transform: translateY(0); | |
| opacity: 1; | |
| pointer-events: all; | |
| } | |
| .recording-indicator { | |
| position: absolute; | |
| top: -10px; | |
| right: -10px; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background: #ff5e62; | |
| animation: pulse 1.5s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 94, 98, 0.7); } | |
| 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(255, 94, 98, 0); } | |
| 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 94, 98, 0); } | |
| } | |
| .clip-item { | |
| display: flex; | |
| align-items: center; | |
| padding: 8px 0; | |
| border-bottom: 1px solid #eee; | |
| cursor: pointer; | |
| } | |
| .clip-item:hover { | |
| background-color: #f9fafb; | |
| } | |
| .clip-item:last-child { | |
| border-bottom: none; | |
| } | |
| .clip-preview { | |
| width: 100%; | |
| height: 180px; | |
| background-color: #f3f4f6; | |
| border-radius: 8px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| margin-bottom: 15px; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .clip-preview video { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| } | |
| .clip-preview-placeholder { | |
| text-align: center; | |
| color: #6b7280; | |
| } | |
| .clip-preview-placeholder i { | |
| font-size: 40px; | |
| margin-bottom: 10px; | |
| display: block; | |
| } | |
| .progress-bar { | |
| height: 4px; | |
| background-color: #e5e7eb; | |
| border-radius: 2px; | |
| overflow: hidden; | |
| margin-top: 10px; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background-color: #4f46e5; | |
| width: 0%; | |
| transition: width 0.3s ease; | |
| } | |
| .tab { | |
| padding: 8px 16px; | |
| border-radius: 20px; | |
| font-size: 14px; | |
| cursor: pointer; | |
| margin-right: 8px; | |
| } | |
| .tab.active { | |
| background-color: #4f46e5; | |
| color: white; | |
| } | |
| .tab.inactive { | |
| background-color: #f3f4f6; | |
| color: #6b7280; | |
| } | |
| .rank-badge { | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 10px; | |
| font-weight: bold; | |
| margin-left: 8px; | |
| } | |
| .rank-1 { | |
| background-color: #f59e0b; | |
| color: white; | |
| } | |
| .rank-2 { | |
| background-color: #6b7280; | |
| color: white; | |
| } | |
| .rank-3 { | |
| background-color: #92400e; | |
| color: white; | |
| } | |
| .bluetooth-connected { | |
| color: #3b82f6; | |
| animation: pulse 2s infinite; | |
| } | |
| .bluetooth-disconnected { | |
| color: #6b7280; | |
| } | |
| .user-tag { | |
| background-color: #e5e7eb; | |
| color: #4b5563; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-size: 10px; | |
| margin-left: 4px; | |
| } | |
| .category-tag { | |
| background-color: #dbeafe; | |
| color: #1e40af; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-size: 10px; | |
| margin-left: 4px; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-100 font-sans"> | |
| <!-- Notification --> | |
| <div id="notification" class="notification hidden"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-bell text-yellow-300 mr-3"></i> | |
| <div> | |
| <p class="font-semibold" id="notification-title">Reminder Set!</p> | |
| <p class="text-sm" id="notification-message">We'll notify you when this show is about to broadcast.</p> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Production Button & Panel --> | |
| <div class="production-button" id="production-button"> | |
| <i class="fas fa-video"></i> | |
| <div class="recording-indicator hidden" id="recording-indicator"></div> | |
| </div> | |
| <div class="production-panel" id="production-panel"> | |
| <div class="flex mb-4"> | |
| <div class="tab active" id="record-tab">Record</div> | |
| <div class="tab inactive" id="edit-tab">Edit</div> | |
| <div class="tab inactive" id="share-tab">Share</div> | |
| <div class="tab inactive" id="rank-tab">Rankings</div> | |
| </div> | |
| <div id="record-section"> | |
| <h3 class="font-bold text-lg mb-4">Create Video Clips</h3> | |
| <p class="text-sm text-gray-600 mb-4">Record your screen and audio to create short clips for your production. Perfect for capturing highlights or creating content snippets.</p> | |
| <div class="clip-preview" id="clip-preview"> | |
| <div class="clip-preview-placeholder"> | |
| <i class="fas fa-video"></i> | |
| <p>Preview will appear here</p> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <button id="start-recording" class="bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded-full text-sm font-medium transition"> | |
| <i class="fas fa-circle mr-1"></i> Start Recording (5s clips) | |
| </button> | |
| <button id="stop-recording" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm font-medium transition ml-2 hidden"> | |
| <i class="fas fa-stop mr-1"></i> Stop | |
| </button> | |
| </div> | |
| <div class="progress-bar"> | |
| <div class="progress-fill" id="progress-fill"></div> | |
| </div> | |
| <div id="clips-container" class="mb-4 max-h-40 overflow-y-auto"> | |
| <p class="text-gray-500 text-sm">Clips will appear here when recorded...</p> | |
| </div> | |
| </div> | |
| <div id="edit-section" class="hidden"> | |
| <h3 class="font-bold text-lg mb-4">Edit Your Production</h3> | |
| <p class="text-sm text-gray-600 mb-4">Combine your clips, add transitions, and create a polished final video.</p> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Video Title</label> | |
| <input type="text" id="video-title" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm" placeholder="My Awesome Video"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Select Transition</label> | |
| <select id="video-transition" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm"> | |
| <option value="none">No Transition</option> | |
| <option value="fade">Fade</option> | |
| <option value="slide">Slide</option> | |
| <option value="zoom">Zoom</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div id="share-section" class="hidden"> | |
| <h3 class="font-bold text-lg mb-4">Share with Bluetooth Group</h3> | |
| <p class="text-sm text-gray-600 mb-4">Connect with nearby devices and share your clips with the group.</p> | |
| <div class="flex items-center mb-4"> | |
| <button id="bluetooth-connect" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-full text-sm font-medium transition"> | |
| <i class="fas fa-bluetooth-b mr-1"></i> Connect Devices | |
| </button> | |
| <div id="connection-status" class="ml-3 text-sm"> | |
| <i class="fas fa-circle bluetooth-disconnected mr-1"></i> | |
| <span>Not connected</span> | |
| </div> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Your Username</label> | |
| <input type="text" id="username" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm" placeholder="Enter a username"> | |
| </div> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Content Category</label> | |
| <select id="content-category" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm"> | |
| <option value="general">General</option> | |
| <option value="gaming">Gaming</option> | |
| <option value="education">Education</option> | |
| <option value="entertainment">Entertainment</option> | |
| <option value="news">News</option> | |
| </select> | |
| </div> | |
| <button id="share-clips" class="w-full bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded-full text-sm font-medium transition mb-4"> | |
| <i class="fas fa-share-alt mr-1"></i> Share Selected Clips | |
| </button> | |
| <div id="shared-clips-container" class="max-h-40 overflow-y-auto"> | |
| <p class="text-gray-500 text-sm">Shared clips will appear here...</p> | |
| </div> | |
| </div> | |
| <div id="rank-section" class="hidden"> | |
| <h3 class="font-bold text-lg mb-4">Group Rankings</h3> | |
| <p class="text-sm text-gray-600 mb-4">See how your clips rank against others in your group.</p> | |
| <div class="mb-4"> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Ranking Algorithm</label> | |
| <select id="ranking-algorithm" class="w-full border border-gray-300 rounded-md px-3 py-2 text-sm"> | |
| <option value="engagement">Engagement Score</option> | |
| <option value="quality">Quality Score</option> | |
| <option value="consistency">Consistency Score</option> | |
| <option value="composite">Composite Score</option> | |
| </select> | |
| </div> | |
| <div id="rankings-container" class="max-h-60 overflow-y-auto"> | |
| <p class="text-gray-500 text-sm">Rankings will appear here when available...</p> | |
| </div> | |
| </div> | |
| <div class="flex space-x-2"> | |
| <button id="generate-video" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-full text-sm font-medium transition flex-1"> | |
| <i class="fas fa-magic mr-1"></i> Generate Video | |
| </button> | |
| <button id="clear-clips" class="bg-gray-200 hover:bg-gray-300 text-gray-700 px-4 py-2 rounded-full text-sm font-medium transition"> | |
| <i class="fas fa-trash mr-1"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Header --> | |
| <header class="gradient-bg text-white shadow-lg"> | |
| <div class="container mx-auto px-4 py-6"> | |
| <div class="flex justify-between items-center"> | |
| <div class="flex items-center space-x-3"> | |
| <i class="fas fa-stream text-3xl"></i> | |
| <h1 class="text-2xl font-bold">StreamAI</h1> | |
| </div> | |
| <nav class="hidden md:flex space-x-6"> | |
| <a href="#" class="hover:text-indigo-200 transition">Home</a> | |
| <a href="#" class="hover:text-indigo-200 transition">Features</a> | |
| <a href="#" class="hover:text-indigo-200 transition">About</a> | |
| <a href="#" class="hover:text-indigo-200 transition">Contact</a> | |
| </nav> | |
| <button class="md:hidden text-xl"> | |
| <i class="fas fa-bars"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Hero Section --> | |
| <section class="gradient-bg text-white py-16"> | |
| <div class="container mx-auto px-4 flex flex-col md:flex-row items-center"> | |
| <div class="md:w-1/2 mb-10 md:mb-0"> | |
| <h2 class="text-4xl md:text-5xl font-bold mb-4">AI-Powered Streaming Recommendations</h2> | |
| <p class="text-xl mb-8 text-indigo-100">Get personalized streaming suggestions powered by Cloudflare Workers AI.</p> | |
| <div class="flex space-x-4"> | |
| <button class="bg-white text-indigo-600 px-6 py-3 rounded-full font-semibold hover:bg-indigo-100 transition"> | |
| Try It Now | |
| </button> | |
| <button class="border-2 border-white text-white px-6 py-3 rounded-full font-semibold hover:bg-white hover:text-indigo-600 transition"> | |
| How It Works | |
| </button> | |
| </div> | |
| </div> | |
| <div class="md:w-1/2 flex justify-center"> | |
| <div class="relative w-64 h-64 md:w-80 md:h-80"> | |
| <div class="absolute inset-0 bg-indigo-500 rounded-full opacity-20 floating"></div> | |
| <div class="absolute inset-4 bg-indigo-400 rounded-full opacity-30 floating" style="animation-delay: 0.5s;"></div> | |
| <div class="absolute inset-8 bg-indigo-300 rounded-full opacity-40 floating" style="animation-delay: 1s;"></div> | |
| <div class="absolute inset-12 bg-white rounded-full flex items-center justify-center"> | |
| <i class="fas fa-robot text-6xl text-indigo-600"></i> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- AI Chat Interface --> | |
| <section class="py-12 bg-white"> | |
| <div class="container mx-auto px-4"> | |
| <h2 class="text-3xl font-bold text-center mb-12 text-gray-800">Your Personal Streaming Assistant</h2> | |
| <div class="max-w-4xl mx-auto bg-gray-50 rounded-xl shadow-lg overflow-hidden"> | |
| <!-- Chat header --> | |
| <div class="bg-indigo-600 text-white p-4 flex items-center"> | |
| <div class="w-10 h-10 rounded-full bg-indigo-400 flex items-center justify-center mr-3"> | |
| <i class="fas fa-robot"></i> | |
| </div> | |
| <div> | |
| <h3 class="font-semibold">StreamAI Assistant</h3> | |
| <p class="text-xs text-indigo-200">Powered by Cloudflare Workers AI</p> | |
| </div> | |
| <div class="ml-auto flex space-x-2"> | |
| <button class="w-8 h-8 rounded-full bg-indigo-500 hover:bg-indigo-400 flex items-center justify-center"> | |
| <i class="fas fa-ellipsis-h text-sm"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Chat messages --> | |
| <div class="h-96 overflow-y-auto p-4 space-y-4" id="chat-messages"> | |
| <div class="chat-bubble ai-bubble p-4 w-3/4"> | |
| <p>Hi there! 👋 I'm your StreamAI assistant powered by Cloudflare Workers AI. I can recommend movies, TV shows, and other streaming content based on your preferences. What are you in the mood for today?</p> | |
| </div> | |
| </div> | |
| <!-- User input --> | |
| <div class="border-t border-gray-200 p-4 bg-white"> | |
| <div class="flex items-center"> | |
| <input type="text" id="user-input" placeholder="Type your message..." class="flex-1 border border-gray-300 rounded-full py-3 px-4 focus:outline-none focus:ring-2 focus:ring-indigo-500"> | |
| <button id="send-btn" class="ml-3 w-12 h-12 rounded-full bg-indigo-600 text-white hover:bg-indigo-700 flex items-center justify-center transition"> | |
| <i class="fas fa-paper-plane"></i> | |
| </button> | |
| </div> | |
| <div class="mt-2 flex space-x-2"> | |
| <button class="text-xs bg-gray-100 hover:bg-gray-200 px-3 py-1 rounded-full transition" onclick="quickPrompt('Recommend a comedy movie')"> | |
| Comedy | |
| </button> | |
| <button class="text-xs bg-gray-100 hover:bg-gray-200 px-3 py-1 rounded-full transition" onclick="quickPrompt('What should I watch if I feel nostalgic?')"> | |
| Nostalgic | |
| </button> | |
| <button class="text-xs bg-gray-100 hover:bg-gray-200 px-3 py-1 rounded-full transition" onclick="quickPrompt('Suggest a thriller series')"> | |
| Thriller | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Recommended Streams Section --> | |
| <section class="py-12 bg-gray-50"> | |
| <div class="container mx-auto px-4"> | |
| <h2 class="text-3xl font-bold text-center mb-6 text-gray-800">Recommended For You</h2> | |
| <!-- RSS Feed Integration --> | |
| <div class="max-w-4xl mx-auto mb-8 bg-white p-4 rounded-lg shadow"> | |
| <div class="flex items-center mb-2"> | |
| <i class="fas fa-rss-square text-orange-500 mr-2"></i> | |
| <h3 class="font-semibold">Personalized RSS Feed</h3> | |
| </div> | |
| <div class="flex items-center space-x-2 overflow-x-auto pb-2"> | |
| <button class="rss-filter-btn px-3 py-1 bg-indigo-100 text-indigo-700 rounded-full text-sm whitespace-nowrap" data-filter="all"> | |
| All Content | |
| </button> | |
| <button class="rss-filter-btn px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm whitespace-nowrap" data-filter="news"> | |
| News Updates | |
| </button> | |
| <button class="rss-filter-btn px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm whitespace-nowrap" data-filter="trending"> | |
| Trending Now | |
| </button> | |
| <button class="rss-filter-btn px-3 py-1 bg-gray-100 text-gray-700 rounded-full text-sm whitespace-nowrap" data-filter="personalized"> | |
| For You | |
| </button> | |
| </div> | |
| <div id="rss-feed" class="mt-4 space-y-3"> | |
| <!-- RSS items will be loaded here --> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4" id="recommendations-container"> | |
| <!-- Recommendations will be dynamically inserted here --> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- How It Works Section --> | |
| <section class="py-12 bg-white"> | |
| <div class="container mx-auto px-4"> | |
| <h2 class="text-3xl font-bold text-center mb-12 text-gray-800">How StreamAI Works</h2> | |
| <div class="grid md:grid-cols-3 gap-8"> | |
| <div class="text-center p-6 rounded-xl bg-gray-50 hover:shadow-lg transition"> | |
| <div class="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <i class="fas fa-comment-alt text-indigo-600 text-xl"></i> | |
| </div> | |
| <h3 class="text-xl font-semibold mb-2">1. Chat with AI</h3> | |
| <p class="text-gray-600">Tell our AI assistant what you're in the mood for or ask for recommendations based on your preferences.</p> | |
| </div> | |
| <div class="text-center p-6 rounded-xl bg-gray-50 hover:shadow-lg transition"> | |
| <div class="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <i class="fas fa-brain text-indigo-600 text-xl"></i> | |
| </div> | |
| <h3 class="text-xl font-semibold mb-2">2. AI Processing</h3> | |
| <p class="text-gray-600">Our Cloudflare Workers AI analyzes your request using advanced natural language processing.</p> | |
| </div> | |
| <div class="text-center p-6 rounded-xl bg-gray-50 hover:shadow-lg transition"> | |
| <div class="w-16 h-16 bg-indigo-100 rounded-full flex items-center justify-center mx-auto mb-4"> | |
| <i class="fas fa-film text-indigo-600 text-xl"></i> | |
| </div> | |
| <h3 class="text-xl font-semibold mb-2">3. Get Recommendations</h3> | |
| <p class="text-gray-600">Receive personalized streaming suggestions with direct links to watch on your favorite platforms.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Video Production Section --> | |
| <section class="py-12 bg-gray-50"> | |
| <div class="container mx-auto px-4"> | |
| <h2 class="text-3xl font-bold text-center mb-12 text-gray-800">Create Your Own Content</h2> | |
| <div class="max-w-4xl mx-auto bg-white rounded-xl shadow-lg overflow-hidden"> | |
| <div class="p-8"> | |
| <div class="flex items-center mb-6"> | |
| <div class="w-14 h-14 rounded-full bg-gradient-to-r from-red-500 to-orange-500 flex items-center justify-center text-white mr-4"> | |
| <i class="fas fa-video text-2xl"></i> | |
| </div> | |
| <div> | |
| <h3 class="text-2xl font-bold">Video Production Tools</h3> | |
| <p class="text-gray-600">Record, edit, and share your own streaming content</p> | |
| </div> | |
| </div> | |
| <div class="grid md:grid-cols-2 gap-8"> | |
| <div> | |
| <h4 class="font-semibold text-lg mb-3">Record Live Clips</h4> | |
| <p class="text-gray-600 mb-4">Easily capture 5-second clips of your screen and audio to create highlight reels or content snippets.</p> | |
| <ul class="space-y-2 text-sm text-gray-600"> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Automatically segmented into manageable clips</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Screen and audio recording with one click</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Preview clips before adding to your production</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Share with Bluetooth-connected groups</span> | |
| </li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h4 class="font-semibold text-lg mb-3">Edit & Combine</h4> | |
| <p class="text-gray-600 mb-4">Combine your clips with smooth transitions to create professional-looking videos.</p> | |
| <ul class="space-y-2 text-sm text-gray-600"> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Multiple transition effects available</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Add titles and descriptions to your videos</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Export in multiple formats for sharing</span> | |
| </li> | |
| <li class="flex items-start"> | |
| <i class="fas fa-check-circle text-green-500 mt-1 mr-2"></i> | |
| <span>Benchmark your content against group submissions</span> | |
| </li> | |
| </ul> | |
| </div> | |
| </div> | |
| <div class="mt-8 pt-6 border-t border-gray-200"> | |
| <h4 class="font-semibold text-lg mb-4">Try It Now</h4> | |
| <p class="text-gray-600 mb-4">Click the video production button in the bottom right corner to start creating your own content.</p> | |
| <div class="flex items-center text-sm text-indigo-600"> | |
| <i class="fas fa-info-circle mr-2"></i> | |
| <span>You'll need to grant screen recording permissions when prompted</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Footer --> | |
| <footer class="bg-gray-800 text-white py-12"> | |
| <div class="container mx-auto px-4"> | |
| <div class="grid md:grid-cols-4 gap-8"> | |
| <div> | |
| <h3 class="text-xl font-bold mb-4 flex items-center"> | |
| <i class="fas fa-stream mr-2"></i> StreamAI | |
| </h3> | |
| <p class="text-gray-400">Your personal streaming assistant powered by Cloudflare Workers AI technology.</p> | |
| </div> | |
| <div> | |
| <h4 class="font-semibold mb-4">Quick Links</h4> | |
| <ul class="space-y-2"> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Home</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Features</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Pricing</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">FAQ</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h4 class="font-semibold mb-4">Streaming Platforms</h4> | |
| <ul class="space-y-2"> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Netflix</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Amazon Prime</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">Disney+</a></li> | |
| <li><a href="#" class="text-gray-400 hover:text-white transition">HBO Max</a></li> | |
| <li><a href="https://smplus.vhx.tv" target="_blank" class="text-gray-400 hover:text-white transition">SMPlus VHX</a></li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h4 class="font-semibold mb-4">Connect With Us</h4> | |
| <div class="flex space-x-4"> | |
| <a href="#" class="w-10 h-10 rounded-full bg-gray-700 hover:bg-indigo-600 flex items-center justify-center transition"> | |
| <i class="fab fa-twitter"></i> | |
| </a> | |
| <a href="#" class="w-10 h-10 rounded-full bg-gray-700 hover:bg-indigo-600 flex items-center justify-center transition"> | |
| <i class="fab fa-facebook-f"></i> | |
| </a> | |
| <a href="#" class="w-10 h-10 rounded-full bg-gray-700 hover:bg-indigo-600 flex items-center justify-center transition"> | |
| <i class="fab fa-instagram"></i> | |
| </a> | |
| <a href="#" class="w-10 h-10 rounded-full bg-gray-700 hover:bg-indigo-600 flex items-center justify-center transition"> | |
| <i class="fab fa-discord"></i> | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="border-t border-gray-700 mt-8 pt-8 text-center text-gray-400"> | |
| <p>© 2023 StreamAI. All rights reserved.</p> | |
| </div> | |
| </div> | |
| </footer> | |
| <script> | |
| // 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 = `<p>${userInput.value}</p>`; | |
| 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 = '<div class="typing-indicator"><span></span><span></span><span></span></div>'; | |
| chatMessages.appendChild(typingIndicator); | |
| // Scroll to bottom | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| // Save user message | |
| const userMessageText = userInput.value; | |
| userInput.value = ''; | |
| try { | |
| // Call Cloudflare Workers AI | |
| const aiResponse = await queryCloudflareAI(userMessageText); | |
| // Remove typing indicator | |
| chatMessages.removeChild(typingIndicator); | |
| // Add AI response to chat | |
| const aiMessage = document.createElement('div'); | |
| aiMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in'; | |
| aiMessage.innerHTML = `<p>${aiResponse}</p>`; | |
| 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 = `<p class="text-red-500">Sorry, I'm having trouble connecting to the AI service. Please try again later.</p>`; | |
| 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 = ` | |
| <div class="mb-3"> | |
| <div class="flex justify-between items-start"> | |
| <h3 class="font-bold text-base">${item.title}</h3> | |
| <span class="bg-yellow-100 text-yellow-800 text-xs px-2 py-1 rounded-full flex items-center"> | |
| <i class="fas fa-star text-yellow-500 mr-1 text-xs"></i> ${item.rating} | |
| </span> | |
| </div> | |
| <p class="text-gray-600 text-xs mb-1">${item.type} • ${item.genre} • ${item.year}</p> | |
| <div class="text-xs text-indigo-600 mb-2">${item.platform}</div> | |
| </div> | |
| <p class="text-gray-700 text-sm mb-4">${item.description}</p> | |
| <div class="flex justify-between items-center"> | |
| <button class="text-indigo-600 hover:text-indigo-800 text-xs font-medium" onclick="saveShow('${item.title}', '${item.broadcastTime}')"> | |
| <i class="far fa-bookmark mr-1"></i> Save | |
| </button> | |
| <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"> | |
| <i class="fas fa-play mr-1"></i> Watch | |
| </a> | |
| </div> | |
| `; | |
| 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 = ` | |
| <h4 class="font-medium text-sm mb-1">${item.title}</h4> | |
| <div class="flex items-center text-xs text-gray-500 mb-2"> | |
| <span>${item.source}</span> | |
| <span class="mx-2">•</span> | |
| <span>${item.time}</span> | |
| </div> | |
| <p class="text-xs text-gray-700">${item.excerpt}</p> | |
| `; | |
| 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 = '<video autoplay muted></video>'; | |
| 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 = ` | |
| <div class="clip-preview-placeholder"> | |
| <i class="fas fa-video"></i> | |
| <p>Preview will appear here</p> | |
| </div> | |
| `; | |
| // Reset progress bar | |
| document.getElementById('progress-fill').style.width = '0%'; | |
| // Update UI | |
| document.getElementById('recording-indicator').classList.add('hidden'); | |
| document.getElementById('start-recording').classList.remove('hidden'); | |
| document.getElementById('stop-recording').classList.add('hidden'); | |
| showNotification("Recording Stopped", `Captured ${recordedClips.length} clips ready for production`); | |
| } | |
| // Update clips list | |
| function updateClipsList() { | |
| const container = document.getElementById('clips-container'); | |
| container.innerHTML = ''; | |
| if (recordedClips.length === 0) { | |
| container.innerHTML = '<p class="text-gray-500 text-sm">Clips will appear here...</p>'; | |
| return; | |
| } | |
| recordedClips.forEach((clip, index) => { | |
| const clipItem = document.createElement('div'); | |
| clipItem.className = 'clip-item'; | |
| clipItem.innerHTML = ` | |
| <i class="fas fa-video text-gray-500 mr-2"></i> | |
| <span class="text-sm flex-1">Clip ${index + 1}</span> | |
| <span class="text-xs text-gray-500">${clip.timestamp}</span> | |
| `; | |
| // Add click handler to preview clip | |
| clipItem.addEventListener('click', () => { | |
| const preview = document.getElementById('clip-preview'); | |
| preview.innerHTML = '<video controls></video>'; | |
| 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 = ` | |
| <div class="clip-preview-placeholder"> | |
| <i class="fas fa-video"></i> | |
| <p>Preview will appear here</p> | |
| </div> | |
| `; | |
| showNotification("Clips Cleared", "All recorded clips have been removed"); | |
| } | |
| // Generate short video from clips | |
| function generateShortVideo() { | |
| if (recordedClips.length === 0) { | |
| showNotification("No Clips", "Please record some clips first"); | |
| return; | |
| } | |
| // In a real app, this would use a video editing library to combine clips | |
| // and sync with audio. For this demo, we'll simulate the process. | |
| showNotification("Video Generation", "Processing your clips into a short video..."); | |
| setTimeout(() => { | |
| // Simulate processing time | |
| const videoUrl = URL.createObjectURL(new Blob(["Simulated video content"], { type: 'video/mp4' })); | |
| // Create download link | |
| const a = document.createElement('a'); | |
| a.href = videoUrl; | |
| a.download = 'streamai-short.mp4'; | |
| a.click(); | |
| showNotification("Video Ready", "Your short video has been generated and downloaded"); | |
| }, 3000); | |
| } | |
| // Connect to Bluetooth devices | |
| async function connectBluetooth() { | |
| try { | |
| // Request Bluetooth device | |
| bluetoothDevice = await navigator.bluetooth.requestDevice({ | |
| acceptAllDevices: true, | |
| optionalServices: ['generic_access'] | |
| }); | |
| // Connect to the GATT Server | |
| bluetoothServer = await bluetoothDevice.gatt.connect(); | |
| // Get the service | |
| bluetoothService = await bluetoothServer.getPrimaryService('generic_access'); | |
| // Get the characteristic | |
| bluetoothCharacteristic = await bluetoothService.getCharacteristic('device_name'); | |
| // Update connection status | |
| isBluetoothConnected = true; | |
| const statusElement = document.getElementById('connection-status'); | |
| statusElement.innerHTML = ` | |
| <i class="fas fa-circle bluetooth-connected mr-1"></i> | |
| <span>Connected to ${bluetoothDevice.name || 'device'}</span> | |
| `; | |
| // Simulate discovering group members | |
| groupMembers = [ | |
| { name: "User" + Math.floor(Math.random() * 1000), device: "Device 1" }, | |
| { name: "User" + Math.floor(Math.random() * 1000), device: "Device 2" }, | |
| { name: "User" + Math.floor(Math.random() * 1000), device: "Device 3" } | |
| ]; | |
| showNotification("Bluetooth Connected", `Connected to ${bluetoothDevice.name}. Found ${groupMembers.length} group members.`); | |
| } catch (error) { | |
| console.error("Bluetooth connection error:", error); | |
| isBluetoothConnected = false; | |
| document.getElementById('connection-status').innerHTML = ` | |
| <i class="fas fa-circle bluetooth-disconnected mr-1"></i> | |
| <span>Not connected</span> | |
| `; | |
| showNotification("Connection Failed", "Could not connect to Bluetooth device"); | |
| } | |
| } | |
| // Share clips with Bluetooth group | |
| async function shareClipsWithGroup() { | |
| if (!isBluetoothConnected) { | |
| showNotification("Not Connected", "Please connect to Bluetooth first"); | |
| return; | |
| } | |
| if (recordedClips.length === 0) { | |
| showNotification("No Clips", "Please record some clips first"); | |
| return; | |
| } | |
| // In a real app, this would actually send the clips over Bluetooth | |
| // For this demo, we'll simulate the sharing process | |
| showNotification("Sharing Clips", "Sharing your clips with the group..."); | |
| // Simulate sharing delay | |
| setTimeout(() => { | |
| // Add metadata to clips and add to shared clips | |
| recordedClips.forEach(clip => { | |
| const sharedClip = { | |
| ...clip, | |
| sharedBy: username, | |
| category: currentCategory, | |
| timestamp: new Date().toLocaleTimeString(), | |
| likes: Math.floor(Math.random() * 10), | |
| views: Math.floor(Math.random() * 50) | |
| }; | |
| sharedClips.push(sharedClip); | |
| }); | |
| // Update shared clips display | |
| updateSharedClipsList(); | |
| showNotification("Clips Shared", `Shared ${recordedClips.length} clips with ${groupMembers.length} group members`); | |
| }, 2000); | |
| } | |
| // Update shared clips list | |
| function updateSharedClipsList() { | |
| const container = document.getElementById('shared-clips-container'); | |
| container.innerHTML = ''; | |
| if (sharedClips.length === 0) { | |
| container.innerHTML = '<p class="text-gray-500 text-sm">Shared clips will appear here...</p>'; | |
| return; | |
| } | |
| sharedClips.forEach((clip, index) => { | |
| const clipItem = document.createElement('div'); | |
| clipItem.className = 'clip-item'; | |
| clipItem.innerHTML = ` | |
| <i class="fas fa-video text-gray-500 mr-2"></i> | |
| <span class="text-sm flex-1">Clip ${index + 1}</span> | |
| <span class="user-tag">${clip.sharedBy}</span> | |
| <span class="category-tag">${clip.category}</span> | |
| `; | |
| // Add click handler to preview clip | |
| clipItem.addEventListener('click', () => { | |
| const preview = document.getElementById('clip-preview'); | |
| preview.innerHTML = '<video controls></video>'; | |
| 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 = '<p class="text-gray-500 text-sm">Rankings will appear here when available...</p>'; | |
| return; | |
| } | |
| </html> |