inoculatemedia commited on
Commit
d3ec1a2
·
verified ·
1 Parent(s): c692dcf

Add 2 files

Browse files
Files changed (2) hide show
  1. index.html +533 -318
  2. prompts.txt +1 -0
index.html CHANGED
@@ -3,400 +3,615 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>NHLS Video Player with Save Feature</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
- /* Custom CSS for video player */
11
- .video-container {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  position: relative;
13
- max-width: 800px;
14
- margin: 0 auto;
15
- background-color: #000;
16
- border-radius: 12px;
17
  overflow: hidden;
18
- box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3);
19
  }
20
-
21
- .video-controls {
 
22
  position: absolute;
23
  bottom: 0;
24
- left: 0;
25
- right: 0;
26
- padding: 10px;
27
- background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
28
- opacity: 0;
29
- transition: opacity 0.3s;
30
- }
31
-
32
- .video-container:hover .video-controls {
33
- opacity: 1;
34
- }
35
-
36
- .progress-container {
37
- height: 5px;
38
- background-color: rgba(255, 255, 255, 0.2);
39
- border-radius: 3px;
40
- cursor: pointer;
41
  }
42
-
43
- .progress-bar {
 
 
 
44
  height: 100%;
45
- background-color: #3b82f6;
46
- border-radius: 3px;
47
- width: 0%;
48
- }
49
-
50
- .volume-container {
51
- width: 100px;
52
- }
53
-
54
- .volume-slider {
55
- width: 0;
56
- transition: width 0.2s;
57
- overflow: hidden;
58
- }
59
-
60
- .volume-btn:hover + .volume-slider, .volume-slider:hover {
61
- width: 100px;
62
  }
63
-
64
- /* Animation for download button */
65
- @keyframes pulse {
66
- 0% { transform: scale(1); }
67
- 50% { transform: scale(1.1); }
68
- 100% { transform: scale(1); }
69
  }
70
-
71
- .download-btn:hover {
72
- animation: pulse 1s infinite;
73
  }
74
-
75
- /* Custom scrollbar */
76
- ::-webkit-scrollbar {
77
- width: 8px;
78
  }
79
-
80
- ::-webkit-scrollbar-track {
81
- background: #f1f1f1;
82
- }
83
-
84
- ::-webkit-scrollbar-thumb {
85
- background: #888;
86
- border-radius: 4px;
87
- }
88
-
89
- ::-webkit-scrollbar-thumb:hover {
90
- background: #555;
91
  }
92
  </style>
93
- <script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
94
- <!-- Or if you want the latest version from the main branch -->
95
- <!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@canary"></script> -->
96
- <script>
97
- var video = document.getElementById('video-player');
98
- var videoSrc = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
99
- if (Hls.isSupported()) {
100
- var hls = new Hls();
101
- hls.loadSource(videoSrc);
102
- hls.attachMedia(video);
103
- }
104
- // HLS.js is not supported on platforms that do not have Media Source
105
- // Extensions (MSE) enabled.
106
- //
107
- // When the browser has built-in HLS support (check using `canPlayType`),
108
- // we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video
109
- // element through the `src` property. This is using the built-in support
110
- // of the plain video element, without using HLS.js.
111
- else if (video.canPlayType('application/vnd.apple.mpegurl')) {
112
- video.src = videoSrc;
113
- }
114
- </script>
115
  </head>
116
- <body class="bg-gray-900 text-white min-h-screen flex flex-col">
117
- <header class="bg-gray-800 py-4 px-6 shadow-lg">
118
- <div class="container mx-auto flex justify-between items-center">
119
- <div class="flex items-center space-x-2">
120
- <i class="fas fa-play-circle text-blue-500 text-3xl"></i>
121
- <h1 class="text-2xl font-bold">HLS Video Player</h1>
122
- </div>
123
- <div class="flex items-center space-x-4">
124
- <button id="theme-toggle" class="p-2 rounded-full hover:bg-gray-700">
125
- <i class="fas fa-moon"></i>
 
 
126
  </button>
127
  </div>
128
  </div>
129
- </header>
130
 
131
- <main class="flex-grow container mx-auto px-4 py-8">
132
- <div class="video-container group">
133
- <video autoplay muted id="video-player" class="w-full" poster="https://via.placeholder.com/800x450/1e293b/64748b?text=NHLS+Video+Player">
134
- Your browser does not support the video tag.
135
- </video>
136
-
137
- <div class="video-controls">
138
- <div class="flex items-center mb-2 px-2">
139
- <div class="progress-container flex-grow mx-2">
140
- <div class="progress-bar"></div>
 
141
  </div>
142
- <span class="time-display text-sm w-20 text-right">
143
- <span class="current-time">00:00</span> / <span class="duration">00:00</span>
144
- </span>
145
- </div>
146
-
147
- <div class="flex items-center justify-between px-2">
148
- <div class="flex items-center space-x-4">
149
- <button id="play-pause" class="text-white hover:text-blue-400">
150
- <i class="fas fa-play"></i>
151
- </button>
152
-
153
- <div class="flex items-center volume-control">
154
- <button class="volume-btn text-white hover:text-blue-400">
155
- <i class="fas fa-volume-up"></i>
156
- </button>
157
- <div class="volume-slider ml-2">
158
- <input type="range" min="0" max="1" step="0.01" value="1" class="w-full">
159
  </div>
