Add video viewer modal for gallery videos
All checks were successful
Build and Push Frontend Docker Image / build (push) Successful in 30s

- Click on video thumbnail to open in large viewer
- Video plays on hover, pauses when mouse leaves
- Close viewer by clicking X or clicking outside video

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-01-08 01:08:13 +00:00
parent ad4114ab82
commit 52dd0d8766
3 changed files with 88 additions and 1 deletions

View File

@@ -695,6 +695,52 @@ body {
}
}
/* Video Viewer */
.video-viewer {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
display: flex;
align-items: center;
justify-content: center;
z-index: 2000;
}
.video-viewer-close {
position: absolute;
top: 20px;
right: 20px;
width: 40px;
height: 40px;
border: none;
border-radius: 50%;
background: rgba(255, 255, 255, 0.2);
color: white;
font-size: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.video-viewer-close:hover {
background: rgba(255, 255, 255, 0.3);
}
.video-viewer-player {
max-width: 90vw;
max-height: 90vh;
border-radius: var(--radius);
}
.gallery-item-media video {
cursor: pointer;
}
/* Privacy Mode */
.privacy-toggle.active {
background: var(--primary);

View File

@@ -182,6 +182,12 @@
<div id="modal-overlay" class="modal-overlay hidden">
<div id="modal-content" class="modal-content"></div>
</div>
<!-- Video Viewer -->
<div id="video-viewer" class="video-viewer hidden">
<button id="video-viewer-close" class="video-viewer-close">&times;</button>
<video id="video-viewer-player" class="video-viewer-player" controls loop></video>
</div>
</div>
<script src="/js/app.js"></script>

View File

@@ -427,7 +427,7 @@ function renderGallery(container, items) {
<div class="gallery-item">
<div class="gallery-item-media">
${item.status === 'completed'
? `<video src="/api/content/${item.id}/stream" muted loop onmouseenter="this.play()" onmouseleave="this.pause()"></video>`
? `<video src="/api/content/${item.id}/stream" muted loop data-content-id="${item.id}"></video>`
: '<div style="display:flex;align-items:center;justify-content:center;height:100%;color:var(--gray-500)">' + item.status + '</div>'
}
<span class="gallery-item-status ${item.status}">${item.status}</span>
@@ -445,6 +445,19 @@ function renderGallery(container, items) {
</div>
`).join('');
// Video hover play/pause and click to view
container.querySelectorAll('.gallery-item-media video').forEach(video => {
video.addEventListener('mouseenter', () => video.play());
video.addEventListener('mouseleave', () => {
video.pause();
video.currentTime = 0;
});
video.addEventListener('click', () => {
const contentId = video.dataset.contentId;
openVideoViewer(`/api/content/${contentId}/stream`);
});
});
container.querySelectorAll('.delete-content-btn').forEach(btn => {
btn.addEventListener('click', async function() {
const contentId = parseInt(this.dataset.contentId, 10);
@@ -673,6 +686,28 @@ document.getElementById('modal-overlay').addEventListener('click', (e) => {
if (e.target === e.currentTarget) hideModal();
});
// Video Viewer
const videoViewer = document.getElementById('video-viewer');
const videoViewerPlayer = document.getElementById('video-viewer-player');
const videoViewerClose = document.getElementById('video-viewer-close');
function openVideoViewer(src) {
videoViewerPlayer.src = src;
videoViewer.classList.remove('hidden');
videoViewerPlayer.play();
}
function closeVideoViewer() {
videoViewerPlayer.pause();
videoViewerPlayer.src = '';
videoViewer.classList.add('hidden');
}
videoViewerClose.addEventListener('click', closeVideoViewer);
videoViewer.addEventListener('click', (e) => {
if (e.target === videoViewer) closeVideoViewer();
});
// Pagination
function renderPagination(container, pagination, loadFn) {
const { page, totalPages } = pagination;