Subham9126 commited on
Commit
2f160a3
·
verified ·
1 Parent(s): 3f097bd

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +111 -460
index.html CHANGED
@@ -4,479 +4,130 @@
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>HF Video Library</title>
7
- <script src="https://cdn.tailwindcss.com"></script>
8
- <link rel="stylesheet" href="https://cdn.plyr.io/3.7.8/plyr.css" />
9
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
10
  <style>
11
- /* Custom CSS for animations and overrides */
12
- .fade-in {
13
- animation: fadeIn 0.3s ease-in-out;
14
- }
15
-
16
- @keyframes fadeIn {
17
- from { opacity: 0; }
18
- to { opacity: 1; }
19
- }
20
-
21
- .video-card:hover .video-overlay {
22
- opacity: 1;
23
- }
24
-
25
- .video-overlay {
26
- transition: opacity 0.2s ease;
27
- }
28
-
29
- .player-modal {
30
- transition: all 0.3s ease;
31
- }
32
-
33
- .player-modal.hidden {
34
- opacity: 0;
35
- visibility: hidden;
36
- transform: translateY(20px);
37
- }
38
-
39
- .player-modal.visible {
40
- opacity: 1;
41
- visibility: visible;
42
- transform: translateY(0);
43
- }
44
-
45
- /* Custom scrollbar */
46
- ::-webkit-scrollbar {
47
- width: 8px;
48
- height: 8px;
49
- }
50
-
51
- ::-webkit-scrollbar-track {
52
- background: #f1f1f1;
53
- border-radius: 10px;
54
- }
55
-
56
- ::-webkit-scrollbar-thumb {
57
- background: #888;
58
- border-radius: 10px;
59
- }
60
-
61
- ::-webkit-scrollbar-thumb:hover {
62
- background: #555;
63
- }
64
-
65
- /* Loading spinner */
66
- .spinner {
67
- width: 40px;
68
- height: 40px;
69
- border: 4px solid rgba(0, 0, 0, 0.1);
70
- border-left-color: #3b82f6;
71
- border-radius: 50%;
72
- animation: spin 1s linear infinite;
73
- }
74
-
75
- @keyframes spin {
76
- to { transform: rotate(360deg); }
77
  }
78
  </style>
79
  </head>