160
  </div>
161
  </div>
162
-
163
- <div class="flex items-center space-x-4">
164
- <button id="fullscreen" class="text-white hover:text-blue-400">
165
- <i class="fas fa-expand"></i>
166
- </button>
167
- <button id="download-btn" class="download-btn bg-blue-600 hover:bg-blue-700 text-white px-4 py-1 rounded-full flex items-center">
168
- <i class="fas fa-download mr-2"></i> Save MP4
169
- </button>
170
- </div>
171
  </div>
172
- </div>
173
- </div>
174
-
175
- <div class="max-w-4xl mx-auto mt-8">
176
- <div class="bg-gray-800 rounded-lg p-6 shadow-lg">
177
- <h2 class="text-xl font-bold mb-4 flex items-center">
178
- <i class="fas fa-info-circle text-blue-500 mr-2"></i> Video Information
179
- </h2>
180
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
181
- <div>
182
- <p class="text-gray-400">Title:</p>
183
- <p class="font-medium">Big Buck Bunny Sample Video</p>
 
 
 
 
 
 
184
  </div>
185
- <div>
186
- <p class="text-gray-400">Duration:</p>
187
- <p class="font-medium"><span id="video-duration">00:00</span></p>
 
 
 
 
188
  </div>
189
- <div>
190
- <p class="text-gray-400">Format:</p>
191
- <p class="font-medium">M3U8</p>
 
192
  </div>
193
- <div>
194
- <p class="text-gray-400">Resolution:</p>
195
- <p class="font-medium">720p</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  </div>
197
  </div>
198
  </div>
199
-
200
- <div class="bg-gray-800 rounded-lg p-6 shadow-lg mt-6">
201
- <h2 class="text-xl font-bold mb-4 flex items-center">
202
- <i class="fas fa-cog text-blue-500 mr-2"></i> Settings
203
- </h2>
204
- <div class="space-y-4">
205
- <div>
206
- <label class="block text-gray-400 mb-2">Playback Speed</label>
207
- <select id="playback-speed" class="bg-gray-700 text-white rounded px-3 py-2 w-full md:w-64">
208
- <option value="0.5">0.5x</option>
209
- <option value="0.75">0.75x</option>
210
- <option value="1" selected>1x (Normal)</option>
211
- <option value="1.25">1.25x</option>
212
- <option value="1.5">1.5x</option>
213
- <option value="2">2x</option>
214
- </select>
215
  </div>
216
- <div>
217
- <label class="flex items-center space-x-2">
218
- <input type="checkbox" id="autoplay" class="rounded text-blue-500">
219
- <span>Autoplay next video</span>
220
- </label>
 
 
 
 
 
221
  </div>
222
  </div>
223
- </div>
224
- </div>
225
- </main>
226
 
227
- <footer class="bg-gray-800 py-4 px-6">
228
- <div class="container mx-auto text-center text-gray-400">
229
- <p>©All rights reserved.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  </div>
231
- </footer>
232
 
233
  <script>
