Загрузка данных


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Видеоплатформа</title>
    <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2/dist/umd/supabase.min.js"></script>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #0f0f0f;
            color: #fff;
            min-height: 100vh;
        }

        .header {
            position: fixed;
            top: 0; left: 0; right: 0;
            background: #0f0f0f;
            border-bottom: 1px solid #303030;
            padding: 12px 20px;
            z-index: 100;
            display: flex;
            align-items: center;
            gap: 20px;
        }

        .logo {
            font-size: 20px;
            font-weight: bold;
            color: #ff0000;
            text-decoration: none;
        }

        .search-box {
            flex: 1;
            max-width: 600px;
        }

        .search-box input {
            width: 100%;
            padding: 8px 16px;
            border: 1px solid #303030;
            border-radius: 20px;
            background: #121212;
            color: #fff;
            font-size: 14px;
            outline: none;
        }

        .search-box input:focus {
            border-color: #3ea6ff;
        }

        .container {
            padding: 80px 20px 40px;
            max-width: 1600px;
            margin: 0 auto;
        }

        .video-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
            gap: 16px;
        }

        .video-card {
            cursor: pointer;
            transition: transform 0.2s;
            border-radius: 12px;
            overflow: hidden;
        }

        .video-card:hover {
            transform: scale(1.02);
        }

        .thumbnail-wrapper {
            position: relative;
            aspect-ratio: 16/9;
            background: #1a1a1a;
            border-radius: 12px;
            overflow: hidden;
        }

        .thumbnail-wrapper img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }

        .duration {
            position: absolute;
            bottom: 8px;
            right: 8px;
            background: rgba(0,0,0,0.8);
            padding: 2px 6px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: 500;
        }

        .video-info {
            padding: 12px 4px;
        }

        .video-title {
            font-size: 14px;
            font-weight: 500;
            line-height: 1.4;
            margin-bottom: 6px;
            display: -webkit-box;
            -webkit-line-clamp: 2;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }

        .video-meta {
            font-size: 12px;
            color: #aaa;
            display: flex;
            gap: 8px;
            flex-wrap: wrap;
        }

        .video-meta span {
            display: flex;
            align-items: center;
            gap: 4px;
        }

        .loading {
            text-align: center;
            padding: 60px;
            color: #aaa;
            font-size: 16px;
        }

        .error {
            text-align: center;
            padding: 60px;
            color: #ff6b6b;
            font-size: 16px;
        }

        .no-videos {
            text-align: center;
            padding: 60px;
            color: #aaa;
            font-size: 16px;
        }

        /* Модальное окно просмотра */
        .modal-overlay {
            display: none;
            position: fixed;
            top: 0; left: 0; right: 0; bottom: 0;
            background: rgba(0,0,0,0.95);
            z-index: 1000;
            overflow-y: auto;
        }

        .modal-overlay.active {
            display: block;
        }

        .modal-content {
            max-width: 1200px;
            margin: 20px auto;
            padding: 0 20px;
        }

        .video-player-wrapper {
            position: relative;
            aspect-ratio: 16/9;
            background: #000;
            border-radius: 12px;
            overflow: hidden;
        }

        .video-player-wrapper video {
            width: 100%;
            height: 100%;
        }

        .close-btn {
            position: fixed;
            top: 20px; right: 20px;
            background: rgba(255,255,255,0.1);
            border: none;
            color: #fff;
            width: 40px; height: 40px;
            border-radius: 50%;
            cursor: pointer;
            font-size: 20px;
            z-index: 1001;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .close-btn:hover {
            background: rgba(255,255,255,0.2);
        }

        .video-details {
            padding: 20px 0;
        }

        .video-details h1 {
            font-size: 20px;
            margin-bottom: 12px;
        }

        .video-details .meta {
            color: #aaa;
            font-size: 14px;
            margin-bottom: 16px;
        }

        .video-details .description {
            background: #1a1a1a;
            padding: 16px;
            border-radius: 12px;
            font-size: 14px;
            line-height: 1.6;
            color: #e0e0e0;
        }

        /* Мобильная адаптация */
        @media (max-width: 768px) {
            .video-grid {
                grid-template-columns: 1fr;
            }

            .header {
                padding: 10px 12px;
            }

            .search-box {
                display: none;
            }

            .container {
                padding: 70px 12px 20px;
            }

            .video-title {
                font-size: 15px;
            }

            .modal-content {
                margin: 0;
                padding: 0;
            }

            .video-player-wrapper {
                border-radius: 0;
            }

            .close-btn {
                top: 10px; right: 10px;
            }
        }

        @media (min-width: 769px) and (max-width: 1200px) {
            .video-grid {
                grid-template-columns: repeat(2, 1fr);
            }
        }
    </style>
</head>
<body>
    <header class="header">
        <a href="#" class="logo">▶ VideoHub</a>
        <div class="search-box">
            <input type="text" id="searchInput" placeholder="Поиск видео..." oninput="searchVideos()">
        </div>
    </header>

    <div class="container">
        <div id="videoGrid" class="video-grid">
            <div class="loading">Загрузка видео...</div>
        </div>
    </div>

    <!-- Модальное окно просмотра -->
    <div id="videoModal" class="modal-overlay" onclick="closeModal(event)">
        <button class="close-btn" onclick="closeModal()">×</button>
        <div class="modal-content" onclick="event.stopPropagation()">
            <div class="video-player-wrapper">
                <video id="modalVideo" controls autoplay></video>
            </div>
            <div class="video-details">
                <h1 id="modalTitle"></h1>
                <div class="meta" id="modalMeta"></div>
                <div class="description" id="modalDescription"></div>
            </div>
        </div>
    </div>

    <script>
        const SUPABASE_URL = 'https://nwvkbmeklotwktyrcwyb.supabase.co';
        const SUPABASE_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Im53dmtibWVrbG90d2t0eXJjd3liIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODE5ODc4MTksImV4cCI6MjA5NzU2MzgxOX0.JzpUeln4QXJkJVO6guq1EIrSLs6lzNlTxWMXzhnxhHs';

        const supabase = supabase.createClient(SUPABASE_URL, SUPABASE_KEY);
        let allVideos = [];

        async function loadVideos() {
            const grid = document.getElementById('videoGrid');
            try {
                const { data, error } = await supabase
                    .from('videos')
                    .select('*')
                    .order('created_at', { ascending: false });

                if (error) throw error;

                allVideos = data || [];
                renderVideos(allVideos);
            } catch (err) {
                grid.innerHTML = `<div class="error">Ошибка загрузки: ${escapeHtml(err.message)}</div>`;
            }
        }

        function renderVideos(videos) {
            const grid = document.getElementById('videoGrid');

            if (videos.length === 0) {
                grid.innerHTML = '<div class="no-videos">Пока нет видео</div>';
                return;
            }

            grid.innerHTML = videos.map(video => {
                const date = new Date(video.created_at);
                const dateStr = formatDate(date);
                const thumbUrl = video.thumbnail_url || 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="320" height="180" fill="%231a1a1a"><text x="50%" y="50%" text-anchor="middle" fill="%23666" font-size="14">Нет превью</text></svg>';

                return `
                    <div class="video-card" onclick="openVideo('${video.id}')">
                        <div class="thumbnail-wrapper">
                            <img src="${escapeHtml(thumbUrl)}" alt="${escapeHtml(video.title || 'Без названия')}" loading="lazy">
                            <span class="duration">${escapeHtml(video.duration || '0:00')}</span>
                        </div>
                        <div class="video-info">
                            <div class="video-title">${escapeHtml(video.title || 'Без названия')}</div>
                            <div class="video-meta">
                                <span>${escapeHtml(video.author || 'Admin')}</span>
                                <span>•</span>
                                <span>${dateStr}</span>
                            </div>
                        </div>
                    </div>
                `;
            }).join('');
        }

        function openVideo(id) {
            const video = allVideos.find(v => v.id === id);
            if (!video) return;

            const modal = document.getElementById('videoModal');
            const videoEl = document.getElementById('modalVideo');
            const date = new Date(video.created_at);

            document.getElementById('modalTitle').textContent = video.title || 'Без названия';
            document.getElementById('modalMeta').innerHTML = `
                ${escapeHtml(video.author || 'Admin')} • 
                ${formatDate(date)} • 
                ${escapeHtml(video.duration || '0:00')}
            `;
            document.getElementById('modalDescription').textContent = video.description || 'Нет описания';

            videoEl.src = video.video_url;
            videoEl.poster = video.thumbnail_url || '';

            modal.classList.add('active');
            document.body.style.overflow = 'hidden';
        }

        function closeModal(event) {
            if (event && event.target !== event.currentTarget && event.target.className !== 'close-btn') return;

            const modal = document.getElementById('videoModal');
            const videoEl = document.getElementById('modalVideo');

            videoEl.pause();
            videoEl.src = '';
            modal.classList.remove('active');
            document.body.style.overflow = '';
        }

        function searchVideos() {
            const query = document.getElementById('searchInput').value.toLowerCase().trim();
            if (!query) {
                renderVideos(allVideos);
                return;
            }

            const filtered = allVideos.filter(v => 
                (v.title || '').toLowerCase().includes(query) ||
                (v.description || '').toLowerCase().includes(query) ||
                (v.author || '').toLowerCase().includes(query)
            );
            renderVideos(filtered);
        }

        function escapeHtml(text) {
            if (!text) return '';
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        function formatDate(date) {
            const now = new Date();
            const diff = now - date;
            const minutes = Math.floor(diff / 60000);
            const hours = Math.floor(diff / 3600000);
            const days = Math.floor(diff / 86400000);

            if (minutes < 1) return 'только что';
            if (minutes < 60) return `${minutes} мин. назад`;
            if (hours < 24) return `${hours} ч. назад`;
            if (days < 7) return `${days} дн. назад`;

            return date.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short', year: 'numeric' });
        }

        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') closeModal();
        });

        loadVideos();
    </script>
</body>
</html>