80
- <body class="bg-gray-100 font-sans antialiased">
81
- <!-- Header -->
82
- <header class="bg-gradient-to-r from-blue-600 to-indigo-700 shadow-lg">
83
- <div class="container mx-auto px-4 py-6">
84
- <div class="flex flex-col md:flex-row justify-between items-center">
85
- <div class="flex items-center mb-4 md:mb-0">
86
- <i class="fas fa-video text-white text-2xl mr-3"></i>
87
- <h1 class="text-2xl md:text-3xl font-bold text-white">HF Video Library</h1>
88
- </div>
89
- <div class="relative w-full md:w-64">
90
- <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
91
- <i class="fas fa-search text-gray-400"></i>
92
- </div>
93
- <input
94
- type="text"
95
- id="searchInput"
96
- placeholder="Search videos..."
97
- class="w-full pl-10 pr-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-400 bg-white bg-opacity-90 text-gray-800 placeholder-gray-500 transition duration-200"
98
- >
99
- </div>
100
- </div>
101
- </div>
102
- </header>
103
-
104
- <!-- Main Content -->
105
- <main class="container mx-auto px-4 py-8">
106
- <!-- Loading State -->
107
- <div id="loadingState" class="flex flex-col items-center justify-center py-20">
108
- <div class="spinner mb-4"></div>
109
- <p class="text-gray-600 text-lg">Loading video library...</p>
110
- </div>
111
-
112
- <!-- Error State -->
113
- <div id="errorState" class="hidden flex-col items-center justify-center py-20 text-center">
114
- <i class="fas fa-exclamation-triangle text-red-500 text-5xl mb-4"></i>
115
- <h2 class="text-2xl font-semibold text-gray-800 mb-2">Failed to load videos</h2>
116
- <p class="text-gray-600 mb-6 max-w-md">We couldn't load the video library. Please check your internet connection and try again.</p>
117
- <button id="retryButton" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-2 rounded-lg transition duration-200">
118
- <i class="fas fa-sync-alt mr-2"></i> Retry
119
- </button>
120
- </div>
121
-
122
- <!-- Video Grid -->
123
- <div id="videoGrid" class="hidden">
124
- <div class="flex justify-between items-center mb-6">
125
- <h2 class="text-xl font-semibold text-gray-800">
126
- <span id="videoCount">0</span> videos available
127
- </h2>
128
- <div class="flex items-center space-x-2">
129
- <span class="text-sm text-gray-600">Items per page:</span>
130
- <select id="itemsPerPage" class="border rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500">
131
- <option value="12">12</option>
132
- <option value="24">24</option>
133
- <option value="48">48</option>
134
- </select>
135
- </div>
136
- </div>
137
-
138
- <div id="videoContainer" class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6"></div>
139
-
140
- <!-- Pagination -->
141
- <div id="pagination" class="mt-10 flex justify-center">
142
- <nav class="flex items-center space-x-1">
143
- <button id="prevPage" class="px-3 py-1 rounded border bg-white text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed">
144
- <i class="fas fa-chevron-left"></i>
145
- </button>
146
-
147
- <div id="pageNumbers" class="flex space-x-1"></div>
148
-
149
- <button id="nextPage" class="px-3 py-1 rounded border bg-white text-gray-700 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed">
150
- <i class="fas fa-chevron-right"></i>
151
- </button>
152
- </nav>
153
- </div>
154
- </div>
155
- </main>
156
-
157
- <!-- Video Player Modal -->
158
- <div id="playerModal" class="player-modal hidden fixed inset-0 z-50 overflow-y-auto bg-black bg-opacity-75 flex items-center justify-center p-4">
159
- <div class="relative w-full max-w-4xl bg-white rounded-xl shadow-2xl overflow-hidden">
160
- <div class="absolute top-4 right-4 z-10">
161
- <button id="closePlayer" class="bg-gray-800 bg-opacity-70 text-white rounded-full p-2 hover:bg-opacity-100 transition duration-200">
162
- <i class="fas fa-times"></i>
163
- </button>
164
- </div>
165
-
166
- <div class="aspect-w-16 aspect-h-9 bg-black">
167
- <div id="playerContainer" class="w-full">
168
- <video id="player" playsinline controls></video>
169
- </div>
170
- </div>
171
-
172
- <div class="p-4 bg-gray-50">
173
- <h2 id="videoTitle" class="text-xl font-semibold text-gray-800 mb-2"></h2>
174
- <div class="flex justify-between items-center text-sm text-gray-600">
175
- <div id="videoInfo"></div>
176
- <div id="videoActions" class="flex space-x-3">
177
- <button id="fullscreenBtn" class="hover:text-blue-600 transition duration-200">
178
- <i class="fas fa-expand mr-1"></i> Fullscreen
179
- </button>
180
- </div>
181
- </div>
182
- </div>
183
- </div>
184
  </div>
185
-
186
- <!-- Footer -->
187
- <footer class="bg-gray-800 text-white py-6">
188
- <div class="container mx-auto px-4">
189
- <div class="flex flex-col md:flex-row justify-between items-center">
190
- <div class="mb-4 md:mb-0">
191
- <p>&copy; 2023 HF Video Library. All rights reserved.</p>
192
- </div>
193
- <div class="flex space-x-4">
194
- <a href="#" class="hover:text-blue-300 transition duration-200">
195
- <i class="fab fa-github"></i>
196
- </a>
197
- <a href="#" class="hover:text-blue-300 transition duration-200">
198
- <i class="fab fa-twitter"></i>
199
- </a>
200
- <a href="#" class="hover:text-blue-300 transition duration-200">
201
- <i class="fas fa-envelope"></i>
202
- </a>
203
- </div>
204
- </div>
205
- </div>
206
- </footer>
207
-
208
- <script src="https://cdn.plyr.io/3.7.8/plyr.js"></script>
209
  <script>