234
  document.addEventListener('DOMContentLoaded', function() {
235
- // Video player elements
236
- const video = document.getElementById('video-player');
237
- const playPauseBtn = document.getElementById('play-pause');
238
- const progressBar = document.querySelector('.progress-bar');
239
- const progressContainer = document.querySelector('.progress-container');
240
- const currentTimeDisplay = document.querySelector('.current-time');
241
- const durationDisplay = document.querySelector('.duration');
242
- const volumeBtn = document.querySelector('.volume-btn');
243
- const volumeSlider = document.querySelector('.volume-slider input');
244
- const volumeIcon = document.querySelector('.volume-btn i');
245
- const fullscreenBtn = document.getElementById('fullscreen');
246
- const downloadBtn = document.getElementById('download-btn');
247
- const playbackSpeed = document.getElementById('playback-speed');
248
- const videoDuration = document.getElementById('video-duration');
249
- const themeToggle = document.getElementById('theme-toggle');
250
 
251
- // Format time as MM:SS
252
- function formatTime(seconds) {
253
- const minutes = Math.floor(seconds / 60);
254
- const remainingSeconds = Math.floor(seconds % 60);
255
- return `${minutes.toString().padStart(2, '0')}:${remainingSeconds.toString().padStart(2, '0')}`;
256
- }
 
 
 
 
 
 
 
 
257
 
258
- // Update progress bar
259
- function updateProgress() {
260
- const progress = (video.currentTime / video.duration) * 100;
261
- progressBar.style.width = `${progress}%`;
262
- currentTimeDisplay.textContent = formatTime(video.currentTime);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  }
264
 
265
- // Set video duration
266
- function setVideoDuration() {
267
- durationDisplay.textContent = formatTime(video.duration);
268
- videoDuration.textContent = formatTime(video.duration);
 
 
 
 
 
 
 
 
 
269
  }
270
 
271
- // Toggle play/pause
272
- function togglePlayPause() {
273
- if (video.paused) {
274
- video.play();
275
- playPauseBtn.innerHTML = '<i class="fas fa-pause"></i>';
276
- } else {
277
- video.pause();
278
- playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
 
 
 
 
279
  }
280
  }
281
 
282
- // Set progress on click
283
- function setProgress(e) {
284
- const width = this.clientWidth;
285
- const clickX = e.offsetX;
286
- const duration = video.duration;
287
- video.currentTime = (clickX / width) * duration;
288
  }
289
 
290
- // Set volume
291
- function setVolume() {
292
- video.volume = this.value;
 
 
 
293
 
294
- if (video.volume === 0) {
295
- volumeIcon.className = 'fas fa-volume-mute';
296
- } else if (video.volume < 0.5) {
297
- volumeIcon.className = 'fas fa-volume-down';
298
- } else {
299
- volumeIcon.className = 'fas fa-volume-up';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300
  }
301
  }
302
 
303
- // Toggle mute
304
- function toggleMute() {
305
- if (video.volume > 0) {
306
- video.volume = 0;
307
- volumeSlider.value = 0;
308
- volumeIcon.className = 'fas fa-volume-mute';
309
- } else {
310
- video.volume = 1;
311
- volumeSlider.value = 1;
312
- volumeIcon.className = 'fas fa-volume-up';
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  }
315
 
316
- // Toggle fullscreen
317
- function toggleFullscreen() {
318
- if (!document.fullscreenElement) {
319
- video.requestFullscreen().catch(err => {
320
- alert(`Error attempting to enable fullscreen: ${err.message}`);
321
- });
322
- } else {
323
- document.exitFullscreen();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  }
325
  }
326
 
327
- // Download video
328
- function downloadVideo() {
329
- const videoSrc = video.querySelector('source').src;
330
- const videoTitle = 'HLS_Video_' + new Date().toISOString().slice(0, 10) + '.mp4';
 
 
331
 
332
- // Create download link
333
- const link = document.createElement('a');
334
- link.href = videoSrc;
335
- link.download = videoTitle;
336
- document.body.appendChild(link);
337
- link.click();
338
- document.body.removeChild(link);
339
 
340
- // Show download notification
341
- const notification = document.createElement('div');
342
- notification.className = 'fixed bottom-4 right-4 bg-green-600 text-white px-4 py-2 rounded-lg shadow-lg flex items-center';
343
- notification.innerHTML = `
344
- <i class="fas fa-check-circle mr-2"></i>
345
- <span>Download started: ${videoTitle}</span>
346
- `;
347
- document.body.appendChild(notification);
 
 
 
 
 
 
 
 
 
348
 
349
- setTimeout(() => {
350
- notification.classList.add('opacity-0', 'transition-opacity', 'duration-500');
351
- setTimeout(() => notification.remove(), 500);
352
- }, 3000);
 
 
 
 
 
 
 
 
 
353
  }
354
 
355
- // Change playback speed
356
- function changePlaybackSpeed() {
357
- video.playbackRate = this.value;
 
 
 
358
  }
359
 
360
- // Toggle dark/light theme
361
- function toggleTheme() {
362
- const isDark = document.documentElement.classList.contains('dark');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
363
 
364
- if (isDark) {
365
- document.documentElement.classList.remove('dark');
366
- document.body.classList.remove('bg-gray-900');
367
- document.body.classList.add('bg-gray-100', 'text-gray-900');
368
- themeToggle.innerHTML = '<i class="fas fa-moon"></i>';
 
 
 
 
 
369
  } else {
370
- document.documentElement.classList.add('dark');
371
- document.body.classList.remove('bg-gray-100', 'text-gray-900');
372
- document.body.classList.add('bg-gray-900', 'text-white');
373
- themeToggle.innerHTML = '<i class="fas fa-sun"></i>';
374
  }
375
- }
376
 
377
- // Event listeners
378
- video.addEventListener('click', togglePlayPause);
379
- video.addEventListener('timeupdate', updateProgress);
380
- video.addEventListener('loadedmetadata', setVideoDuration);
381
- video.addEventListener('ended', () => {
382
- playPauseBtn.innerHTML = '<i class="fas fa-play"></i>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  });
384
 
385
- playPauseBtn.addEventListener('click', togglePlayPause);
386
- progressContainer.addEventListener('click', setProgress);
387
- volumeSlider.addEventListener('input', setVolume);
388
- volumeBtn.addEventListener('click', toggleMute);
389
- fullscreenBtn.addEventListener('click', toggleFullscreen);
390
- downloadBtn.addEventListener('click', downloadVideo);
391
- playbackSpeed.addEventListener('change', changePlaybackSpeed);
392
- themeToggle.addEventListener('click', toggleTheme);
393
 
394
- // Initialize volume icon
395
- setVolume.call(volumeSlider);
396
  });
397
-
398
-
399
  </script>
400
-
401
  <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=inoculatemedia/hls-player-and-recorder" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
402
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Advanced HLS Player with Recording & Upscaling</title>
7
  <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
  <style>
11
+ .timeline-handle {
12
+ width: 12px;
13
+ height: 20px;
14
+ background-color: #3b82f6;
15
+ cursor: ew-resize;
16
+ position: absolute;
17
+ top: -4px;
18
+ z-index: 10;
19
+ }
20
+ .timeline-range {
21
+ background-color: rgba(59, 130, 246, 0.3);
22
+ height: 12px;
23
+ position: absolute;
24
+ top: 0;
25
+ }
26
+ .waveform {
27
+ background: linear-gradient(90deg, #4b5563 0%, #6b7280 100%);
28
+ height: 60px;
29
  position: relative;
 
 
 
 
30
  overflow: hidden;
 
31
  }
32
+ .waveform-bar {
33
+ background-color: #d1d5db;
34
+ width: 2px;
35
  position: absolute;
36
  bottom: 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
+ .progress-indicator {
39
+ position: absolute;
40
+ top: 0;
41
+ left: 0;
42
+ width: 2px;
43
  height: 100%;
44
+ background-color: #ef4444;
45
+ z-index: 5;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
+ .upscale-preview {
48
+ transition: all 0.3s ease;
 
 
 
 
49
  }
50
+ .upscale-preview:hover {
51
+ transform: scale(1.05);
52
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);
53
  }
54
+ .glow {
55
+ animation: glow 2s infinite alternate;
 
 
56
  }
57
+ @keyframes glow {
58
+ from {
59
+ box-shadow: 0 0 5px rgba(59, 130, 246, 0.5);
60
+ }
61
+ to {
62
+ box-shadow: 0 0 15px rgba(59, 130, 246, 0.8);
63
+ }
 
 
 
 
 
64
  }
65
  </style>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  </head>
67
+ <body class="bg-gray-900 text-gray-100 min-h-screen">
68
+ <div class="container mx-auto px-4 py-8">
69
+ <div class="flex justify-between items-center mb-8">
70
+ <h1 class="text-3xl font-bold bg-gradient-to-r from-blue-500 to-purple-600 bg-clip-text text-transparent">
71
+ <i class="fas fa-video mr-2"></i>Advanced HLS Player
72
+ </h1>
73
+ <div class="flex space-x-4">
74
+ <button id="recordBtn" class="px-4 py-2 bg-red-600 hover:bg-red-700 rounded-lg flex items-center">
75
+ <i class="fas fa-circle mr-2"></i> Record
76
+ </button>
77
+ <button id="upscaleBtn" class="px-4 py-2 bg-purple-600 hover:bg-purple-700 rounded-lg flex items-center">
78
+ <i class="fas fa-expand mr-2"></i> Upscale
79
  </button>
80
  </div>
81
  </div>
 
82
 
83
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
84
+ <!-- Main Player Column -->
85
+ <div class="lg:col-span-2 space-y-6">
86
+ <!-- Video Player -->
87
+ <div class="relative bg-black rounded-xl overflow-hidden shadow-2xl">
88
+ <video id="video" controls class="w-full aspect-video"></video>
89
+ <div id="recordingIndicator" class="absolute top-4 right-4 bg-red-600 px-3 py-1 rounded-full hidden">
90
+ <span class="flex items-center">
91
+ <span class="h-2 w-2 bg-white rounded-full mr-2 animate-pulse"></span>
92
+ REC
93
+ </span>
94
  </div>
95
+ <div id="upscaleOverlay" class="absolute inset-0 bg-black bg-opacity-70 flex items-center justify-center hidden">
96
+ <div class="text-center p-6 bg-gray-800 rounded-lg max-w-md">
97
+ <div class="spinner mb-4">
98
+ <i class="fas fa-spinner fa-spin text-4xl text-purple-500"></i>
99
+ </div>
100
+ <h3 class="text-xl font-bold mb-2">Upscaling Video</h3>
101
+ <p class="text-gray-300">Processing with Real-ESRGAN...</p>
102
+ <div class="w-full bg-gray-700 h-2 mt-4 rounded-full overflow-hidden">
103
+ <div id="upscaleProgress" class="bg-purple-500 h-full" style="width: 0%"></div>
 
 
 
 
 
 
 
 
104
  </div>
105
  </div>
106
  </div>
 
 
 
 
 
 
 
 
 
107
  </div>
108
+
109
+ <!-- Timeline Controls -->
110
+ <div class="bg-gray-800 p-4 rounded-xl shadow-lg">
111
+ <div class="flex justify-between items-center mb-4">
112
+ <h3 class="font-semibold text-lg">
113
+ <i class="fas fa-cut mr-2 text-blue-400"></i>Clip Editor
114
+ </h3>
115
+ <div class="flex space-x-2">
116
+ <button id="setStartBtn" class="px-3 py-1 bg-blue-600 hover:bg-blue-700 rounded text-sm">
117
+ Set Start
118
+ </button>
119
+ <button id="setEndBtn" class="px-3 py-1 bg-blue-600 hover:bg-blue-700 rounded text-sm">
120
+ Set End
121
+ </button>
122
+ <button id="exportClipBtn" class="px-3 py-1 bg-green-600 hover:bg-green-700 rounded text-sm">
123
+ Export Clip
124
+ </button>
125
+ </div>
126
  </div>
127
+
128
+ <div class="relative waveform rounded-lg mb-2">
129
+ <div id="waveformBars"></div>
130
+ <div id="progressIndicator" class="progress-indicator"></div>
131
+ <div id="timelineRange" class="timeline-range hidden"></div>
132
+ <div id="startHandle" class="timeline-handle hidden"></div>
133
+ <div id="endHandle" class="timeline-handle hidden"></div>
134
  </div>
135
+
136
+ <div class="flex justify-between text-xs text-gray-400">
137
+ <span id="currentTime">00:00:00</span>
138
+ <span id="duration">00:00:00</span>
139
  </div>
140
+ </div>
141
+
142
+ <!-- Stream Controls -->
143
+ <div class="bg-gray-800 p-4 rounded-xl shadow-lg">
144
+ <h3 class="font-semibold text-lg mb-4">
145
+ <i class="fas fa-stream mr-2 text-blue-400"></i>Stream Controls
146
+ </h3>
147
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
148
+ <div>
149
+ <label class="block text-sm font-medium mb-1">HLS Stream URL</label>
150
+ <div class="flex">
151
+ <input id="streamUrl" type="text" value="https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8"
152
+ class="flex-1 px-3 py-2 bg-gray-700 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
153
+ <button id="loadStreamBtn" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-r-lg">
154
+ Load
155
+ </button>
156
+ </div>
157
+ </div>
158
+ <div>
159
+ <label class="block text-sm font-medium mb-1">Quality</label>
160
+ <select id="qualitySelect" class="w-full px-3 py-2 bg-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
161
+ <option value="auto">Auto</option>
162
+ <option value="high">High</option>
163
+ <option value="medium">Medium</option>
164
+ <option value="low">Low</option>
165
+ </select>
166
+ </div>
167
  </div>
168
  </div>
169
  </div>
170
+
171
+ <!-- Sidebar Column -->
172
+ <div class="space-y-6">
173
+ <!-- Recording Panel -->
174
+ <div class="bg-gray-800 p-4 rounded-xl shadow-lg">
175
+ <h3 class="font-semibold text-lg mb-4 flex items-center">
176
+ <i class="fas fa-record-vinyl mr-2 text-red-400"></i>Recordings
177
+ </h3>
178
+ <div id="recordingsList" class="space-y-2 max-h-60 overflow-y-auto">
179
+ <div class="text-center py-4 text-gray-500">
180
+ <i class="fas fa-folder-open text-2xl mb-2"></i>
181
+ <p>No recordings yet</p>
182
+ </div>
 
 
 
183
  </div>
184
+ <div class="mt-4 pt-4 border-t border-gray-700">
185
+ <label class="block text-sm font-medium mb-2">Recording Format</label>
186
+ <div class="flex space-x-2">
187
+ <button class="flex-1 px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg text-sm">
188
+ MP4
189
+ </button>
190
+ <button class="flex-1 px-3 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg text-sm">
191
+ WebM
192
+ </button>
193
+ </div>
194
  </div>
195
  </div>
 
 
 
196
 
197
+ <!-- Upscale Panel -->
198
+ <div class="bg-gray-800 p-4 rounded-xl shadow-lg">
199
+ <h3 class="font-semibold text-lg mb-4 flex items-center">
200
+ <i class="fas fa-expand mr-2 text-purple-400"></i>Upscale Settings
201
+ </h3>
202
+ <div class="space-y-4">
203
+ <div>
204
+ <label class="block text-sm font-medium mb-1">Model</label>
205
+ <select class="w-full px-3 py-2 bg-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500">
206
+ <option>Real-ESRGAN (General)</option>
207
+ <option>Real-ESRGAN (Anime)</option>
208
+ <option>Real-ESRGAN+</option>
209
+ </select>
210
+ </div>
211
+ <div>
212
+ <label class="block text-sm font-medium mb-1">Scale Factor</label>
213
+ <select class="w-full px-3 py-2 bg-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-purple-500">
214
+ <option>2x</option>
215
+ <option>4x</option>
216
+ <option>8x (Experimental)</option>
217
+ </select>
218
+ </div>
219
+ <div>
220
+ <label class="block text-sm font-medium mb-1">Denoise Level</label>
221
+ <input type="range" min="0" max="100" value="50" class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
222
+ </div>
223
+ </div>
224
+ </div>
225
+
226
+ <!-- Upscale Preview -->
227
+ <div id="upscalePreviewContainer" class="bg-gray-800 p-4 rounded-xl shadow-lg hidden">
228
+ <h3 class="font-semibold text-lg mb-4 flex items-center">
229
+ <i class="fas fa-eye mr-2 text-green-400"></i>Upscale Preview
230
+ </h3>
231
+ <div class="grid grid-cols-2 gap-2 mb-4">
232
+ <div>
233
+ <p class="text-xs text-center mb-1">Original</p>
234
+ <img id="originalPreview" src="" class="w-full rounded border border-gray-600 upscale-preview">
235
+ </div>
236
+ <div>
237
+ <p class="text-xs text-center mb-1">Upscaled</p>
238
+ <img id="upscaledPreview" src="" class="w-full rounded border border-purple-500 border-2 upscale-preview glow">
239
+ </div>
240
+ </div>
241
+ <button class="w-full py-2 bg-purple-600 hover:bg-purple-700 rounded-lg">
242
+ Apply to Entire Video
243
+ </button>
244
+ </div>
245
+ </div>
246
  </div>
247
+ </div>
248
 
249
  <script>
250
  document.addEventListener('DOMContentLoaded', function() {
251
+ // Video elements
252
+ const video = document.getElementById('video');
253
+ const streamUrlInput = document.getElementById('streamUrl');
254
+ const loadStreamBtn = document.getElementById('loadStreamBtn');
255
+ const qualitySelect = document.getElementById('qualitySelect');
256
+ const currentTimeDisplay = document.getElementById('currentTime');
257
+ const durationDisplay = document.getElementById('duration');
 
 
 
 
 
 
 
 
258
 
259
+ // Recording elements
260
+ const recordBtn = document.getElementById('recordBtn');
261
+ const recordingIndicator = document.getElementById('recordingIndicator');
262
+ const recordingsList = document.getElementById('recordingsList');
263
+
264
+ // Timeline elements
265
+ const waveformBars = document.getElementById('waveformBars');
266
+ const progressIndicator = document.getElementById('progressIndicator');
267
+ const timelineRange = document.getElementById('timelineRange');
268
+ const startHandle = document.getElementById('startHandle');
269
+ const endHandle = document.getElementById('endHandle');
270
+ const setStartBtn = document.getElementById('setStartBtn');
271
+ const setEndBtn = document.getElementById('setEndBtn');
272
+ const exportClipBtn = document.getElementById('exportClipBtn');
273
 
274
+ // Upscale elements
275
+ const upscaleBtn = document.getElementById('upscaleBtn');
276
+ const upscaleOverlay = document.getElementById('upscaleOverlay');
277
+ const upscaleProgress = document.getElementById('upscaleProgress');
278
+ const upscalePreviewContainer = document.getElementById('upscalePreviewContainer');
279
+ const originalPreview = document.getElementById('originalPreview');
280
+ const upscaledPreview = document.getElementById('upscaledPreview');
281
+
282
+ // Player state
283
+ let hls = null;
284
+ let isRecording = false;
285
+ let mediaRecorder = null;
286
+ let recordedChunks = [];
287
+ let startTime = 0;
288
+ let endTime = 0;
289
+ let isDraggingStart = false;
290
+ let isDraggingEnd = false;
291
+ let isDraggingProgress = false;
292
+
293
+ // Initialize HLS player
294
+ function initHlsPlayer(url) {
295
+ if (hls) {
296
+ hls.destroy();
297
+ }
298
+
299
+ if (Hls.isSupported()) {
300
+ hls = new Hls();
301
+ hls.loadSource(url);
302
+ hls.attachMedia(video);
303
+ hls.on(Hls.Events.MANIFEST_PARSED, function() {
304
+ console.log('Manifest parsed');
305
+ video.play();
306
+ generateWaveform();
307
+ });
308
+
309
+ hls.on(Hls.Events.ERROR, function(event, data) {
310
+ console.error('HLS Error:', data);
311
+ });
312
+ } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
313
+ // For Safari
314
+ video.src = url;
315
+ video.addEventListener('loadedmetadata', function() {
316
+ video.play();
317
+ generateWaveform();
318
+ });
319
+ }
320
  }
321
 
322
+ // Generate fake waveform for demo
323
+ function generateWaveform() {
324
+ waveformBars.innerHTML = '';
325
+ const width = waveformBars.clientWidth;
326
+ const height = waveformBars.clientHeight;
327
+
328
+ for (let i = 0; i < 200; i++) {
329
+ const bar = document.createElement('div');
330
+ bar.className = 'waveform-bar';
331
+ bar.style.left = `${(i / 200) * 100}%`;
332
+ bar.style.height = `${Math.random() * 80 + 20}%`;
333
+ waveformBars.appendChild(bar);
334
+ }
335
  }
336
 
337
+ // Update time display
338
+ function updateTimeDisplay() {
339
+ const currentTime = video.currentTime || 0;
340
+ const duration = video.duration || 0;
341
+
342
+ currentTimeDisplay.textContent = formatTime(currentTime);
343
+ durationDisplay.textContent = formatTime(duration);
344
+
345
+ // Update progress indicator
346
+ if (duration > 0) {
347
+ const progressPercent = (currentTime / duration) * 100;
348
+ progressIndicator.style.left = `${progressPercent}%`;
349
  }
350
  }
351
 
352
+ // Format time as HH:MM:SS
353
+ function formatTime(seconds) {
354
+ const date = new Date(0);
355
+ date.setSeconds(seconds);
356
+ return date.toISOString().substr(11, 8);
 
357
  }
358
 
359
+ // Start recording
360
+ async function startRecording() {
361
+ if (!video.src) {
362
+ alert('No video stream to record');
363
+ return;
364
+ }
365
 
366
+ try {
367
+ // In a real implementation, we would use MediaRecorder with WebCodecs
368
+ // This is a simplified version for demonstration
369
+ recordedChunks = [];
370
+ isRecording = true;
371
+ recordBtn.innerHTML = '<i class="fas fa-stop mr-2"></i> Stop';
372
+ recordBtn.classList.remove('bg-red-600');
373
+ recordBtn.classList.add('bg-gray-600');
374
+ recordingIndicator.classList.remove('hidden');
375
+
376
+ // Simulate recording with WebCodecs
377
+ console.log('Recording started with WebCodecs (simulated)');
378
+
379
+ // In a real implementation, we would:
380
+ // 1. Create a MediaStream from the video element
381
+ // 2. Use MediaRecorder with WebCodecs to encode to MP4
382
+ // 3. Process frames with GPU acceleration
383
+
384
+ } catch (error) {
385
+ console.error('Recording error:', error);
386
+ stopRecording();
387
  }
388
  }
389
 
390
+ // Stop recording
391
+ function stopRecording() {
392
+ isRecording = false;
393
+ recordBtn.innerHTML = '<i class="fas fa-circle mr-2"></i> Record';
394
+ recordBtn.classList.add('bg-red-600');
395
+ recordBtn.classList.remove('bg-gray-600');
396
+ recordingIndicator.classList.add('hidden');
397
+
398
+ // Simulate saving the recording
399
+ console.log('Recording stopped');
400
+
401
+ // In a real implementation, we would:
402
+ // 1. Finalize the MP4 file
403
+ // 2. Save to disk or offer download
404
+
405
+ // Add to recordings list for demo
406
+ addRecordingToUI();
407
+ }
408
+
409
+ // Add recording to UI (demo)
410
+ function addRecordingToUI() {
411
+ if (recordingsList.querySelector('.text-center')) {
412
+ recordingsList.innerHTML = '';
413
  }
414
+
415
+ const recordingItem = document.createElement('div');
416
+ recordingItem.className = 'flex items-center justify-between p-2 bg-gray-700 rounded-lg';
417
+ recordingItem.innerHTML = `
418
+ <div class="flex items-center">
419
+ <i class="fas fa-file-video text-blue-400 mr-3"></i>
420
+ <div>
421
+ <p class="font-medium">Recording_${new Date().toLocaleTimeString()}</p>
422
+ <p class="text-xs text-gray-400">00:30:45 - MP4</p>
423
+ </div>
424
+ </div>
425
+ <div class="flex space-x-2">
426
+ <button class="p-1 text-blue-400 hover:text-blue-300">
427
+ <i class="fas fa-play"></i>
428
+ </button>
429
+ <button class="p-1 text-green-400 hover:text-green-300">
430
+ <i class="fas fa-download"></i>
431
+ </button>
432
+ <button class="p-1 text-red-400 hover:text-red-300">
433
+ <i class="fas fa-trash"></i>
434
+ </button>
435
+ </div>
436
+ `;
437
+
438
+ recordingsList.prepend(recordingItem);
439
  }
440
 
441
+ // Set clip start time
442
+ function setClipStart() {
443
+ startTime = video.currentTime;
444
+ updateClipRangeUI();
445
+ }
446
+
447
+ // Set clip end time
448
+ function setClipEnd() {
449
+ endTime = video.currentTime;
450
+ updateClipRangeUI();
451
+ }
452
+
453
+ // Update the clip range UI
454
+ function updateClipRangeUI() {
455
+ if (video.duration) {
456
+ const startPercent = (startTime / video.duration) * 100;
457
+ const endPercent = (endTime / video.duration) * 100;
458
+
459
+ if (startTime > 0 && endTime > 0 && startTime < endTime) {
460
+ timelineRange.style.left = `${startPercent}%`;
461
+ timelineRange.style.width = `${endPercent - startPercent}%`;
462
+ timelineRange.classList.remove('hidden');
463
+
464
+ startHandle.style.left = `${startPercent}%`;
465
+ startHandle.classList.remove('hidden');
466
+
467
+ endHandle.style.left = `${endPercent}%`;
468
+ endHandle.classList.remove('hidden');
469
+ }
470
  }
471
  }
472
 
473
+ // Export clip
474
+ function exportClip() {
475
+ if (startTime >= endTime || startTime < 0 || endTime < 0) {
476
+ alert('Please set a valid start and end time');
477
+ return;
478
+ }
479
 
480
+ // In a real implementation, we would:
481
+ // 1. Use WebCodecs to extract the clip between startTime and endTime
482
+ // 2. Encode as MP4
483
+ // 3. Offer download
 
 
 
484
 
485
+ alert(`Clip exported from ${formatTime(startTime)} to ${formatTime(endTime)}`);
486
+ console.log('Exporting clip with WebCodecs (simulated)');
487
+ }
488
+
489
+ // Upscale video
490
+ function upscaleVideo() {
491
+ if (!video.src) {
492
+ alert('No video loaded');
493
+ return;
494
+ }
495
+
496
+ // Show upscale overlay
497
+ upscaleOverlay.classList.remove('hidden');
498
+
499
+ // Simulate upscaling with Real-ESRGAN
500
+ // In a real implementation, this would require server-side processing
501
+ // or a WebAssembly port of Real-ESRGAN
502
 
503
+ let progress = 0;
504
+ const interval = setInterval(() => {
505
+ progress += 5;
506
+ upscaleProgress.style.width = `${progress}%`;
507
+
508
+ if (progress >= 100) {
509
+ clearInterval(interval);
510
+ setTimeout(() => {
511
+ upscaleOverlay.classList.add('hidden');
512
+ showUpscalePreview();
513
+ }, 500);
514
+ }
515
+ }, 200);
516
  }
517
 
518
+ // Show upscale preview
519
+ function showUpscalePreview() {
520
+ // For demo purposes, we'll use placeholder images
521
+ originalPreview.src = 'https://via.placeholder.com/200x112/4b5563/ffffff?text=Original';
522
+ upscaledPreview.src = 'https://via.placeholder.com/400x224/7e22ce/ffffff?text=Upscaled+4x';
523
+ upscalePreviewContainer.classList.remove('hidden');
524
  }
525
 
526
+ // Event listeners
527
+ loadStreamBtn.addEventListener('click', () => {
528
+ const url = streamUrlInput.value.trim();
529
+ if (url) {
530
+ initHlsPlayer(url);
531
+ }
532
+ });
533
+
534
+ qualitySelect.addEventListener('change', () => {
535
+ if (hls) {
536
+ const level = qualitySelect.value;
537
+ if (level === 'auto') {
538
+ hls.currentLevel = -1;
539
+ } else {
540
+ const levels = { high: 0, medium: 1, low: 2 };
541
+ hls.currentLevel = levels[level];
542
+ }
543
+ }
544
+ });
545
+
546
+ recordBtn.addEventListener('click', () => {
547
+ if (isRecording) {
548
+ stopRecording();
549
+ } else {
550
+ startRecording();
551
+ }
552
+ });
553
+
554
+ video.addEventListener('timeupdate', updateTimeDisplay);
555
+
556
+ setStartBtn.addEventListener('click', setClipStart);
557
+ setEndBtn.addEventListener('click', setClipEnd);
558
+ exportClipBtn.addEventListener('click', exportClip);
559
+
560
+ upscaleBtn.addEventListener('click', upscaleVideo);
561
+
562
+ // Timeline dragging
563
+ waveformBars.addEventListener('mousedown', (e) => {
564
+ if (!video.duration) return;
565
 
566
+ const rect = waveformBars.getBoundingClientRect();
567
+ const percent = (e.clientX - rect.left) / rect.width;
568
+ const time = percent * video.duration;
569
+
570
+ if (startHandle.classList.contains('hidden') === false &&
571
+ Math.abs(percent - parseFloat(startHandle.style.left) / 100) < 0.02) {
572
+ isDraggingStart = true;
573
+ } else if (endHandle.classList.contains('hidden') === false &&
574
+ Math.abs(percent - parseFloat(endHandle.style.left) / 100) < 0.02) {
575
+ isDraggingEnd = true;
576
  } else {
577
+ isDraggingProgress = true;
578
+ video.currentTime = time;
 
 
579
  }
580
+ });
581
 
582
+ document.addEventListener('mousemove', (e) => {
583
+ if (!video.duration) return;
584
+
585
+ const rect = waveformBars.getBoundingClientRect();
586
+ const percent = Math.min(1, Math.max(0, (e.clientX - rect.left) / rect.width));
587
+ const time = percent * video.duration;
588
+
589
+ if (isDraggingStart) {
590
+ startTime = time;
591
+ if (startTime > endTime && endTime > 0) {
592
+ startTime = endTime - 0.1;
593
+ }
594
+ updateClipRangeUI();
595
+ } else if (isDraggingEnd) {
596
+ endTime = time;
597
+ if (endTime < startTime) {
598
+ endTime = startTime + 0.1;
599
+ }
600
+ updateClipRangeUI();
601
+ } else if (isDraggingProgress) {
602
+ video.currentTime = time;
603
+ }
604
  });
605
 
606
+ document.addEventListener('mouseup', () => {
607
+ isDraggingStart = false;
608
+ isDraggingEnd = false;
609
+ isDraggingProgress = false;
610
+ });
 
 
 
611
 
612
+ // Initialize with default stream
613
+ initHlsPlayer(streamUrlInput.value);
614
  });
 
 
615
  </script>
 
616
  <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=inoculatemedia/hls-player-and-recorder" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
617
  </html>
prompts.txt CHANGED
@@ -0,0 +1 @@
 
 
1
+ hls player with record to mp4 with webcodecs gpu, clip cutting, upscale with real esrgan