privateuserh commited on
Commit
6a27c33
·
verified ·
1 Parent(s): 359a21e

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +5 -1113
index.html CHANGED
@@ -6,253 +6,10 @@
6
  <title>StreamAI - Personalized Streaming Recommendations</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
- <style>
10
- @keyframes float {
11
- 0% { transform: translateY(0px); }
12
- 50% { transform: translateY(-10px); }
13
- 100% { transform: translateY(0px); }
14
- }
15
- .floating {
16
- animation: float 6s ease-in-out infinite;
17
- }
18
- .gradient-bg {
19
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
20
- }
21
- .chat-bubble {
22
- border-radius: 20px;
23
- position: relative;
24
- max-width: 80%;
25
- }
26
- .user-bubble {
27
- background-color: #4f46e5;
28
- color: white;
29
- margin-left: auto;
30
- border-bottom-right-radius: 5px;
31
- }
32
- .ai-bubble {
33
- background-color: #f3f4f6;
34
- color: #1f2937;
35
- margin-right: auto;
36
- border-bottom-left-radius: 5px;
37
- }
38
- .typing-indicator span {
39
- display: inline-block;
40
- width: 8px;
41
- height: 8px;
42
- border-radius: 50%;
43
- background-color: #9ca3af;
44
- margin: 0 2px;
45
- }
46
- .typing-indicator span:nth-child(1) {
47
- animation: bounce 1s infinite;
48
- }
49
- .typing-indicator span:nth-child(2) {
50
- animation: bounce 1s infinite 0.2s;
51
- }
52
- .typing-indicator span:nth-child(3) {
53
- animation: bounce 1s infinite 0.4s;
54
- }
55
- @keyframes bounce {
56
- 0%, 100% { transform: translateY(0); }
57
- 50% { transform: translateY(-5px); }
58
- }
59
- .stream-card:hover {
60
- transform: translateY(-5px);
61
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
62
- }
63
- .fade-in {
64
- animation: fadeIn 0.5s ease-in;
65
- }
66
- @keyframes fadeIn {
67
- from { opacity: 0; }
68
- to { opacity: 1; }
69
- }
70
- .notification {
71
- position: fixed;
72
- bottom: 20px;
73
- right: 20px;
74
- background: #4f46e5;
75
- color: white;
76
- padding: 15px 25px;
77
- border-radius: 8px;
78
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
79
- transform: translateX(120%);
80
- transition: transform 0.3s ease-out;
81
- z-index: 1000;
82
- }
83
- .notification.show {
84
- transform: translateX(0);
85
- }
86
- .production-button {
87
- position: fixed;
88
- bottom: 30px;
89
- right: 30px;
90
- width: 60px;
91
- height: 60px;
92
- border-radius: 50%;
93
- background: linear-gradient(135deg, #ff5e62 0%, #ff9966 100%);
94
- color: white;
95
- display: flex;
96
- align-items: center;
97
- justify-content: center;
98
- box-shadow: 0 10px 25px rgba(255, 94, 98, 0.3);
99
- cursor: pointer;
100
- z-index: 999;
101
- transition: all 0.3s ease;
102
- }
103
- .production-button:hover {
104
- transform: scale(1.1);
105
- box-shadow: 0 15px 30px rgba(255, 94, 98, 0.4);
106
- }
107
- .production-panel {
108
- position: fixed;
109
- bottom: 110px;
110
- right: 30px;
111
- width: 350px;
112
- background: white;
113
- border-radius: 12px;
114
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
115
- padding: 20px;
116
- z-index: 998;
117
- transform: translateY(20px);
118
- opacity: 0;
119
- pointer-events: none;
120
- transition: all 0.3s ease;
121
- }
122
- .production-panel.open {
123
- transform: translateY(0);
124
- opacity: 1;
125
- pointer-events: all;
126
- }
127
- .recording-indicator {
128
- position: absolute;
129
- top: -10px;
130
- right: -10px;
131
- width: 20px;
132
- height: 20px;
133
- border-radius: 50%;
134
- background: #ff5e62;
135
- animation: pulse 1.5s infinite;
136
- }
137
- @keyframes pulse {
138
- 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 94, 98, 0.7); }
139
- 70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(255, 94, 98, 0); }
140
- 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 94, 98, 0); }
141
- }
142
- .clip-item {
143
- display: flex;
144
- align-items: center;
145
- padding: 8px 0;
146
- border-bottom: 1px solid #eee;
147
- cursor: pointer;
148
- }
149
- .clip-item:hover {
150
- background-color: #f9fafb;
151
- }
152
- .clip-item:last-child {
153
- border-bottom: none;
154
- }
155
- .clip-preview {
156
- width: 100%;
157
- height: 180px;
158
- background-color: #f3f4f6;
159
- border-radius: 8px;
160
- display: flex;
161
- align-items: center;
162
- justify-content: center;
163
- margin-bottom: 15px;
164
- position: relative;
165
- overflow: hidden;
166
- }
167
- .clip-preview video {
168
- width: 100%;
169
- height: 100%;
170
- object-fit: cover;
171
- }
172
- .clip-preview-placeholder {
173
- text-align: center;
174
- color: #6b7280;
175
- }
176
- .clip-preview-placeholder i {
177
- font-size: 40px;
178
- margin-bottom: 10px;
179
- display: block;
180
- }
181
- .progress-bar {
182
- height: 4px;
183
- background-color: #e5e7eb;
184
- border-radius: 2px;
185
- overflow: hidden;
186
- margin-top: 10px;
187
- }
188
- .progress-fill {
189
- height: 100%;
190
- background-color: #4f46e5;
191
- width: 0%;
192
- transition: width 0.3s ease;
193
- }
194
- .tab {
195
- padding: 8px 16px;
196
- border-radius: 20px;
197
- font-size: 14px;
198
- cursor: pointer;
199
- margin-right: 8px;
200
- }
201
- .tab.active {
202
- background-color: #4f46e5;
203
- color: white;
204
- }
205
- .tab.inactive {
206
- background-color: #f3f4f6;
207
- color: #6b7280;
208
- }
209
- .rank-badge {
210
- width: 20px;
211
- height: 20px;
212
- border-radius: 50%;
213
- display: flex;
214
- align-items: center;
215
- justify-content: center;
216
- font-size: 10px;
217
- font-weight: bold;
218
- margin-left: 8px;
219
- }
220
- .rank-1 {
221
- background-color: #f59e0b;
222
- color: white;
223
- }
224
- .rank-2 {
225
- background-color: #6b7280;
226
- color: white;
227
- }
228
- .rank-3 {
229
- background-color: #92400e;
230
- color: white;
231
- }
232
- .bluetooth-connected {
233
- color: #3b82f6;
234
- animation: pulse 2s infinite;
235
- }
236
- .bluetooth-disconnected {
237
- color: #6b7280;
238
- }
239
- .user-tag {
240
- background-color: #e5e7eb;
241
- color: #4b5563;
242
- padding: 2px 6px;
243
- border-radius: 4px;
244
- font-size: 10px;
245
- margin-left: 4px;
246
- }
247
- .category-tag {
248
- background-color: #dbeafe;
249
- color: #1e40af;
250
- padding: 2px 6px;
251
- border-radius: 4px;
252
- font-size: 10px;
253
- margin-left: 4px;
254
- }
255
- </style>
256
  </head>