210
- // Configuration
211
- const config = {
212
- databaseUrl: 'database.json',
213
- itemsPerPage: 12,
214
- currentPage: 1,
215
- totalPages: 1,
216
- videos: [],
217
- filteredVideos: [],
218
- player: null
219
- };
220
-
221
- // DOM Elements
222
- const elements = {
223
- loadingState: document.getElementById('loadingState'),
224
- errorState: document.getElementById('errorState'),
225
- videoGrid: document.getElementById('videoGrid'),
226
- videoContainer: document.getElementById('videoContainer'),
227
- videoCount: document.getElementById('videoCount'),
228
- pagination: document.getElementById('pagination'),
229
- prevPage: document.getElementById('prevPage'),
230
- nextPage: document.getElementById('nextPage'),
231
- pageNumbers: document.getElementById('pageNumbers'),
232
- itemsPerPage: document.getElementById('itemsPerPage'),
233
- searchInput: document.getElementById('searchInput'),
234
- playerModal: document.getElementById('playerModal'),
235
- closePlayer: document.getElementById('closePlayer'),
236
- playerContainer: document.getElementById('playerContainer'),
237
- videoTitle: document.getElementById('videoTitle'),
238
- videoInfo: document.getElementById('videoInfo'),
239
- retryButton: document.getElementById('retryButton'),
240
- fullscreenBtn: document.getElementById('fullscreenBtn')
241
- };
242
-
243
- // Initialize the application
244
- document.addEventListener('DOMContentLoaded', () => {
245
- fetchVideoData();
246
-
247
- // Event listeners
248
- elements.searchInput.addEventListener('input', handleSearch);
249
- elements.itemsPerPage.addEventListener('change', handleItemsPerPageChange);
250
- elements.prevPage.addEventListener('click', goToPrevPage);
251
- elements.nextPage.addEventListener('click', goToNextPage);
252
- elements.closePlayer.addEventListener('click', closeVideoPlayer);
253
- elements.retryButton.addEventListener('click', fetchVideoData);
254
- elements.fullscreenBtn.addEventListener('click', toggleFullscreen);
255
-
256
- // Keyboard shortcuts for player (when modal is open)
257
- document.addEventListener('keydown', (e) => {
258
- if (elements.playerModal.classList.contains('visible') && config.player) {
259
- // Space for play/pause
260
- if (e.code === 'Space') {
261
- e.preventDefault();
262
- if (config.player.paused) {
263
- config.player.play();
264
- } else {
265
- config.player.pause();
266
- }
267
- }
268
-
269
- // F for fullscreen
270
- if (e.code === 'KeyF') {
271
- e.preventDefault();
272
- toggleFullscreen();
273
- }
274
-
275
- // M for mute
276
- if (e.code === 'KeyM') {
277
- e.preventDefault();
278
- config.player.muted = !config.player.muted;
279
- }
280
-
281
- // Arrow keys for seeking
282
- if (e.code === 'ArrowLeft') {
283
- e.preventDefault();
284
- config.player.currentTime -= 5;
285
- }
286
-
287
- if (e.code === 'ArrowRight') {
288
- e.preventDefault();
289
- config.player.currentTime += 5;
290
- }
291
- }
292
  });
293
- });
294
-
295
- // Fetch video data from JSON
296
- async function fetchVideoData() {
297
- try {
298
- showLoading();
299
-
300
- const response = await fetch(config.databaseUrl);
301
-
302
- if (!response.ok) {
303
- throw new Error('Failed to fetch video data');
304
- }
305
-
306
- const data = await response.json();
307
-
308
- if (!data.videos || !Array.isArray(data.videos)) {
309
- throw new Error('Invalid video data format');
310
- }
311
-
312
- // Filter out invalid video entries
313
- config.videos = data.videos.filter(video => {
314
- return video.title && video.url;
315
- });
316
-
317
- config.filteredVideos = [...config.videos];
318
-
319
- // Calculate total pages
320
- config.totalPages = Math.ceil(config.filteredVideos.length / config.itemsPerPage);
321
-
322
- // Render UI
323
- updateVideoCount();
324
- renderVideoGrid();
325
- renderPagination();
326
-
327
- hideLoading();
328
- showVideoGrid();
329
- } catch (error) {
330
- console.error('Error loading video data:', error);
331
- hideLoading();
332
- showError();
333
- }
334
- }
335
-
336
- // Show loading state
337
- function showLoading() {
338
- elements.loadingState.classList.remove('hidden');
339
- elements.errorState.classList.add('hidden');
340
- elements.videoGrid.classList.add('hidden');
341
  }
342
 
343
- // Hide loading state
344
- function hideLoading() {
345
- elements.loadingState.classList.add('hidden');
346
- }
347
-
348
- // Show error state
349
- function showError() {
350
- elements.errorState.classList.remove('hidden');
351
- }
352
-
353
- // Show video grid
354
- function showVideoGrid() {
355
- elements.videoGrid.classList.remove('hidden');
356
- }
357
 
