makinuh commited on
Commit
e880acb
·
verified ·
1 Parent(s): f993aec

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +768 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Bando
3
- emoji: 🏢
4
- colorFrom: indigo
5
- colorTo: blue
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: bando
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: pink
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,768 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Low-Bandwidth Connect</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
+ .video-container {
11
+ position: relative;
12
+ width: 100%;
13
+ padding-bottom: 56.25%; /* 16:9 aspect ratio */
14
+ background-color: #1e293b;
15
+ border-radius: 0.5rem;
16
+ overflow: hidden;
17
+ }
18
+ .video-element {
19
+ position: absolute;
20
+ top: 0;
21
+ left: 0;
22
+ width: 100%;
23
+ height: 100%;
24
+ object-fit: cover;
25
+ }
26
+ .connection-quality {
27
+ position: absolute;
28
+ bottom: 10px;
29
+ right: 10px;
30
+ background-color: rgba(0,0,0,0.5);
31
+ color: white;
32
+ padding: 2px 6px;
33
+ border-radius: 4px;
34
+ font-size: 12px;
35
+ }
36
+ .bandwidth-optimizer {
37
+ transition: all 0.3s ease;
38
+ }
39
+ .bandwidth-optimizer:hover {
40
+ transform: scale(1.05);
41
+ }
42
+ .pulse {
43
+ animation: pulse 2s infinite;
44
+ }
45
+ @keyframes pulse {
46
+ 0% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0.7); }
47
+ 70% { box-shadow: 0 0 0 10px rgba(74, 222, 128, 0); }
48
+ 100% { box-shadow: 0 0 0 0 rgba(74, 222, 128, 0); }
49
+ }
50
+ </style>
51
+ </head>
52
+ <body class="bg-gray-900 text-gray-100 min-h-screen">
53
+ <div class="container mx-auto px-4 py-8 max-w-6xl">
54
+ <header class="flex justify-between items-center mb-8">
55
+ <div class="flex items-center">
56
+ <i class="fas fa-signal text-green-400 text-2xl mr-3"></i>
57
+ <h1 class="text-2xl font-bold bg-gradient-to-r from-green-400 to-blue-500 bg-clip-text text-transparent">
58
+ LowBand Connect
59
+ </h1>
60
+ </div>
61
+ <div class="flex items-center space-x-4">
62
+ <div class="hidden md:flex items-center space-x-2 text-sm">
63
+ <span class="text-gray-400">Optimized for</span>
64
+ <span class="px-2 py-1 bg-gray-800 rounded-full text-green-400 font-medium">
65
+ <i class="fas fa-wifi mr-1"></i> Low Bandwidth
66
+ </span>
67
+ </div>
68
+ <button id="settingsBtn" class="p-2 rounded-full hover:bg-gray-800 transition">
69
+ <i class="fas fa-cog text-gray-400"></i>
70
+ </button>
71
+ </div>
72
+ </header>
73
+
74
+ <main>
75
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
76
+ <!-- Local Video -->
77
+ <div class="video-container">
78
+ <video id="localVideo" class="video-element" autoplay muted></video>
79
+ <div class="connection-quality hidden">
80
+ <i class="fas fa-signal mr-1"></i>
81
+ <span>Local</span>
82
+ </div>
83
+ <div class="absolute top-2 left-2 bg-gray-900 bg-opacity-70 px-2 py-1 rounded text-sm">
84
+ You
85
+ </div>
86
+ </div>
87
+
88
+ <!-- Remote Video -->
89
+ <div class="video-container">
90
+ <video id="remoteVideo" class="video-element" autoplay></video>
91
+ <div class="connection-quality hidden">
92
+ <i class="fas fa-signal mr-1"></i>
93
+ <span>Remote</span>
94
+ </div>
95
+ <div class="absolute top-2 left-2 bg-gray-900 bg-opacity-70 px-2 py-1 rounded text-sm">
96
+ Partner
97
+ </div>
98
+ </div>
99
+ </div>
100
+
101
+ <div class="flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
102
+ <div class="flex items-center space-x-2">
103
+ <div class="text-sm bg-gray-800 px-3 py-1 rounded-full">
104
+ <span class="text-gray-400">Connection:</span>
105
+ <span id="connectionStatus" class="font-medium text-yellow-400">Disconnected</span>
106
+ </div>
107
+ <div id="bandwidthIndicator" class="text-sm bg-gray-800 px-3 py-1 rounded-full hidden">
108
+ <span class="text-gray-400">Bandwidth:</span>
109
+ <span id="bandwidthValue" class="font-medium">-- kbps</span>
110
+ </div>
111
+ </div>
112
+
113
+ <div class="flex space-x-3">
114
+ <button id="toggleVideoBtn" class="bg-gray-800 hover:bg-gray-700 text-white p-3 rounded-full transition">
115
+ <i class="fas fa-video"></i>
116
+ </button>
117
+ <button id="toggleAudioBtn" class="bg-gray-800 hover:bg-gray-700 text-white p-3 rounded-full transition">
118
+ <i class="fas fa-microphone"></i>
119
+ </button>
120
+ <button id="callBtn" class="bg-green-600 hover:bg-green-700 text-white px-6 py-3 rounded-full font-medium flex items-center pulse">
121
+ <i class="fas fa-phone mr-2"></i>
122
+ <span>Start Call</span>
123
+ </button>
124
+ <button id="endCallBtn" class="bg-red-600 hover:bg-red-700 text-white px-6 py-3 rounded-full font-medium flex items-center hidden">
125
+ <i class="fas fa-phone-slash mr-2"></i>
126
+ <span>End Call</span>
127
+ </button>
128
+ </div>
129
+ </div>
130
+
131
+ <!-- Bandwidth Optimizer Panel -->
132
+ <div id="optimizerPanel" class="mt-8 bg-gray-800 rounded-lg p-4 hidden">
133
+ <h3 class="text-lg font-medium mb-4 flex items-center">
134
+ <i class="fas fa-tachometer-alt mr-2 text-blue-400"></i>
135
+ Bandwidth Optimizer
136
+ </h3>
137
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
138
+ <div class="bandwidth-optimizer bg-gray-700 p-4 rounded-lg cursor-pointer" data-preset="low">
139
+ <div class="flex items-center mb-2">
140
+ <i class="fas fa-bicycle text-green-400 mr-2"></i>
141
+ <h4 class="font-medium">Low Bandwidth</h4>
142
+ </div>
143
+ <p class="text-sm text-gray-400">Optimized for slow connections (64-128 kbps)</p>
144
+ <div class="mt-3 text-xs text-gray-500">
145
+ <span>• 160x120 resolution</span><br>
146
+ <span>• 10fps</span><br>
147
+ <span>• Low bitrate</span>
148
+ </div>
149
+ </div>
150
+ <div class="bandwidth-optimizer bg-gray-700 p-4 rounded-lg cursor-pointer" data-preset="medium">
151
+ <div class="flex items-center mb-2">
152
+ <i class="fas fa-car text-yellow-400 mr-2"></i>
153
+ <h4 class="font-medium">Medium Bandwidth</h4>
154
+ </div>
155
+ <p class="text-sm text-gray-400">Balanced quality and bandwidth (128-256 kbps)</p>
156
+ <div class="mt-3 text-xs text-gray-500">
157
+ <span>• 320x240 resolution</span><br>
158
+ <span>• 15fps</span><br>
159
+ <span>• Medium bitrate</span>
160
+ </div>
161
+ </div>
162
+ <div class="bandwidth-optimizer bg-gray-700 p-4 rounded-lg cursor-pointer" data-preset="high">
163
+ <div class="flex items-center mb-2">
164
+ <i class="fas fa-rocket text-red-400 mr-2"></i>
165
+ <h4 class="font-medium">High Bandwidth</h4>
166
+ </div>
167
+ <p class="text-sm text-gray-400">For better connections (256+ kbps)</p>
168
+ <div class="mt-3 text-xs text-gray-500">
169
+ <span>• 640x480 resolution</span><br>
170
+ <span>• 24fps</span><br>
171
+ <span>• High bitrate</span>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ </main>
177
+
178
+ <!-- Settings Modal -->
179
+ <div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden z-50">
180
+ <div class="bg-gray-800 rounded-lg p-6 w-full max-w-md">
181
+ <div class="flex justify-between items-center mb-4">
182
+ <h3 class="text-xl font-medium">
183
+ <i class="fas fa-cog mr-2 text-blue-400"></i>
184
+ Settings
185
+ </h3>
186
+ <button id="closeSettingsBtn" class="text-gray-400 hover:text-white">
187
+ <i class="fas fa-times"></i>
188
+ </button>
189
+ </div>
190
+
191
+ <div class="space-y-4">
192
+ <div>
193
+ <label class="block text-sm font-medium mb-1">Video Source</label>
194
+ <select id="videoSource" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
195
+ <option value="">Default Camera</option>
196
+ </select>
197
+ </div>
198
+
199
+ <div>
200
+ <label class="block text-sm font-medium mb-1">Audio Source</label>
201
+ <select id="audioSource" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
202
+ <option value="">Default Microphone</option>
203
+ </select>
204
+ </div>
205
+
206
+ <div>
207
+ <label class="flex items-center space-x-2">
208
+ <input type="checkbox" id="enableBandwidthDetection" class="rounded bg-gray-700 border-gray-600" checked>
209
+ <span class="text-sm">Auto-detect bandwidth</span>
210
+ </label>
211
+ </div>
212
+
213
+ <div>
214
+ <label class="block text-sm font-medium mb-1">Default Bandwidth</label>
215
+ <select id="defaultBandwidth" class="w-full bg-gray-700 border border-gray-600 rounded px-3 py-2 text-sm">
216
+ <option value="low">Low (64-128 kbps)</option>
217
+ <option value="medium" selected>Medium (128-256 kbps)</option>
218
+ <option value="high">High (256+ kbps)</option>
219
+ </select>
220
+ </div>
221
+ </div>
222
+
223
+ <div class="mt-6 flex justify-end space-x-3">
224
+ <button id="saveSettingsBtn" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded font-medium">
225
+ Save Settings
226
+ </button>
227
+ </div>
228
+ </div>
229
+ </div>
230
+
231
+ <!-- Connection Modal -->
232
+ <div id="connectionModal" class="fixed inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden z-50">
233
+ <div class="bg-gray-800 rounded-lg p-6 w-full max-w-md text-center">
234
+ <div class="mb-4">
235
+ <i class="fas fa-link text-blue-400 text-5xl mb-3"></i>
236
+ <h3 class="text-xl font-medium mb-2">Establishing Connection</h3>
237
+ <p class="text-gray-400 text-sm">Optimizing for low bandwidth...</p>
238
+ </div>
239
+ <div class="w-full bg-gray-700 rounded-full h-2 mb-4">
240
+ <div id="connectionProgress" class="bg-blue-500 h-2 rounded-full" style="width: 0%"></div>
241
+ </div>
242
+ <button id="cancelConnectionBtn" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded font-medium">
243
+ Cancel
244
+ </button>
245
+ </div>
246
+ </div>
247
+ </div>
248
+
249
+ <script>
250
+ // DOM Elements
251
+ const localVideo = document.getElementById('localVideo');
252
+ const remoteVideo = document.getElementById('remoteVideo');
253
+ const callBtn = document.getElementById('callBtn');
254
+ const endCallBtn = document.getElementById('endCallBtn');
255
+ const toggleVideoBtn = document.getElementById('toggleVideoBtn');
256
+ const toggleAudioBtn = document.getElementById('toggleAudioBtn');
257
+ const connectionStatus = document.getElementById('connectionStatus');
258
+ const bandwidthIndicator = document.getElementById('bandwidthIndicator');
259
+ const bandwidthValue = document.getElementById('bandwidthValue');
260
+ const optimizerPanel = document.getElementById('optimizerPanel');
261
+ const settingsBtn = document.getElementById('settingsBtn');
262
+ const settingsModal = document.getElementById('settingsModal');
263
+ const closeSettingsBtn = document.getElementById('closeSettingsBtn');
264
+ const saveSettingsBtn = document.getElementById('saveSettingsBtn');
265
+ const connectionModal = document.getElementById('connectionModal');
266
+ const connectionProgress = document.getElementById('connectionProgress');
267
+ const cancelConnectionBtn = document.getElementById('cancelConnectionBtn');
268
+
269
+ // State variables
270
+ let localStream;
271
+ let peerConnection;
272
+ let isCallActive = false;
273
+ let isVideoEnabled = true;
274
+ let isAudioEnabled = true;
275
+ let currentBandwidthPreset = 'medium';
276
+
277
+ // Initialize the app
278
+ async function init() {
279
+ try {
280
+ // Get media devices
281
+ await getMediaDevices();
282
+
283
+ // Set up event listeners
284
+ setupEventListeners();
285
+
286
+ // Show bandwidth optimizer panel
287
+ optimizerPanel.classList.remove('hidden');
288
+
289
+ // Set default bandwidth preset
290
+ applyBandwidthPreset(currentBandwidthPreset);
291
+
292
+ } catch (error) {
293
+ console.error('Initialization error:', error);
294
+ }
295
+ }
296
+
297
+ // Set up event listeners
298
+ function setupEventListeners() {
299
+ // Call buttons
300
+ callBtn.addEventListener('click', startCall);
301
+ endCallBtn.addEventListener('click', endCall);
302
+
303
+ // Toggle buttons
304
+ toggleVideoBtn.addEventListener('click', toggleVideo);
305
+ toggleAudioBtn.addEventListener('click', toggleAudio);
306
+
307
+ // Bandwidth optimizers
308
+ document.querySelectorAll('.bandwidth-optimizer').forEach(optimizer => {
309
+ optimizer.addEventListener('click', () => {
310
+ const preset = optimizer.getAttribute('data-preset');
311
+ applyBandwidthPreset(preset);
312
+ });
313
+ });
314
+
315
+ // Settings
316
+ settingsBtn.addEventListener('click', () => settingsModal.classList.remove('hidden'));
317
+ closeSettingsBtn.addEventListener('click', () => settingsModal.classList.add('hidden'));
318
+ saveSettingsBtn.addEventListener('click', saveSettings);
319
+
320
+ // Connection modal
321
+ cancelConnectionBtn.addEventListener('click', cancelConnection);
322
+ }
323
+
324
+ // Get media devices
325
+ async function getMediaDevices() {
326
+ try {
327
+ localStream = await navigator.mediaDevices.getUserMedia({
328
+ video: true,
329
+ audio: true
330
+ });
331
+
332
+ localVideo.srcObject = localStream;
333
+
334
+ // Populate device selectors
335
+ const devices = await navigator.mediaDevices.enumerateDevices();
336
+ const videoSource = document.getElementById('videoSource');
337
+ const audioSource = document.getElementById('audioSource');
338
+
339
+ devices.forEach(device => {
340
+ if (device.kind === 'videoinput') {
341
+ const option = document.createElement('option');
342
+ option.value = device.deviceId;
343
+ option.text = device.label || `Camera ${videoSource.length + 1}`;
344
+ videoSource.appendChild(option);
345
+ } else if (device.kind === 'audioinput') {
346
+ const option = document.createElement('option');
347
+ option.value = device.deviceId;
348
+ option.text = device.label || `Microphone ${audioSource.length + 1}`;
349
+ audioSource.appendChild(option);
350
+ }
351
+ });
352
+
353
+ } catch (error) {
354
+ console.error('Error accessing media devices:', error);
355
+ alert('Could not access camera or microphone. Please check permissions.');
356
+ }
357
+ }
358
+
359
+ // Start a call
360
+ async function startCall() {
361
+ if (!localStream) {
362
+ alert('Please allow camera and microphone access first.');
363
+ return;
364
+ }
365
+
366
+ // Show connection modal
367
+ connectionModal.classList.remove('hidden');
368
+ simulateConnectionProgress();
369
+
370
+ try {
371
+ // Create peer connection
372
+ const configuration = {
373
+ iceServers: [
374
+ { urls: 'stun:stun.l.google.com:19302' },
375
+ // Add your TURN server here for NAT traversal
376
+ ]
377
+ };
378
+
379
+ peerConnection = new RTCPeerConnection(configuration);
380
+
381
+ // Set up event handlers
382
+ peerConnection.onicecandidate = handleICECandidateEvent;
383
+ peerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
384
+ peerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
385
+ peerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
386
+ peerConnection.ontrack = handleTrackEvent;
387
+
388
+ // Add local stream tracks
389
+ localStream.getTracks().forEach(track => {
390
+ peerConnection.addTrack(track, localStream);
391
+ });
392
+
393
+ // Create offer
394
+ const offer = await peerConnection.createOffer({
395
+ offerToReceiveAudio: true,
396
+ offerToReceiveVideo: true
397
+ });
398
+
399
+ await peerConnection.setLocalDescription(offer);
400
+
401
+ // In a real app, you would send the offer to the other peer via signaling
402
+ // For this demo, we'll simulate the connection
403
+
404
+ setTimeout(() => {
405
+ // Simulate receiving an answer
406
+ simulateAnswer();
407
+
408
+ // Update UI
409
+ connectionStatus.textContent = 'Connected';
410
+ connectionStatus.className = 'font-medium text-green-400';
411
+
412
+ // Show bandwidth indicator
413
+ bandwidthIndicator.classList.remove('hidden');
414
+ updateBandwidthDisplay();
415
+
416
+ // Toggle call buttons
417
+ callBtn.classList.add('hidden');
418
+ endCallBtn.classList.remove('hidden');
419
+
420
+ // Hide connection modal
421
+ connectionModal.classList.add('hidden');
422
+
423
+ isCallActive = true;
424
+ }, 2000);
425
+
426
+ } catch (error) {
427
+ console.error('Error starting call:', error);
428
+ connectionModal.classList.add('hidden');
429
+ alert('Failed to start call. Please try again.');
430
+ }
431
+ }
432
+
433
+ // Simulate connection progress
434
+ function simulateConnectionProgress() {
435
+ let progress = 0;
436
+ const interval = setInterval(() => {
437
+ progress += 5;
438
+ connectionProgress.style.width = `${progress}%`;
439
+
440
+ if (progress >= 100) {
441
+ clearInterval(interval);
442
+ }
443
+ }, 200);
444
+ }
445
+
446
+ // Simulate receiving an answer (for demo purposes)
447
+ function simulateAnswer() {
448
+ if (!peerConnection) return;
449
+
450
+ // In a real app, you would receive this from the other peer
451
+ const answer = {
452
+ type: 'answer',
453
+ sdp: `v=0
454
+ o=- 123456789 2 IN IP4 127.0.0.1
455
+ s=-
456
+ t=0 0
457
+ a=group:BUNDLE 0 1
458
+ a=msid-semantic: WMS
459
+ m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
460
+ c=IN IP4 0.0.0.0
461
+ a=rtcp:9 IN IP4 0.0.0.0
462
+ a=ice-ufrag:xyz
463
+ a=ice-pwd:abc
464
+ a=fingerprint:sha-256 AA:BB:CC
465
+ a=setup:active
466
+ a=mid:0
467
+ a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
468
+ a=sendrecv
469
+ a=rtpmap:111 opus/48000/2
470
+ a=fmtp:111 minptime=10;useinbandfec=1
471
+ a=rtpmap:103 ISAC/16000
472
+ a=rtpmap:104 ISAC/32000
473
+ a=rtpmap:9 G722/8000
474
+ a=rtpmap:0 PCMU/8000
475
+ a=rtpmap:8 PCMA/8000
476
+ a=rtpmap:106 CN/32000
477
+ a=rtpmap:105 CN/16000
478
+ a=rtpmap:13 CN/8000
479
+ a=rtpmap:110 telephone-event/48000
480
+ a=rtpmap:112 telephone-event/32000
481
+ a=rtpmap:113 telephone-event/16000
482
+ a=rtpmap:126 telephone-event/8000
483
+ a=maxptime:60
484
+ a=ssrc:12345678 cname:audio
485
+ m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102
486
+ c=IN IP4 0.0.0.0
487
+ a=rtcp:9 IN IP4 0.0.0.0
488
+ a=ice-ufrag:xyz
489
+ a=ice-pwd:abc
490
+ a=fingerprint:sha-256 AA:BB:CC
491
+ a=setup:active
492
+ a=mid:1
493
+ a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
494
+ a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
495
+ a=extmap:4 urn:3gpp:video-orientation
496
+ a=sendrecv
497
+ a=rtpmap:96 VP8/90000
498
+ a=rtcp-fb:96 goog-remb
499
+ a=rtcp-fb:96 transport-cc
500
+ a=rtcp-fb:96 ccm fir
501
+ a=rtcp-fb:96 nack
502
+ a=rtcp-fb:96 nack pli
503
+ a=rtpmap:97 rtx/90000
504
+ a=fmtp:97 apt=96
505
+ a=rtpmap:98 VP9/90000
506
+ a=rtcp-fb:98 goog-remb
507
+ a=rtcp-fb:98 transport-cc
508
+ a=rtcp-fb:98 ccm fir
509
+ a=rtcp-fb:98 nack
510
+ a=rtcp-fb:98 nack pli
511
+ a=rtpmap:99 rtx/90000
512
+ a=fmtp:99 apt=98
513
+ a=rtpmap:100 H264/90000
514
+ a=rtcp-fb:100 goog-remb
515
+ a=rtcp-fb:100 transport-cc
516
+ a=rtcp-fb:100 ccm fir
517
+ a=rtcp-fb:100 nack
518
+ a=rtcp-fb:100 nack pli
519
+ a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
520
+ a=rtpmap:101 rtx/90000
521
+ a=fmtp:101 apt=100
522
+ a=rtpmap:102 H264/90000
523
+ a=rtcp-fb:102 goog-remb
524
+ a=rtcp-fb:102 transport-cc
525
+ a=rtcp-fb:102 ccm fir
526
+ a=rtcp-fb:102 nack
527
+ a=rtcp-fb:102 nack pli
528
+ a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f
529
+ a=ssrc-group:FID 12345678 12345679
530
+ a=ssrc:12345678 cname:video
531
+ a=ssrc:12345679 cname:video`
532
+ };
533
+
534
+ peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
535
+ }
536
+
537
+ // End the call
538
+ function endCall() {
539
+ if (peerConnection) {
540
+ peerConnection.close();
541
+ peerConnection = null;
542
+ }
543
+
544
+ if (remoteVideo.srcObject) {
545
+ remoteVideo.srcObject.getTracks().forEach(track => track.stop());
546
+ remoteVideo.srcObject = null;
547
+ }
548
+
549
+ // Update UI
550
+ connectionStatus.textContent = 'Disconnected';
551
+ connectionStatus.className = 'font-medium text-yellow-400';
552
+
553
+ // Hide bandwidth indicator
554
+ bandwidthIndicator.classList.add('hidden');
555
+
556
+ // Toggle call buttons
557
+ callBtn.classList.remove('hidden');
558
+ endCallBtn.classList.add('hidden');
559
+
560
+ isCallActive = false;
561
+ }
562
+
563
+ // Cancel connection attempt
564
+ function cancelConnection() {
565
+ if (peerConnection) {
566
+ peerConnection.close();
567
+ peerConnection = null;
568
+ }
569
+
570
+ connectionModal.classList.add('hidden');
571
+ }
572
+
573
+ // Toggle video
574
+ function toggleVideo() {
575
+ if (!localStream) return;
576
+
577
+ const videoTrack = localStream.getVideoTracks()[0];
578
+ if (videoTrack) {
579
+ isVideoEnabled = !videoTrack.enabled;
580
+ videoTrack.enabled = isVideoEnabled;
581
+
582
+ toggleVideoBtn.innerHTML = isVideoEnabled ? '<i class="fas fa-video"></i>' : '<i class="fas fa-video-slash"></i>';
583
+ toggleVideoBtn.classList.toggle('bg-gray-800');
584
+ toggleVideoBtn.classList.toggle('bg-red-600');
585
+
586
+ // Update connection if active
587
+ if (isCallActive) {
588
+ updateBandwidthSettings();
589
+ }
590
+ }
591
+ }
592
+
593
+ // Toggle audio
594
+ function toggleAudio() {
595
+ if (!localStream) return;
596
+
597
+ const audioTrack = localStream.getAudioTracks()[0];
598
+ if (audioTrack) {
599
+ isAudioEnabled = !audioTrack.enabled;
600
+ audioTrack.enabled = isAudioEnabled;
601
+
602
+ toggleAudioBtn.innerHTML = isAudioEnabled ? '<i class="fas fa-microphone"></i>' : '<i class="fas fa-microphone-slash"></i>';
603
+ toggleAudioBtn.classList.toggle('bg-gray-800');
604
+ toggleAudioBtn.classList.toggle('bg-red-600');
605
+ }
606
+ }
607
+
608
+ // Apply bandwidth preset
609
+ function applyBandwidthPreset(preset) {
610
+ currentBandwidthPreset = preset;
611
+
612
+ // Update UI
613
+ document.querySelectorAll('.bandwidth-optimizer').forEach(opt => {
614
+ opt.classList.remove('border-2', 'border-green-400');
615
+ if (opt.getAttribute('data-preset') === preset) {
616
+ opt.classList.add('border-2', 'border-green-400');
617
+ }
618
+ });
619
+
620
+ // Update connection if active
621
+ if (isCallActive) {
622
+ updateBandwidthSettings();
623
+ }
624
+
625
+ // Update bandwidth display
626
+ updateBandwidthDisplay();
627
+ }
628
+
629
+ // Update bandwidth settings for the connection
630
+ function updateBandwidthSettings() {
631
+ if (!peerConnection || !isCallActive) return;
632
+
633
+ const senders = peerConnection.getSenders();
634
+ senders.forEach(sender => {
635
+ if (sender.track.kind === 'video') {
636
+ const parameters = sender.getParameters();
637
+
638
+ if (!parameters.encodings) {
639
+ parameters.encodings = [{}];
640
+ }
641
+
642
+ // Apply bandwidth constraints based on preset
643
+ switch (currentBandwidthPreset) {
644
+ case 'low':
645
+ parameters.encodings[0].maxBitrate = 128000; // 128 kbps
646
+ break;
647
+ case 'medium':
648
+ parameters.encodings[0].maxBitrate = 256000; // 256 kbps
649
+ break;
650
+ case 'high':
651
+ parameters.encodings[0].maxBitrate = 512000; // 512 kbps
652
+ break;
653
+ }
654
+
655
+ // Apply resolution scaling based on preset
656
+ if (sender.track.kind === 'video') {
657
+ const constraints = {};
658
+
659
+ switch (currentBandwidthPreset) {
660
+ case 'low':
661
+ constraints.width = { ideal: 160 };
662
+ constraints.height = { ideal: 120 };
663
+ constraints.frameRate = { ideal: 10 };
664
+ break;
665
+ case 'medium':
666
+ constraints.width = { ideal: 320 };
667
+ constraints.height = { ideal: 240 };
668
+ constraints.frameRate = { ideal: 15 };
669
+ break;
670
+ case 'high':
671
+ constraints.width = { ideal: 640 };
672
+ constraints.height = { ideal: 480 };
673
+ constraints.frameRate = { ideal: 24 };
674
+ break;
675
+ }
676
+
677
+ sender.track.applyConstraints(constraints);
678
+ }
679
+
680
+ sender.setParameters(parameters);
681
+ }
682
+ });
683
+
684
+ updateBandwidthDisplay();
685
+ }
686
+
687
+ // Update bandwidth display
688
+ function updateBandwidthDisplay() {
689
+ let bandwidthText = '';
690
+
691
+ switch (currentBandwidthPreset) {
692
+ case 'low':
693
+ bandwidthText = '64-128 kbps';
694
+ break;
695
+ case 'medium':
696
+ bandwidthText = '128-256 kbps';
697
+ break;
698
+ case 'high':
699
+ bandwidthText = '256-512 kbps';
700
+ break;
701
+ }
702
+
703
+ bandwidthValue.textContent = bandwidthText;
704
+ }
705
+
706
+ // Save settings
707
+ function saveSettings() {
708
+ const videoSource = document.getElementById('videoSource').value;
709
+ const audioSource = document.getElementById('audioSource').value;
710
+ const enableBandwidthDetection = document.getElementById('enableBandwidthDetection').checked;
711
+ const defaultBandwidth = document.getElementById('defaultBandwidth').value;
712
+
713
+ // In a real app, you would save these settings to localStorage or a server
714
+ console.log('Settings saved:', {
715
+ videoSource,
716
+ audioSource,
717
+ enableBandwidthDetection,
718
+ defaultBandwidth
719
+ });
720
+
721
+ // Apply default bandwidth if changed
722
+ if (defaultBandwidth !== currentBandwidthPreset) {
723
+ applyBandwidthPreset(defaultBandwidth);
724
+ }
725
+
726
+ // Close settings modal
727
+ settingsModal.classList.add('hidden');
728
+
729
+ alert('Settings saved successfully!');
730
+ }
731
+
732
+ // WebRTC event handlers
733
+ function handleICECandidateEvent(event) {
734
+ if (event.candidate) {
735
+ // In a real app, you would send the candidate to the other peer
736
+ console.log('ICE candidate:', event.candidate);
737
+ }
738
+ }
739
+
740
+ function handleICEConnectionStateChangeEvent() {
741
+ if (peerConnection) {
742
+ console.log('ICE connection state:', peerConnection.iceConnectionState);
743
+ }
744
+ }
745
+
746
+ function handleICEGatheringStateChangeEvent() {
747
+ if (peerConnection) {
748
+ console.log('ICE gathering state:', peerConnection.iceGatheringState);
749
+ }
750
+ }
751
+
752
+ function handleSignalingStateChangeEvent() {
753
+ if (peerConnection) {
754
+ console.log('Signaling state:', peerConnection.signalingState);
755
+ }
756
+ }
757
+
758
+ function handleTrackEvent(event) {
759
+ if (event.streams && event.streams[0]) {
760
+ remoteVideo.srcObject = event.streams[0];
761
+ }
762
+ }
763
+
764
+ // Initialize the app when the page loads
765
+ window.addEventListener('load', init);
766
+ </script>
767
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=makinuh/bando" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
768
+ </html>