Subham9126 commited on
Commit
9ba0fbc
·
verified ·
1 Parent(s): 4a4d1fc

Update script.js

Browse files
Files changed (1) hide show
  1. script.js +504 -120
script.js CHANGED
@@ -1,159 +1,543 @@
1
  document.addEventListener('DOMContentLoaded', () => {
2
  // DOM Elements
3
- const videoList = document.getElementById('videoList');
4
- const videoPlayer = document.getElementById('videoPlayer');
5
- const currentVideoTitle = document.getElementById('currentVideoTitle');
6
- const noVideoMessage = document.getElementById('noVideoMessage');
7
  const searchInput = document.getElementById('searchInput');
8
- const searchButton = document.getElementById('searchButton');
9
- const fullscreenBtn = document.getElementById('fullscreenBtn');
10
-
 
 
 
 
 
 
 
 
 
 
 
11
  // State
12
  let videos = [];
13
- let currentVideoIndex = -1;
14
-
15
- // Fetch video data from JSON file
16
- async function fetchVideos() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  try {
 
 
 
 
18
  const response = await fetch('database.json');
19
  if (!response.ok) {
20
- throw new Error('Failed to load video database');
21
  }
 
 
22
 
23
- videos = await response.json();
24
- renderVideoList(videos);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  } catch (error) {
26
  console.error('Error loading videos:', error);
27
- videoList.innerHTML = `
28
- <div class="error-message">
29
- <p>Failed to load videos. Please try again later.</p>
30
- </div>
31
- `;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
-
35
- // Render video list
36
- function renderVideoList(videosToRender) {
37
- // Clear loading state
38
- videoList.innerHTML = '';
39
 
40
- if (videosToRender.length === 0) {
41
- videoList.innerHTML = `
42
- <div class="error-message">
43
- <p>No videos found</p>
 
44
  </div>
45
  `;
46
  return;
47
  }
48
 
49
- // Create video list items
50
- videosToRender.forEach((video, index) => {
51
- const videoItem = document.createElement('div');
52
- videoItem.className = 'video-item';
53
- videoItem.innerHTML = `
54
- <h3>${video.title}</h3>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  `;
56
 
57
- videoItem.addEventListener('click', () => {
58
- playVideo(index);
59
  });
60
 
61
- videoList.appendChild(videoItem);
62
  });
63
  }
64
-
65
- // Play selected video
66
- function playVideo(index) {
67
- if (index < 0 || index >= videos.length) return;
68
-
69
- // Update UI
70
- const videoItems = document.querySelectorAll('.video-item');
71
- videoItems.forEach(item => item.classList.remove('active'));
72
- videoItems[index].classList.add('active');
73
-
74
- // Update video source and title
75
- const video = videos[index];
76
- videoPlayer.src = video.url;
77
- currentVideoTitle.textContent = video.title;
78
-
79
- // Show video player and hide placeholder
80
- noVideoMessage.style.display = 'none';
81
- videoPlayer.style.display = 'block';
82
-
83
- // Play video
84
- videoPlayer.load();
85
- videoPlayer.play()
86
- .catch(error => {
87
- console.error('Failed to play video:', error);
88
- // Handle formats that might not be supported
89
- if (video.url.toLowerCase().endsWith('.mkv')) {
90
- alert('MKV format may not be supported in your browser. Consider using MP4 files for better compatibility.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  }
92
- });
 
 
 
93
 
94
- currentVideoIndex = index;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  }
96
-
97
- // Search functionality
98
- function searchVideos() {
99
- const searchTerm = searchInput.value.toLowerCase().trim();
 
 
 
100
 
101
- if (!searchTerm) {
102
- renderVideoList(videos);
103
- return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  }
 
 
 
 
 
105
 
106
- const filteredVideos = videos.filter(video =>
107
- video.title.toLowerCase().includes(searchTerm)
108
- );
109
-
110
- renderVideoList(filteredVideos);
111
- }
112
-
113
- // Toggle fullscreen
114
- function toggleFullscreen() {
115
- if (!document.fullscreenElement) {
116
- if (videoPlayer.requestFullscreen) {
117
- videoPlayer.requestFullscreen();
118
- } else if (videoPlayer.webkitRequestFullscreen) { /* Safari */
119
- videoPlayer.webkitRequestFullscreen();
120
- } else if (videoPlayer.msRequestFullscreen) { /* IE11 */
121
- videoPlayer.msRequestFullscreen();
 
 
 
 
 
 
 
 
 
 
122
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  } else {
124
- if (document.exitFullscreen) {
125
- document.exitFullscreen();
126
- } else if (document.webkitExitFullscreen) { /* Safari */
127
- document.webkitExitFullscreen();
128
- } else if (document.msExitFullscreen) { /* IE11 */
129
- document.msExitFullscreen();
130
- }
131
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  }
133
-
134
- // Handle video ended event
135
- videoPlayer.addEventListener('ended', () => {
136
- // Optionally auto-play next video
137
- if (currentVideoIndex < videos.length - 1) {
138
- playVideo(currentVideoIndex + 1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  }
140
- });
141
-
142
- // Handle video error
143
- videoPlayer.addEventListener('error', () => {
144
- console.error('Video error:', videoPlayer.error);
145
- alert('Error playing video. This could be due to format incompatibility or access issues.');
146
- });
147
-
148
- // Event listeners
149
- searchButton.addEventListener('click', searchVideos);
150
- searchInput.addEventListener('keyup', (e) => {
151
- if (e.key === 'Enter') {
152
- searchVideos();
153
  }
154
- });
155
- fullscreenBtn.addEventListener('click', toggleFullscreen);
156
-
157
- // Initial load
158
- fetchVideos();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  });
 
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
  });