358
- // Handle search input
359
- function handleSearch() {
360
- const searchTerm = elements.searchInput.value.toLowerCase();
361
-
362
- if (searchTerm === '') {
363
- config.filteredVideos = [...config.videos];
364
- } else {
365
- config.filteredVideos = config.videos.filter(video =>
366
- video.title.toLowerCase().includes(searchTerm)
367
- );
368
  }
369
-
370
- // Reset to first page
371
- config.currentPage = 1;
372
- config.totalPages = Math.ceil(config.filteredVideos.length / config.itemsPerPage);
373
-
374
- updateVideoCount();
375
- renderVideoGrid();
376
- renderPagination();
377
- }
378
-
379
- // Handle items per page change
380
- function handleItemsPerPageChange() {
381
- config.itemsPerPage = parseInt(elements.itemsPerPage.value);
382
- config.currentPage = 1;
383
- config.totalPages = Math.ceil(config.filteredVideos.length / config.itemsPerPage);
384
-
385
- renderVideoGrid();
386
- renderPagination();
387
- }
388
-
389
- // Update video count display
390
- function updateVideoCount() {
391
- elements.videoCount.textContent = config.filteredVideos.length;
392
- }
393
-
394
- // Render video grid
395
- function renderVideoGrid() {
396
- elements.videoContainer.innerHTML = '';
397
-
398
- const startIndex = (config.currentPage - 1) * config.itemsPerPage;
399
- const endIndex = startIndex + config.itemsPerPage;
400
- const videosToDisplay = config.filteredVideos.slice(startIndex, endIndex);
401
-
402
- if (videosToDisplay.length === 0) {
403
- elements.videoContainer.innerHTML = `
404
- <div class="col-span-full text-center py-10">
405
- <i class="fas fa-video-slash text-4xl text-gray-400 mb-4"></i>
406
- <h3 class="text-xl font-medium text-gray-600">No videos found</h3>
407
- <p class="text-gray-500 mt-2">Try adjusting your search or filters</p>
408
- </div>
409
- `;
410
- return;
411
  }
412
-
413
- videosToDisplay.forEach(video => {
414
- const videoCard = document.createElement('div');
415
- videoCard.className = 'bg-white rounded-xl shadow-md overflow-hidden transition-transform duration-200 hover:shadow-lg hover:-translate-y-1';
416
- videoCard.innerHTML = `
417
- <div class="relative cursor-pointer" onclick="playVideo('${video.url}', '${escapeHtml(video.title)}')">
418
- <div class="aspect-w-16 aspect-h-9 bg-gray-200 relative">
419
- <div class="absolute inset-0 flex items-center justify-center">
420
- <i class="fas fa-play text-white text-4xl opacity-70"></i>
421
- </div>
422
- <img src="${video.thumbnail || 'https://via.placeholder.com/320x180?text=No+Thumbnail'}"
423
- alt="${escapeHtml(video.title)}"
424
- class="w-full h-full object-cover">
425
- </div>
426
- <div class="p-4">
427
- <h3 class="font-medium text-gray-800 truncate">${escapeHtml(video.title)}</h3>
428
- </div>
429
- <div class="absolute inset-0 bg-black bg-opacity-0 hover:bg-opacity-20 transition duration-200"></div>
430
- </div>
431
- `;
432
- elements.videoContainer.appendChild(videoCard);
433
- });
434
- }
435
 
436
- // Render pagination controls
437
- function renderPagination() {
438
- elements.pageNumbers.innerHTML = '';
439
-
440
- // Disable prev/next buttons if on first/last page
441
- elements.prevPage.disabled = config.currentPage === 1;
442
- elements.nextPage.disabled = config.currentPage === config.totalPages;
443
-
444
- // Always show first page
445
- addPageButton(1);
446
-
447
- // Show ellipsis if needed before current page
448
- if (config.currentPage > 3) {
449
- addEllipsis();
450
- }
451
-
452
- // Show pages around current page
453
- const startPage = Math.max(2, config.currentPage - 1);
454
- const endPage = Math.min(config.totalPages - 1, config.currentPage + 1);
455
-
456
- for (let i = startPage; i <= endPage; i++) {
457
- addPageButton(i);
458
- }
459
-
460
- // Show ellipsis if needed after current page
461
- if (config.currentPage < config.totalPages - 2) {
462
- addEllipsis();
463
- }
464
-
465
- // Always show last page if different from first
466
- if (config.totalPages > 1) {
467
- addPageButton(config.totalPages);
468
  }
469
  }
470
-
471
- // Helper to add a page button
472
- function addPageButton(pageNumber) {
473
- const button = document.createElement('button');
474
- button.className = `px-3 py-1 rounded border ${config.currentPage === pageNumber ? 'bg-blue-600 text-white border-blue-600' : 'bg-white text-gray-700 hover:bg-gray-50'}`;
475
- button.textContent = pageNumber;
476
- button.addEventListener('click', () => goToPage(pageNumber));
477
- elements.pageNumbers.appendChild(button);
478
- }
479
-
480
- // Helper to add ellipsis
481
- function addEll
482
  </html>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>HF Video Library</title>
 
 
 
