Subham9126 commited on
Commit
6042536
·
verified ·
1 Parent(s): bc9545e

Delete script.js

Browse files
Files changed (1) hide show
  1. script.js +0 -543
script.js DELETED
@@ -1,543 +0,0 @@
1
- document.addEventListener('DOMContentLoaded', () => {
2
- // DOM Elements
3
- const videoGrid = document.getElementById('videoGrid');
4
- const searchInput = document.getElementById('searchInput');
5
- const clearSearchBtn = document.getElementById('clearSearch');
6
- const videoModal = document.getElementById('videoModal');
7
- const videoPlayer = document.getElementById('videoPlayer');
8
- const modalVideoTitle = document.getElementById('modalVideoTitle');
9
- const closeModal = document.getElementById('closeModal');
10
- const loadingIndicator = document.getElementById('loadingIndicator');
11
- const errorMessage = document.getElementById('errorMessage');
12
- const retryBtn = document.getElementById('retryBtn');
13
- const gridViewBtn = document.getElementById('gridViewBtn');
14
- const listViewBtn = document.getElementById('listViewBtn');
15
- const pagination = document.getElementById('pagination');
16
- const visibleCount = document.getElementById('visibleCount');
17
- const totalCount = document.getElementById('totalCount');
18
-
19
- // State
20
- let videos = [];
21
- let filteredVideos = [];
22
- let currentPage = 1;
23
- let videosPerPage = 12;
24
- let currentPlayer = null;
25
- let viewMode = 'grid';
26
- let lastTapTime = 0;
27
- let lastDoubleTapX = 0;
28
- let doubleTapThreshold = 10; // px tolerance for tap position
29
-
30
- // Initialize the app
31
- init();
32
-
33
- function init() {
34
- fetchVideoData();
35
- setupEventListeners();
36
- }
37
-
38
- // Fetch video data from JSON
39
- async function fetchVideoData() {
40
- try {
41
- loadingIndicator.style.display = 'flex';
42
- errorMessage.style.display = 'none';
43
- videoGrid.innerHTML = '';
44
-
45
- const response = await fetch('database.json');
46
- if (!response.ok) {
47
- throw new Error(`Failed to load database.json: ${response.status}`);
48
- }
49
-
50
- const data = await response.json();
51
-
52
- // Check if data has the expected structure
53
- if (!data.videos || !Array.isArray(data.videos)) {
54
- throw new Error('Invalid database format: missing "videos" array');
55
- }
56
-
57
- // Filter out invalid entries
58
- videos = data.videos.filter(video => {
59
- if (!video.title || !video.url) {
60
- console.warn('Invalid video entry:', video);
61
- return false;
62
- }
63
- return true;
64
- });
65
-
66
- filteredVideos = [...videos];
67
- updateVideoCount();
68
- renderPagination();
69
- renderVideos();
70
-
71
- showToast('Videos loaded successfully!', 'success');
72
- } catch (error) {
73
- console.error('Error loading videos:', error);
74
- loadingIndicator.style.display = 'none';
75
- errorMessage.style.display = 'flex';
76
- }
77
- }
78
-
79
- // Set up event listeners
80
- function setupEventListeners() {
81
- // Search functionality
82
- searchInput.addEventListener('input', handleSearch);
83
- clearSearchBtn.addEventListener('click', clearSearch);
84
-
85
- // Modal controls
86
- closeModal.addEventListener('click', closeVideoModal);
87
-
88
- // Retry button
89
- retryBtn.addEventListener('click', fetchVideoData);
90
-
91
- // View toggle
92
- gridViewBtn.addEventListener('click', () => setViewMode('grid'));
93
- listViewBtn.addEventListener('click', () => setViewMode('list'));
94
-
95
- // Handle clicks outside the modal to close it
96
- window.addEventListener('click', (e) => {
97
- if (e.target === videoModal) {
98
- closeVideoModal();
99
- }
100
- });
101
-
102
- // Keyboard controls for modal
103
- window.addEventListener('keydown', (e) => {
104
- if (videoModal.style.display === 'block') {
105
- if (e.key === 'Escape') {
106
- closeVideoModal();
107
- }
108
- }
109
- });
110
- }
111
-
112
- // Handle search input
113
- function handleSearch() {
114
- const searchTerm = searchInput.value.toLowerCase().trim();
115
- clearSearchBtn.style.display = searchTerm ? 'block' : 'none';
116
-
117
- if (searchTerm === '') {
118
- filteredVideos = [...videos];
119
- } else {
120
- filteredVideos = videos.filter(video =>
121
- video.title.toLowerCase().includes(searchTerm)
122
- );
123
- }
124
-
125
- currentPage = 1;
126
- updateVideoCount();
127
- renderPagination();
128
- renderVideos();
129
- }
130
-
131
- // Clear search
132
- function clearSearch() {
133
- searchInput.value = '';
134
- clearSearchBtn.style.display = 'none';
135
- filteredVideos = [...videos];
136
- currentPage = 1;
137
- updateVideoCount();
138
- renderPagination();
139
- renderVideos();
140
- }
141
-
142
- // Update video count display
143
- function updateVideoCount() {
144
- const start = (currentPage - 1) * videosPerPage;
145
- const end = Math.min(start + videosPerPage, filteredVideos.length);
146
- const count = Math.min(videosPerPage, filteredVideos.length - start);
147
-
148
- visibleCount.textContent = count > 0 ? end - start : 0;
149
- totalCount.textContent = filteredVideos.length;
150
- }
151
-
152
- // Set view mode (grid or list)
153
- function setViewMode(mode) {
154
- viewMode = mode;
155
-
156
- // Update active button
157
- gridViewBtn.classList.toggle('active', mode === 'grid');
158
- listViewBtn.classList.toggle('active', mode === 'list');
159
-
160
- // Update grid class
161
- videoGrid.classList.toggle('list-view', mode === 'list');
162
-
163
- // Re-render to apply changes
164
- renderVideos();
165
- }
166
-
167
- // Render videos in grid
168
- function renderVideos() {
169
- loadingIndicator.style.display = 'none';
170
- videoGrid.innerHTML = '';
171
-
172
- if (filteredVideos.length === 0) {
173
- videoGrid.innerHTML = `
174
- <div class="error-container" style="grid-column: 1 / -1;">
175
- <i class="fas fa-search"></i>
176
- <p>No videos found matching your search.</p>
177
- </div>
178
- `;
179
- return;
180
- }
181
-
182
- // Calculate pagination
183
- const start = (currentPage - 1) * videosPerPage;
184
- const end = Math.min(start + videosPerPage, filteredVideos.length);
185
- const currentPageVideos = filteredVideos.slice(start, end);
186
-
187
- // Create video cards
188
- currentPageVideos.forEach((video, index) => {
189
- const videoCard = document.createElement('div');
190
- videoCard.className = `video-card ${viewMode === 'list' ? 'list-view' : ''}`;
191
-
192
- // Determine if URL is from Hugging Face
193
- const isHuggingFaceUrl = video.url.includes('huggingface.co');
194
-
195
- // For HF URLs, append download=true if not already present
196
- let videoUrl = video.url;
197
- if (isHuggingFaceUrl && !videoUrl.includes('download=true')) {
198
- videoUrl = videoUrl.includes('?') ?
199
- `${videoUrl}&download=true` :
200
- `${videoUrl}?download=true`;
201
- }
202
-
203
- videoCard.innerHTML = `
204
- <div class="video-thumbnail">
205
- <div class="thumbnail-placeholder">
206
- <i class="fas fa-film fa-2x"></i>
207
- </div>
208
- <div class="play-icon">
209
- <i class="fas fa-play fa-lg"></i>
210
- </div>
211
- </div>
212
- <div class="video-info">
213
- <h3 class="video-title">${video.title}</h3>
214
- </div>
215
- `;
216
-
217
- videoCard.addEventListener('click', () => {
218
- playVideo(video);
219
- });
220
-
221
- videoGrid.appendChild(videoCard);
222
- });
223
- }
224
-
225
- // Render pagination controls
226
- function renderPagination() {
227
- pagination.innerHTML = '';
228
-
229
- if (filteredVideos.length <= videosPerPage) {
230
- return; // No pagination needed
231
- }
232
-
233
- const totalPages = Math.ceil(filteredVideos.length / videosPerPage);
234
-
235
- // Previous button
236
- const prevBtn = document.createElement('button');
237
- prevBtn.className = `page-btn ${currentPage === 1 ? 'disabled' : ''}`;
238
- prevBtn.innerHTML = '<i class="fas fa-chevron-left"></i>';
239
- prevBtn.disabled = currentPage === 1;
240
- prevBtn.addEventListener('click', () => {
241
- if (currentPage > 1) {
242
- goToPage(currentPage - 1);
243
- }
244
- });
245
- pagination.appendChild(prevBtn);
246
-
247
- // Page numbers with ellipsis
248
- const renderPageButton = (pageNum) => {
249
- const pageBtn = document.createElement('button');
250
- pageBtn.className = `page-btn ${currentPage === pageNum ? 'active' : ''}`;
251
- pageBtn.textContent = pageNum;
252
- pageBtn.addEventListener('click', () => goToPage(pageNum));
253
- pagination.appendChild(pageBtn);
254
- };
255
-
256
- // Determine visible page range
257
- const delta = 2; // Number of pages to show on each side of current
258
- const range = [];
259
- const rangeWithDots = [];
260
- let l;
261
-
262
- range.push(1);
263
-
264
- for (let i = currentPage - delta; i <= currentPage + delta; i++) {
265
- if (i > 1 && i < totalPages) {
266
- range.push(i);
267
- }
268
- }
269
-
270
- range.push(totalPages);
271
-
272
- for (let i of range) {
273
- if (l) {
274
- if (i - l === 2) {
275
- rangeWithDots.push(l + 1);
276
- } else if (i - l !== 1) {
277
- rangeWithDots.push('...');
278
- }
279
- }
280
- rangeWithDots.push(i);
281
- l = i;
282
- }
283
-
284
- rangeWithDots.forEach(item => {
285
- if (item === '...') {
286
- const ellipsis = document.createElement('span');
287
- ellipsis.className = 'page-btn disabled';
288
- ellipsis.textContent = '...';
289
- pagination.appendChild(ellipsis);
290
- } else {
291
- renderPageButton(item);
292
- }
293
- });
294
-
295
- // Next button
296
- const nextBtn = document.createElement('button');
297
- nextBtn.className = `page-btn ${currentPage === totalPages ? 'disabled' : ''}`;
298
- nextBtn.innerHTML = '<i class="fas fa-chevron-right"></i>';
299
- nextBtn.disabled = currentPage === totalPages;
300
- nextBtn.addEventListener('click', () => {
301
- if (currentPage < totalPages) {
302
- goToPage(currentPage + 1);
303
- }
304
- });
305
- pagination.appendChild(nextBtn);
306
- }
307
-
308
- // Navigate to a specific page
309
- function goToPage(pageNum) {
310
- currentPage = pageNum;
311
- updateVideoCount();
312
- renderPagination();
313
- renderVideos();
314
-
315
- // Scroll to top of video grid
316
- videoGrid.scrollIntoView({ behavior: 'smooth' });
317
- }
318
-
319
- // Play video in modal
320
- function playVideo(video) {
321
- // Clean up any existing player
322
- cleanupPlayer();
323
-
324
- // Update modal title
325
- modalVideoTitle.textContent = video.title;
326
-
327
- // Determine if URL is from Hugging Face
328
- const isHuggingFaceUrl = video.url.includes('huggingface.co');
329
-
330
- // For HF URLs, append download=true if not already present
331
- let videoUrl = video.url;
332
- if (isHuggingFaceUrl && !videoUrl.includes('download=true')) {
333
- videoUrl = videoUrl.includes('?') ?
334
- `${videoUrl}&download=true` :
335
- `${videoUrl}?download=true`;
336
- }
337
-
338
- // Set up video player
339
- videoPlayer.src = videoUrl;
340
-
341
- // Initialize Plyr
342
- currentPlayer = new Plyr(videoPlayer, {
343
- controls: [
344
- 'play-large', 'play', 'progress', 'current-time', 'mute',
345
- 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen'
346
- ],
347
- keyboard: { focused: true, global: false },
348
- tooltips: { controls: true, seek: true },
349
- captions: { active: true, language: 'auto', update: true }
350
- });
351
-
352
- // Set up double-tap controls for mobile
353
- setupDoubleTapControls();
354
-
355
- // Show modal
356
- videoModal.style.display = 'block';
357
- document.body.style.overflow = 'hidden'; // Prevent scrolling while modal is open
358
-
359
- // Play the video (with error handling)
360
- try {
361
- currentPlayer.play().catch(error => {
362
- console.warn('Failed to autoplay video:', error);
363
-
364
- // Check if it's a format issue
365
- const fileExtension = videoUrl.split('.').pop().toLowerCase();
366
- if (fileExtension === 'mkv') {
367
- showToast('MKV format may not be fully supported in your browser.', 'warning');
368
- }
369
- });
370
- } catch (error) {
371
- console.error('Error playing video:', error);
372
- showToast('Error playing video. Please try again.', 'error');
373
- }
374
- }
375
-
376
- // Set up double-tap controls for mobile
377
- function setupDoubleTapControls() {
378
- const playerElement = videoPlayer.closest('.player-container');
379
-
380
- const handleTap = (e) => {
381
- const now = Date.now();
382
- const tapX = e.touches ? e.touches[0].clientX : e.clientX;
383
- const playerWidth = playerElement.offsetWidth;
384
- const tapPosition = tapX / playerWidth; // 0 to 1 position
385
-
386
- if (now - lastTapTime < 300 && Math.abs(tapX - lastDoubleTapX) < doubleTapThreshold) {
387
- // Double tap detected
388
- e.preventDefault();
389
-
390
- if (tapPosition < 0.3) {
391
- // Double tap on left side - rewind
392
- currentPlayer.rewind(10);
393
- showControlFeedback('left', 'Rewind 10s');
394
- } else if (tapPosition > 0.7) {
395
- // Double tap on right side - forward
396
- currentPlayer.forward(10);
397
- showControlFeedback('right', 'Forward 10s');
398
- } else {
399
- // Double tap in center - play/pause
400
- if (currentPlayer.playing) {
401
- currentPlayer.pause();
402
- } else {
403
- currentPlayer.play();
404
- }
405
- }
406
- }
407
-
408
- lastTapTime = now;
409
- lastDoubleTapX = tapX;
410
- };
411
-
412
- // Add touch event listeners
413
- playerElement.addEventListener('touchstart', handleTap);
414
-
415
- // Store for cleanup
416
- playerElement.handleTapFunc = handleTap;
417
- }
418
-
419
- // Visual feedback for touch controls
420
- function showControlFeedback(position, text) {
421
- const playerElement = videoPlayer.closest('.player-container');
422
-
423
- // Create a feedback element
424
- const feedback = document.createElement('div');
425
- feedback.className = `tap-feedback ${position}`;
426
- feedback.innerHTML = `
427
- <div class="feedback-icon">
428
- <i class="fas fa-${position === 'left' ? 'backward' : position === 'right' ? 'forward' : 'play'}"></i>
429
- </div>
430
- <div class="feedback-text">${text}</div>
431
- `;
432
-
433
- // Style it
434
- feedback.style.position = 'absolute';
435
- feedback.style.top = '50%';
436
- feedback.style.transform = 'translateY(-50%)';
437
- feedback.style.color = 'white';
438
- feedback.style.backgroundColor = 'rgba(0,0,0,0.6)';
439
- feedback.style.padding = '15px';
440
- feedback.style.borderRadius = '50%';
441
- feedback.style.display = 'flex';
442
- feedback.style.flexDirection = 'column';
443
- feedback.style.alignItems = 'center';
444
- feedback.style.opacity = '0';
445
- feedback.style.animation = 'fadeInOut 1s ease-in-out';
446
-
447
- if (position === 'left') {
448
- feedback.style.left = '15%';
449
- } else if (position === 'right') {
450
- feedback.style.right = '15%';
451
- } else {
452
- feedback.style.left = '50%';
453
- feedback.style.transform = 'translate(-50%, -50%)';
454
- }
455
-
456
- // Add animation
457
- const keyframes = `
458
- @keyframes fadeInOut {
459
- 0% { opacity: 0; }
460
- 20% { opacity: 1; }
461
- 80% { opacity: 1; }
462
- 100% { opacity: 0; }
463
- }
464
- `;
465
-
466
- const style = document.createElement('style');
467
- style.textContent = keyframes;
468
- document.head.appendChild(style);
469
-
470
- // Add to player
471
- playerElement.appendChild(feedback);
472
-
473
- // Remove after animation
474
- setTimeout(() => {
475
- feedback.remove();
476
- style.remove();
477
- }, 1000);
478
- }
479
-
480
- // Close video modal and clean up player
481
- function closeVideoModal() {
482
- // Clean up player first
483
- cleanupPlayer();
484
-
485
- // Hide modal
486
- videoModal.style.display = 'none';
487
- document.body.style.overflow = ''; // Restore scrolling
488
- }
489
-
490
- // Clean up video player
491
- function cleanupPlayer() {
492
- // If a player instance exists, destroy it
493
- if (currentPlayer) {
494
- // Make sure video is paused
495
- try {
496
- currentPlayer.pause();
497
- } catch (e) {
498
- console.warn('Error pausing player:', e);
499
- }
500
-
501
- // Try-catch to handle potential Plyr errors
502
- try {
503
- currentPlayer.destroy();
504
- } catch (e) {
505
- console.warn('Error destroying Plyr instance:', e);
506
- }
507
-
508
- currentPlayer = null;
509
- }
510
-
511
- // Directly manipulate the video element as a failsafe
512
- videoPlayer.pause();
513
- videoPlayer.removeAttribute('src');
514
- videoPlayer.load(); // Important: this resets the video element
515
-
516
- // Remove any event listeners from the player container
517
- const playerElement = videoPlayer.closest('.player-container');
518
- if (playerElement && playerElement.handleTapFunc) {
519
- playerElement.removeEventListener('touchstart', playerElement.handleTapFunc);
520
- delete playerElement.handleTapFunc;
521
- }
522
-
523
- // Clear any existing feedback elements
524
- const feedbacks = document.querySelectorAll('.tap-feedback');
525
- feedbacks.forEach(el => el.remove());
526
- }
527
-
528
- // Show toast message
529
- function showToast(message, type = 'info') {
530
- const toastContainer = document.getElementById('toastContainer');
531
-
532
- const toast = document.createElement('div');
533
- toast.className = `toast ${type}`;
534
- toast.textContent = message;
535
-
536
- toastContainer.appendChild(toast);
537
-
538
- // Remove toast after animation completes
539
- setTimeout(() => {
540
- toast.remove();
541
- }, 3000);
542
- }
543
- });