257
  <body class="bg-gray-100 font-sans">
258
  <!-- Notification -->
@@ -695,870 +452,5 @@
695
  </div>
696
  </div>
697
  </footer>
698
-
699
- <script>
700
- // Configuration for Cloudflare Workers AI
701
- const AI_CONFIG = {
702
- accountId: 'oFD0IMs0aV8eKMMMdTEF2zRQmtzvKMH43LX5ZWUJ',
703
- gatewayId: 'streamai_gateway',
704
- apiToken: 'masked_for_security', // In a real app, this would be handled server-side
705
- model: '@cf/meta/llama-2-7b-chat-int8'
706
- };
707
-
708
- // Sample streaming data with SMPlus VHX added to the top
709
- const streamingData = [
710
- {
711
- title: "SMPlus Exclusive Series",
712
- type: "TV Series",
713
- genre: "Drama, Action",
714
- platform: "SMPlus VHX",
715
- rating: "4.9",
716
- year: "2023",
717
- description: "An exclusive action-packed drama series only available on SMPlus VHX.",
718
- broadcastTime: "2023-12-15T20:00:00"
719
- },
720
- {
721
- title: "The Grand Adventure",
722
- type: "Movie",
723
- genre: "Adventure, Comedy",
724
- platform: "Netflix",
725
- rating: "4.8",
726
- year: "2022",
727
- description: "A hilarious journey across continents with unexpected twists.",
728
- broadcastTime: "2023-12-10T19:30:00"
729
- },
730
- {
731
- title: "Dark Secrets",
732
- type: "TV Series",
733
- genre: "Drama, Thriller",
734
- platform: "HBO Max",
735
- rating: "4.7",
736
- year: "2021",
737
- description: "A small town's dark past resurfaces with shocking revelations.",
738
- broadcastTime: "2023-12-12T21:00:00"
739
- },
740
- {
741
- title: "Space Explorers",
742
- type: "Documentary",
743
- genre: "Science, Space",
744
- platform: "Disney+",
745
- rating: "4.9",
746
- year: "2023",
747
- description: "The latest discoveries from the frontiers of space exploration.",
748
- broadcastTime: "2023-12-14T18:00:00"
749
- },
750
- {
751
- title: "Romantic Getaway",
752
- type: "Movie",
753
- genre: "Romance, Comedy",
754
- platform: "Amazon Prime",
755
- rating: "4.5",
756
- year: "2021",
757
- description: "Two strangers find love during an unexpected vacation.",
758
- broadcastTime: "2023-12-16T20:30:00"
759
- },
760
- {
761
- title: "Tech Today",
762
- type: "News Show",
763
- genre: "Technology, News",
764
- platform: "SMPlus VHX",
765
- rating: "4.6",
766
- year: "2023",
767
- description: "Daily tech news and gadget reviews from around the world.",
768
- broadcastTime: "2023-12-17T09:00:00"
769
- },
770
- {
771
- title: "Cooking Masters",
772
- type: "Reality Show",
773
- genre: "Food, Competition",
774
- platform: "Netflix",
775
- rating: "4.7",
776
- year: "2023",
777
- description: "Top chefs compete in intense culinary challenges.",
778
- broadcastTime: "2023-12-18T20:00:00"
779
- },
780
- {
781
- title: "History Unearthed",
782
- type: "Documentary",
783
- genre: "History, Education",
784
- platform: "HBO Max",
785
- rating: "4.8",
786
- year: "2023",
787
- description: "Fascinating historical discoveries and their modern implications.",
788
- broadcastTime: "2023-12-19T21:00:00"
789
- }
790
- ];
791
-
792
- // Sample RSS feed data
793
- const rssFeedData = {
794
- all: [
795
- {
796
- title: "New Episode: SMPlus Exclusive Series",
797
- source: "SMPlus VHX",
798
- time: "2 hours ago",
799
- excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.",
800
- category: "personalized"
801
- },
802
- {
803
- title: "Trending: The Grand Adventure hits #1",
804
- source: "Netflix",
805
- time: "5 hours ago",
806
- excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.",
807
- category: "trending"
808
- },
809
- {
810
- title: "Breaking: New streaming partnership announced",
811
- source: "Streaming News",
812
- time: "1 day ago",
813
- excerpt: "Major platforms announce new content sharing agreement starting next month.",
814
- category: "news"
815
- },
816
- {
817
- title: "Recommended for you: Space Explorers",
818
- source: "Disney+",
819
- time: "1 day ago",
820
- excerpt: "Based on your interest in science documentaries, we recommend this new series.",
821
- category: "personalized"
822
- },
823
- {
824
- title: "Upcoming: Romantic Getaway special event",
825
- source: "Amazon Prime",
826
- time: "2 days ago",
827
- excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.",
828
- category: "trending"
829
- }
830
- ],
831
- news: [
832
- {
833
- title: "Breaking: New streaming partnership announced",
834
- source: "Streaming News",
835
- time: "1 day ago",
836
- excerpt: "Major platforms announce new content sharing agreement starting next month.",
837
- category: "news"
838
- },
839
- {
840
- title: "Streaming industry report Q4 2023",
841
- source: "Tech Insights",
842
- time: "3 days ago",
843
- excerpt: "Latest statistics show continued growth in streaming subscriptions worldwide.",
844
- category: "news"
845
- }
846
- ],
847
- trending: [
848
- {
849
- title: "Trending: The Grand Adventure hits #1",
850
- source: "Netflix",
851
- time: "5 hours ago",
852
- excerpt: "The comedy adventure movie is now the most-watched title on Netflix this week.",
853
- category: "trending"
854
- },
855
- {
856
- title: "Upcoming: Romantic Getaway special event",
857
- source: "Amazon Prime",
858
- time: "2 days ago",
859
- excerpt: "Join the cast for a live Q&A before the movie premiere this weekend.",
860
- category: "trending"
861
- }
862
- ],
863
- personalized: [
864
- {
865
- title: "New Episode: SMPlus Exclusive Series",
866
- source: "SMPlus VHX",
867
- time: "2 hours ago",
868
- excerpt: "The latest episode of our exclusive series is now streaming with intense action scenes.",
869
- category: "personalized"
870
- },
871
- {
872
- title: "Recommended for you: Space Explorers",
873
- source: "Disney+",
874
- time: "1 day ago",
875
- excerpt: "Based on your interest in science documentaries, we recommend this new series.",
876
- category: "personalized"
877
- }
878
- ]
879
- };
880
-
881
- // Video production variables
882
- let isRecording = false;
883
- let recordingInterval;
884
- let recordedClips = [];
885
- let mediaRecorder;
886
- let audioContext;
887
- let audioStream;
888
- let videoStream;
889
- let currentClipTime = 0;
890
- let currentClipInterval;
891
-
892
- // Bluetooth sharing variables
893
- let isBluetoothConnected = false;
894
- let bluetoothDevice;
895
- let bluetoothServer;
896
- let bluetoothService;
897
- let bluetoothCharacteristic;
898
- let sharedClips = [];
899
- let groupMembers = [];
900
- let username = "User" + Math.floor(Math.random() * 1000);
901
- let currentCategory = "general";
902
-
903
- // Initialize chat
904
- document.addEventListener('DOMContentLoaded', function() {
905
- const sendBtn = document.getElementById('send-btn');
906
- const userInput = document.getElementById('user-input');
907
- const chatMessages = document.getElementById('chat-messages');
908
- const productionButton = document.getElementById('production-button');
909
- const productionPanel = document.getElementById('production-panel');
910
- const startRecordingBtn = document.getElementById('start-recording');
911
- const stopRecordingBtn = document.getElementById('stop-recording');
912
- const generateVideoBtn = document.getElementById('generate-video');
913
- const clearClipsBtn = document.getElementById('clear-clips');
914
- const rssFilterBtns = document.querySelectorAll('.rss-filter-btn');
915
- const recordTab = document.getElementById('record-tab');
916
- const editTab = document.getElementById('edit-tab');
917
- const shareTab = document.getElementById('share-tab');
918
- const rankTab = document.getElementById('rank-tab');
919
- const recordSection = document.getElementById('record-section');
920
- const editSection = document.getElementById('edit-section');
921
- const shareSection = document.getElementById('share-section');
922
- const rankSection = document.getElementById('rank-section');
923
- const bluetoothConnectBtn = document.getElementById('bluetooth-connect');
924
- const shareClipsBtn = document.getElementById('share-clips');
925
- const usernameInput = document.getElementById('username');
926
- const contentCategorySelect = document.getElementById('content-category');
927
- const rankingAlgorithmSelect = document.getElementById('ranking-algorithm');
928
-
929
- // Set default username
930
- usernameInput.value = username;
931
-
932
- // Load sample recommendations
933
- loadRecommendations();
934
-
935
- // Load RSS feed
936
- loadRSSFeed('all');
937
-
938
- // Send message on button click
939
- sendBtn.addEventListener('click', sendMessage);
940
-
941
- // Send message on Enter key
942
- userInput.addEventListener('keypress', function(e) {
943
- if (e.key === 'Enter') {
944
- sendMessage();
945
- }
946
- });
947
-
948
- // Toggle production panel
949
- productionButton.addEventListener('click', function() {
950
- productionPanel.classList.toggle('open');
951
- });
952
-
953
- // Start recording
954
- startRecordingBtn.addEventListener('click', startRecording);
955
-
956
- // Stop recording
957
- stopRecordingBtn.addEventListener('click', stopRecording);
958
-
959
- // Generate video
960
- generateVideoBtn.addEventListener('click', generateShortVideo);
961
-
962
- // Clear clips
963
- clearClipsBtn.addEventListener('click', clearClips);
964
-
965
- // Filter RSS feed
966
- rssFilterBtns.forEach(btn => {
967
- btn.addEventListener('click', function() {
968
- // Update active button
969
- rssFilterBtns.forEach(b => {
970
- b.classList.remove('bg-indigo-100', 'text-indigo-700');
971
- b.classList.add('bg-gray-100', 'text-gray-700');
972
- });
973
- this.classList.remove('bg-gray-100', 'text-gray-700');
974
- this.classList.add('bg-indigo-100', 'text-indigo-700');
975
-
976
- // Load filtered feed
977
- loadRSSFeed(this.dataset.filter);
978
- });
979
- });
980
-
981
- // Switch between record and edit tabs
982
- recordTab.addEventListener('click', function() {
983
- recordTab.classList.remove('inactive');
984
- recordTab.classList.add('active');
985
- editTab.classList.remove('active');
986
- editTab.classList.add('inactive');
987
- shareTab.classList.remove('active');
988
- shareTab.classList.add('inactive');
989
- rankTab.classList.remove('active');
990
- rankTab.classList.add('inactive');
991
- recordSection.classList.remove('hidden');
992
- editSection.classList.add('hidden');
993
- shareSection.classList.add('hidden');
994
- rankSection.classList.add('hidden');
995
- });
996
-
997
- editTab.addEventListener('click', function() {
998
- if (recordedClips.length === 0) {
999
- showNotification("No Clips", "Record some clips first to edit them");
1000
- return;
1001
- }
1002
-
1003
- editTab.classList.remove('inactive');
1004
- editTab.classList.add('active');
1005
- recordTab.classList.remove('active');
1006
- recordTab.classList.add('inactive');
1007
- shareTab.classList.remove('active');
1008
- shareTab.classList.add('inactive');
1009
- rankTab.classList.remove('active');
1010
- rankTab.classList.add('inactive');
1011
- recordSection.classList.add('hidden');
1012
- editSection.classList.remove('hidden');
1013
- shareSection.classList.add('hidden');
1014
- rankSection.classList.add('hidden');
1015
- });
1016
-
1017
- shareTab.addEventListener('click', function() {
1018
- shareTab.classList.remove('inactive');
1019
- shareTab.classList.add('active');
1020
- recordTab.classList.remove('active');
1021
- recordTab.classList.add('inactive');
1022
- editTab.classList.remove('active');
1023
- editTab.classList.add('inactive');
1024
- rankTab.classList.remove('active');
1025
- rankTab.classList.add('inactive');
1026
- recordSection.classList.add('hidden');
1027
- editSection.classList.add('hidden');
1028
- shareSection.classList.remove('hidden');
1029
- rankSection.classList.add('hidden');
1030
- });
1031
-
1032
- rankTab.addEventListener('click', function() {
1033
- if (sharedClips.length === 0) {
1034
- showNotification("No Shared Clips", "Share some clips first to see rankings");
1035
- return;
1036
- }
1037
-
1038
- rankTab.classList.remove('inactive');
1039
- rankTab.classList.add('active');
1040
- recordTab.classList.remove('active');
1041
- recordTab.classList.add('inactive');
1042
- editTab.classList.remove('active');
1043
- editTab.classList.add('inactive');
1044
- shareTab.classList.remove('active');
1045
- shareTab.classList.add('inactive');
1046
- recordSection.classList.add('hidden');
1047
- editSection.classList.add('hidden');
1048
- shareSection.classList.add('hidden');
1049
- rankSection.classList.remove('hidden');
1050
-
1051
- // Update rankings when tab is opened
1052
- updateRankings();
1053
- });
1054
-
1055
- // Connect to Bluetooth
1056
- bluetoothConnectBtn.addEventListener('click', connectBluetooth);
1057
-
1058
- // Share clips
1059
- shareClipsBtn.addEventListener('click', shareClipsWithGroup);
1060
-
1061
- // Update username when changed
1062
- usernameInput.addEventListener('change', function() {
1063
- username = this.value || "User" + Math.floor(Math.random() * 1000);
1064
- });
1065
-
1066
- // Update category when changed
1067
- contentCategorySelect.addEventListener('change', function() {
1068
- currentCategory = this.value;
1069
- });
1070
-
1071
- // Update rankings when algorithm changes
1072
- rankingAlgorithmSelect.addEventListener('change', updateRankings);
1073
- });
1074
-
1075
- // Show notification
1076
- function showNotification(title, message) {
1077
- const notification = document.getElementById('notification');
1078
- const titleElement = document.getElementById('notification-title');
1079
- const messageElement = document.getElementById('notification-message');
1080
-
1081
- titleElement.textContent = title;
1082
- messageElement.textContent = message;
1083
-
1084
- notification.classList.remove('hidden');
1085
- notification.classList.add('show');
1086
-
1087
- setTimeout(() => {
1088
- notification.classList.remove('show');
1089
- setTimeout(() => notification.classList.add('hidden'), 300);
1090
- }, 3000);
1091
- }
1092
-
1093
- // Quick prompt buttons
1094
- function quickPrompt(prompt) {
1095
- document.getElementById('user-input').value = prompt;
1096
- sendMessage();
1097
- }
1098
-
1099
- // Send message to AI
1100
- async function sendMessage() {
1101
- const userInput = document.getElementById('user-input');
1102
- const chatMessages = document.getElementById('chat-messages');
1103
-
1104
- if (userInput.value.trim() === '') return;
1105
-
1106
- // Add user message to chat
1107
- const userMessage = document.createElement('div');
1108
- userMessage.className = 'chat-bubble user-bubble p-4 w-3/4 ml-auto mb-4 fade-in';
1109
- userMessage.innerHTML = `<p>${userInput.value}</p>`;
1110
- chatMessages.appendChild(userMessage);
1111
-
1112
- // Show typing indicator
1113
- const typingIndicator = document.createElement('div');
1114
- typingIndicator.className = 'chat-bubble ai-bubble p-4 w-1/2 mb-4';
1115
- typingIndicator.innerHTML = '<div class="typing-indicator"><span></span><span></span><span></span></div>';
1116
- chatMessages.appendChild(typingIndicator);
1117
-
1118
- // Scroll to bottom
1119
- chatMessages.scrollTop = chatMessages.scrollHeight;
1120
-
1121
- // Save user message
1122
- const userMessageText = userInput.value;
1123
- userInput.value = '';
1124
-
1125
- try {
1126
- // Call Cloudflare Workers AI
1127
- const aiResponse = await queryCloudflareAI(userMessageText);
1128
-
1129
- // Remove typing indicator
1130
- chatMessages.removeChild(typingIndicator);
1131
-
1132
- // Add AI response to chat
1133
- const aiMessage = document.createElement('div');
1134
- aiMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in';
1135
- aiMessage.innerHTML = `<p>${aiResponse}</p>`;
1136
- chatMessages.appendChild(aiMessage);
1137
-
1138
- // Update recommendations based on AI response
1139
- updateRecommendationsFromAI(aiResponse);
1140
-
1141
- } catch (error) {
1142
- // Remove typing indicator
1143
- chatMessages.removeChild(typingIndicator);
1144
-
1145
- // Show error message
1146
- const errorMessage = document.createElement('div');
1147
- errorMessage.className = 'chat-bubble ai-bubble p-4 w-3/4 fade-in';
1148
- errorMessage.innerHTML = `<p class="text-red-500">Sorry, I'm having trouble connecting to the AI service. Please try again later.</p>`;
1149
- chatMessages.appendChild(errorMessage);
1150
- }
1151
-
1152
- // Scroll to bottom
1153
- chatMessages.scrollTop = chatMessages.scrollHeight;
1154
- }
1155
-
1156
- // Query Cloudflare Workers AI
1157
- async function queryCloudflareAI(prompt) {
1158
- // In a production environment, this would be handled by a backend service
1159
- // to keep the API token secure. For this demo, we'll simulate the response.
1160
-
1161
- console.log(`[DEBUG] Would call Cloudflare AI with prompt: "${prompt}"`);
1162
-
1163
- // Simulate API call delay
1164
- await new Promise(resolve => setTimeout(resolve, 1500));
1165
-
1166
- // Simulate different responses based on prompt
1167
- const lowerPrompt = prompt.toLowerCase();
1168
-
1169
- if (lowerPrompt.includes('comedy') || lowerPrompt.includes('funny')) {
1170
- 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?";
1171
- }
1172
- else if (lowerPrompt.includes('thriller') || lowerPrompt.includes('suspense')) {
1173
- 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!";
1174
- }
1175
- else if (lowerPrompt.includes('romance') || lowerPrompt.includes('love')) {
1176
- 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!";
1177
- }
1178
- else if (lowerPrompt.includes('recommend') || lowerPrompt.includes('suggest')) {
1179
- 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!";
1180
- }
1181
- else {
1182
- 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?'";
1183
- }
1184
- }
1185
-
1186
- // Update recommendations based on AI response
1187
- function updateRecommendationsFromAI(aiResponse) {
1188
- let filter = 'all';
1189
-
1190
- if (aiResponse.includes('comedy')) {
1191
- filter = 'comedy';
1192
- } else if (aiResponse.includes('thriller') || aiResponse.includes('suspense')) {
1193
- filter = 'thriller';
1194
- } else if (aiResponse.includes('romance') || aiResponse.includes('love')) {
1195
- filter = 'romance';
1196
- }
1197
-
1198
- loadRecommendations(filter);
1199
- }
1200
-
1201
- // Save show and set broadcast reminder
1202
- function saveShow(title, broadcastTime) {
1203
- // In a real app, this would save to a database
1204
- console.log(`Saved show: ${title}`);
1205
-
1206
- // Show notification
1207
- showNotification("Reminder Set!", `We'll notify you when "${title}" is about to broadcast.`);
1208
-
1209
- // In a real app, you would schedule a notification for the broadcast time
1210
- if (broadcastTime) {
1211
- const broadcastDate = new Date(broadcastTime);
1212
- const now = new Date();
1213
-
1214
- // Only schedule if broadcast is in the future
1215
- if (broadcastDate > now) {
1216
- const timeUntilBroadcast = broadcastDate - now;
1217
-
1218
- // Schedule notification 30 minutes before broadcast
1219
- setTimeout(() => {
1220
- showNotification("Starting Soon!", `"${title}" will begin broadcasting in 30 minutes!`);
1221
- }, timeUntilBroadcast - (30 * 60 * 1000));
1222
- }
1223
- }
1224
- }
1225
-
1226
- // Load recommendations
1227
- function loadRecommendations(filter = 'all') {
1228
- const container = document.getElementById('recommendations-container');
1229
- container.innerHTML = '';
1230
-
1231
- let filteredData = streamingData;
1232
-
1233
- if (filter === 'comedy') {
1234
- filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('comedy'));
1235
- } else if (filter === 'thriller') {
1236
- filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('thriller'));
1237
- } else if (filter === 'romance') {
1238
- filteredData = streamingData.filter(item => item.genre.toLowerCase().includes('romance'));
1239
- }
1240
-
1241
- filteredData.forEach(item => {
1242
- const card = document.createElement('div');
1243
- card.className = 'stream-card bg-white rounded-lg overflow-hidden shadow-md hover:shadow-xl transition duration-300 fade-in p-4';
1244
- card.innerHTML = `
1245
- <div class="mb-3">
1246
- <div class="flex justify-between items-start">
1247
- <h3 class="font-bold text-base">${item.title}</h3>
1248
- <span class="bg-yellow-100 text-yellow-800 text-xs px-2 py-1 rounded-full flex items-center">
1249
- <i class="fas fa-star text-yellow-500 mr-1 text-xs"></i> ${item.rating}
1250
- </span>
1251
- </div>
1252
- <p class="text-gray-600 text-xs mb-1">${item.type} • ${item.genre} • ${item.year}</p>
1253
- <div class="text-xs text-indigo-600 mb-2">${item.platform}</div>
1254
- </div>
1255
- <p class="text-gray-700 text-sm mb-4">${item.description}</p>
1256
- <div class="flex justify-between items-center">
1257
- <button class="text-indigo-600 hover:text-indigo-800 text-xs font-medium" onclick="saveShow('${item.title}', '${item.broadcastTime}')">
1258
- <i class="far fa-bookmark mr-1"></i> Save
1259
- </button>
1260
- <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">
1261
- <i class="fas fa-play mr-1"></i> Watch
1262
- </a>
1263
- </div>
1264
- `;
1265
- container.appendChild(card);
1266
- });
1267
- }
1268
-
1269
- // Load RSS feed
1270
- function loadRSSFeed(filter) {
1271
- const container = document.getElementById('rss-feed');
1272
- container.innerHTML = '';
1273
-
1274
- const feedItems = rssFeedData[filter] || rssFeedData.all;
1275
-
1276
- feedItems.forEach(item => {
1277
- const feedItem = document.createElement('div');
1278
- feedItem.className = 'rss-item bg-gray-50 p-3 rounded-lg';
1279
- feedItem.innerHTML = `
1280
- <h4 class="font-medium text-sm mb-1">${item.title}</h4>
1281
- <div class="flex items-center text-xs text-gray-500 mb-2">
1282
- <span>${item.source}</span>
1283
- <span class="mx-2">•</span>
1284
- <span>${item.time}</span>
1285
- </div>
1286
- <p class="text-xs text-gray-700">${item.excerpt}</p>
1287
- `;
1288
- container.appendChild(feedItem);
1289
- });
1290
- }
1291
-
1292
- // Start recording video and audio
1293
- async function startRecording() {
1294
- try {
1295
- // Get user media
1296
- videoStream = await navigator.mediaDevices.getDisplayMedia({
1297
- video: true,
1298
- audio: true
1299
- });
1300
-
1301
- audioStream = await navigator.mediaDevices.getUserMedia({
1302
- audio: true
1303
- });
1304
-
1305
- // Create audio context
1306
- audioContext = new AudioContext();
1307
- const source = audioContext.createMediaStreamSource(audioStream);
1308
- const destination = audioContext.createMediaStreamDestination();
1309
- source.connect(destination);
1310
-
1311
- // Combine video and audio streams
1312
- const combinedStream = new MediaStream([
1313
- ...videoStream.getVideoTracks(),
1314
- ...destination.stream.getAudioTracks()
1315
- ]);
1316
-
1317
- // Create media recorder
1318
- mediaRecorder = new MediaRecorder(combinedStream, {
1319
- mimeType: 'video/webm'
1320
- });
1321
-
1322
- // Update preview with live recording
1323
- const preview = document.getElementById('clip-preview');
1324
- preview.innerHTML = '<video autoplay muted></video>';
1325
- const previewVideo = preview.querySelector('video');
1326
- previewVideo.srcObject = combinedStream;
1327
-
1328
- // Set recording state
1329
- isRecording = true;
1330
- document.getElementById('recording-indicator').classList.remove('hidden');
1331
- document.getElementById('start-recording').classList.add('hidden');
1332
- document.getElementById('stop-recording').classList.remove('hidden');
1333
-
1334
- // Start progress bar animation
1335
- currentClipTime = 0;
1336
- const progressFill = document.getElementById('progress-fill');
1337
- progressFill.style.width = '0%';
1338
-
1339
- currentClipInterval = setInterval(() => {
1340
- currentClipTime += 100;
1341
- const progressPercent = (currentClipTime / 5000) * 100;
1342
- progressFill.style.width = `${progressPercent}%`;
1343
- }, 100);
1344
-
1345
- // Start recording in 5-second clips
1346
- let clipCount = 0;
1347
- recordingInterval = setInterval(() => {
1348
- if (clipCount > 0) {
1349
- // Stop current recording
1350
- mediaRecorder.stop();
1351
- }
1352
-
1353
- // Start new recording
1354
- mediaRecorder.start();
1355
- clipCount++;
1356
-
1357
- // Reset progress bar
1358
- currentClipTime = 0;
1359
- progressFill.style.width = '0%';
1360
-
1361
- // Store clip data when available
1362
- mediaRecorder.ondataavailable = (e) => {
1363
- const clip = {
1364
- blob: e.data,
1365
- timestamp: new Date().toLocaleTimeString(),
1366
- url: URL.createObjectURL(e.data),
1367
- username: username,
1368
- category: currentCategory,
1369
- engagementScore: Math.random() * 5, // Simulated metrics
1370
- qualityScore: 3 + Math.random() * 2,
1371
- consistencyScore: 3 + Math.random() * 2
1372
- };
1373
- recordedClips.push(clip);
1374
- updateClipsList();
1375
- };
1376
- }, 5000);
1377
-
1378
- showNotification("Recording Started", "Recording 5-second clips of your screen and audio");
1379
-
1380
- } catch (error) {
1381
- console.error("Error starting recording:", error);
1382
- showNotification("Recording Error", "Could not start recording. Please check permissions.");
1383
- stopRecording();
1384
- }
1385
- }
1386
-
1387
- // Stop recording
1388
- function stopRecording() {
1389
- if (mediaRecorder && mediaRecorder.state !== 'inactive') {
1390
- mediaRecorder.stop();
1391
- }
1392
-
1393
- clearInterval(recordingInterval);
1394
- clearInterval(currentClipInterval);
1395
- isRecording = false;
1396
-
1397
- // Stop all tracks
1398
- if (videoStream) {
1399
- videoStream.getTracks().forEach(track => track.stop());
1400
- }
1401
- if (audioStream) {
1402
- audioStream.getTracks().forEach(track => track.stop());
1403
- }
1404
-
1405
- // Close audio context
1406
- if (audioContext && audioContext.state !== 'closed') {
1407
- audioContext.close();
1408
- }
1409
-
1410
- // Reset preview
1411
- const preview = document.getElementById('clip-preview');
1412
- preview.innerHTML = `
1413
- <div class="clip-preview-placeholder">
1414
- <i class="fas fa-video"></i>
1415
- <p>Preview will appear here</p>
1416
- </div>
1417
- `;
1418
-
1419
- // Reset progress bar
1420
- document.getElementById('progress-fill').style.width = '0%';
1421
-
1422
- // Update UI
1423
- document.getElementById('recording-indicator').classList.add('hidden');
1424
- document.getElementById('start-recording').classList.remove('hidden');
1425
- document.getElementById('stop-recording').classList.add('hidden');
1426
-
1427
- showNotification("Recording Stopped", `Captured ${recordedClips.length} clips ready for production`);
1428
- }
1429
-
1430
- // Update clips list
1431
- function updateClipsList() {
1432
- const container = document.getElementById('clips-container');
1433
- container.innerHTML = '';
1434
-
1435
- if (recordedClips.length === 0) {
1436
- container.innerHTML = '<p class="text-gray-500 text-sm">Clips will appear here...</p>';
1437
- return;
1438
- }
1439
-
1440
- recordedClips.forEach((clip, index) => {
1441
- const clipItem = document.createElement('div');
1442
- clipItem.className = 'clip-item';
1443
- clipItem.innerHTML = `
1444
- <i class="fas fa-video text-gray-500 mr-2"></i>
1445
- <span class="text-sm flex-1">Clip ${index + 1}</span>
1446
- <span class="text-xs text-gray-500">${clip.timestamp}</span>
1447
- `;
1448
-
1449
- // Add click handler to preview clip
1450
- clipItem.addEventListener('click', () => {
1451
- const preview = document.getElementById('clip-preview');
1452
- preview.innerHTML = '<video controls></video>';
1453
- const previewVideo = preview.querySelector('video');
1454
- previewVideo.src = clip.url;
1455
- });
1456
-
1457
- container.appendChild(clipItem);
1458
- });
1459
- }
1460
-
1461
- // Clear all clips
1462
- function clearClips() {
1463
- if (recordedClips.length === 0) return;
1464
-
1465
- // In a real app, we would properly revoke the object URLs
1466
- recordedClips.forEach(clip => {
1467
- if (clip.url) {
1468
- URL.revokeObjectURL(clip.url);
1469
- }
1470
- });
1471
-
1472
- recordedClips = [];
1473
- updateClipsList();
1474
-
1475
- // Reset preview
1476
- const preview = document.getElementById('clip-preview');
1477
- preview.innerHTML = `
1478
- <div class="clip-preview-placeholder">
1479
- <i class="fas fa-video"></i>
1480
- <p>Preview will appear here</p>
1481
- </div>
1482
- `;
1483
-
1484
- showNotification("Clips Cleared", "All recorded clips have been removed");
1485
- }
1486
-
1487
- // Generate short video from clips
1488
- function generateShortVideo() {
1489
- if (recordedClips.length === 0) {
1490
- showNotification("No Clips", "Please record some clips first");
1491
- return;
1492
- }
1493
-
1494
- // In a real app, this would use a video editing library to combine clips
1495
- // and sync with audio. For this demo, we'll simulate the process.
1496
-
1497
- showNotification("Video Generation", "Processing your clips into a short video...");
1498
-
1499
- setTimeout(() => {
1500
- // Simulate processing time
1501
- const videoUrl = URL.createObjectURL(new Blob(["Simulated video content"], { type: 'video/mp4' }));
1502
-
1503
- // Create download link
1504
- const a = document.createElement('a');
1505
- a.href = videoUrl;
1506
- a.download = 'streamai-short.mp4';
1507
- a.click();
1508
-
1509
- showNotification("Video Ready", "Your short video has been generated and downloaded");
1510
- }, 3000);
1511
- }
1512
-
1513
- // Connect to Bluetooth devices
1514
- async function connectBluetooth() {
1515
- try {
1516
- // Request Bluetooth device
1517
- bluetoothDevice = await navigator.bluetooth.requestDevice({
1518
- acceptAllDevices: true,
1519
- optionalServices: ['generic_access']
1520
- });
1521
-
1522
- // Connect to the GATT Server
1523
- bluetoothServer = await bluetoothDevice.gatt.connect();
1524
-
1525
- // Get the service
1526
- bluetoothService = await bluetoothServer.getPrimaryService('generic_access');
1527
-
1528
- // Get the characteristic
1529
- bluetoothCharacteristic = await bluetoothService.getCharacteristic('device_name');
1530
-
1531
- // Update connection status
1532
- isBluetoothConnected = true;
1533
- const statusElement = document.getElementById('connection-status');
1534
- statusElement.innerHTML = `
1535
- <i class="fas fa-circle bluetooth-connected mr-1"></i>
1536
- <span>Connected to ${bluetoothDevice.name || 'device'}</span>
1537
- `;
1538
-
1539
- // Simulate discovering group members
1540
- groupMembers = [
1541
- { name: "User" + Math.floor(Math.random() * 1000), device: "Device 1" },
1542
- { name: "User" + Math.floor(Math.random() * 1000), device: "Device 2" },
1543
- { name: "User" + Math.floor(Math.random() * 1000), device: "Device 3" }
1544
- ];
1545
-
1546
- showNotification("Bluetooth Connected", `Connected to ${bluetoothDevice.name}. Found ${groupMembers.length} group members.`);
1547
-
1548
- } catch (error) {
1549
- console.error("Bluetooth connection error:", error);
1550
- isBluetoothConnected = false;
1551
- document.getElementById('connection-status').innerHTML = `
1552
- <i class="fas fa-circle bluetooth-disconnected mr-1"></i>
1553
- <span>Not connected</span>
1554
- `;
1555
- showNotification("Connection Failed", "Could not connect to Bluetooth device");
1556
- }
1557
- }
1558
-
1559
- // Share clips with Bluetooth group
1560
- async function shareClipsWithGroup() {
1561
- if (!isBluetoothConnected) {
1562
-
1563
-
1564
  </html>
 
6
  <title>StreamAI - Personalized Streaming Recommendations</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <link rel="stylesheet" href="style.css">
10
+ <script src="app.js" defer></script>
11
+
12
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  </head>
14
  <body class="bg-gray-100 font-sans">
15
  <!-- Notification -->
 
452
  </div>
453
  </div>
454
  </footer>
455
+ </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  </html>