7
  <style>
8
+ /* Custom CSS for the application */
9
+ #search-bar {
10
+ margin: 20px;
11
+ text-align: center;
12
+ }
13
+ #search-input {
14
+ padding: 10px;
15
+ width: 300px;
16
+ }
17
+ #video-grid {
18
+ display: grid;
19
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
20
+ gap: 20px;
21
+ padding: 20px;
22
+ }
23
+ .video-card {
24
+ border: 1px solid #ccc;
25
+ padding: 10px;
26
+ text-align: center;
27
+ cursor: pointer;
28
+ }
29
+ #video-modal {
30
+ position: fixed;
31
+ top: 0;
32
+ left: 0;
33
+ width: 100%;
34
+ height: 100%;
35
+ background: rgba(0,0,0,0.8);
36
+ display: flex;
37
+ justify-content: center;
38
+ align-items: center;
39
+ }
40
+ #player {
41
+ width: 80%;
42
+ height: 80%;
43
+ }
44
+ #close-modal {
45
+ position: absolute;
46
+ top: 20px;
47
+ right: 20px;
48
+ background: white;
49
+ border: none;
50
+ padding: 10px;
51
+ cursor: pointer;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  }
53
  </style>
54
  </head>
55
+ <body>
56
+ <div id="search-bar">
57
+ <input type="text" id="search-input" placeholder="Search videos...">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  </div>
59
+ <div id="video-grid">
60
+ <!-- Videos will be rendered here -->
61
+ </div>
62
+ <div id="video-modal" style="display:none;">
63
+ <div id="player"></div>
64
+ <button id="close-modal">Close</button>
65
+ </div>
66
+ <script src="[invalid url, do not cite]
67
+ <link rel="stylesheet" href="[invalid url, do not cite] />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  <script>
69
+ // Custom JavaScript for the application
70
+ let videos = [];
71
+ let player;
72
+
73
+ // Fetch video data from database.json
74
+ fetch('database.json')
75
+ .then(response => response.json())
76
+ .then(data => {
77
+ videos = data.videos;
78
+ renderVideos(videos);
79
+ })
80
+ .catch(error => console.error('Error loading database.json', error));
81
+
82
+ // Render videos in the grid
83
+ function renderVideos(videosToRender) {
84
+ const grid = document.getElementById('video-grid');
85
+ grid.innerHTML = '';
86
+ videosToRender.forEach(video => {
87
+ const card = document.createElement('div');
88
+ card.className = 'video-card';
89
+ card.innerHTML = `<h3>${video.title}</h3><p>Click to play</p>`;
90
+ card.onclick = () => playVideo(video.url, video.title);
91
+ grid.appendChild(card);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
 
95
+ // Handle search functionality
96
+ const searchInput = document.getElementById('search-input');
97
+ searchInput.addEventListener('input', () => {
98
+ const query = searchInput.value.toLowerCase();
99
+ const filteredVideos = videos.filter(video => video.title.toLowerCase().includes(query));
100
+ renderVideos(filteredVideos);
101
+ });
 
 
 
 
 
 
 
102
 
103
+ // Play video in modal
104
+ function playVideo(url, title) {
105
+ if (player) {
106
+ player.destroy();
 
 
 
 
 
 
107
  }
108
+ const modal = document.getElementById('video-modal');
109
+ modal.style.display = 'flex';
110
+ const playerElement = document.getElementById('player');
111
+ playerElement.innerHTML = `<video id="plyr-player" playsinline controls><source src="${url}" type="video/mp4"></video>`;
112
+ player = new Plyr('#plyr-player');
113
+ }
114
+
115
+ // Close modal functionality
116
+ document.getElementById('close-modal').addEventListener('click', closeModal);
117
+ document.getElementById('video-modal').addEventListener('click', (e) => {
118
+ if (e.target === document.getElementById('video-modal')) {
119
+ closeModal();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  }
121
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
+ function closeModal() {
124
+ const modal = document.getElementById('video-modal');
125
+ modal.style.display = 'none';
126
+ if (player) {
127
+ player.destroy();
128
+ player = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  }
130
  }
131
+ </script>
132
+ </body>
 
 
 
 
 
 
 
 
 
 
133
  </html>