cd /var/www/clients/client0/web3/web

cat << 'EOF' > play.php
<?php
// BEZWZGLĘDNA BLOKADA CACHE - Gwarantuje prawidłowe wczytanie czasu po powrocie ze strzałki Wstecz
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

setlocale(LC_CTYPE, "pl_PL.UTF-8", "en_US.UTF-8", "C.UTF-8");
require_once 'functions.php';
checkAuth();

session_write_close();

$cfg = cfg();
$folderKey = $_GET['folder'] ?? '';
$subPath = $_GET['path'] ?? '';
$fileName = $_GET['file'] ?? '';

if (!isset($cfg['allowedFolders'][$folderKey])) die('Odmowa dostępu.');
$basePath = '/mnt/mega' . $cfg['allowedFolders'][$folderKey];
$filePath = rtrim($basePath . '/' . $subPath, '/') . '/' . $fileName;

if (!file_exists($filePath)) die('Plik nie istnieje.');

$ext = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));
$isNative = in_array($ext, ['mp4', 'webm']);

$streamUrl = $isNative 
    ? "view.php?folder=".urlencode($folderKey)."&path=".urlencode($subPath)."&file=".urlencode($fileName)
    : "stream.php?folder=".urlencode($folderKey)."&path=".urlencode($subPath)."&file=".urlencode($fileName);

// SYSTEM AUTO-NEXT
$dirPath = dirname($filePath);
$dirFiles = scandir($dirPath);
$videos = [];
$supportedExts = ['mkv', 'avi', 'ts', 'mts', 'mp4', 'webm'];

foreach($dirFiles as $f) {
    if($f === '.' || $f === '..') continue;
    if(is_file($dirPath . '/' . $f)) {
        if(in_array(strtolower(pathinfo($f, PATHINFO_EXTENSION)), $supportedExts)) {
            $videos[] = $f;
        }
    }
}

natsort($videos); 
$videos = array_values($videos); 
$currentIndex = array_search($fileName, $videos);
$nextFile = '';
$nextUrl = '';

if($currentIndex !== false && isset($videos[$currentIndex + 1])) {
    $nextFile = $videos[$currentIndex + 1];
    $nextUrl = "play.php?folder=".urlencode($folderKey)."&path=".urlencode($subPath)."&file=".urlencode($nextFile);
}

// LINK 24H
$hash = bin2hex(random_bytes(6));
$db = getDb();
$db['shares'][$hash] = [
    'folder' => $folderKey,
    'path' => $subPath,
    'file' => $fileName,
    'expires' => time() + (24 * 3600)
];
saveDb($db);
$scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? "https" : "http";
$fullUrl = $scheme . "://" . $_SERVER['HTTP_HOST'] . rtrim(dirname($_SERVER['PHP_SELF']), '/') . "/share.php?h=" . $hash;

$durCmd = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 " . escapeshellarg($filePath);
$duration = (float)shell_exec($durCmd);

// --- ODCZYT POSTĘPU Z SERWERA ---
if (session_status() === PHP_SESSION_NONE) session_start();
$pin = $_SESSION['user_pin'] ?? $_SERVER['REMOTE_ADDR'];
$progFile = __DIR__ . '/data/progress.json';
$progData = file_exists($progFile) ? json_decode(file_get_contents($progFile), true) : [];
$savedTime = isset($progData[$pin][$fileName]) ? (float)$progData[$pin][$fileName]['time'] : 0;
$initialStart = ($savedTime > 15) ? floor($savedTime) : 0;

// --- NAPRAWIONE GENEROWANIE MINIATUR (Odciążenie CPU, błyskawiczne keyframe'y) ---
$thumbDir = __DIR__ . '/data/thumbnails';
if (!is_dir($thumbDir)) @mkdir($thumbDir, 0777, true);
$thumbHash = md5($filePath);
$spriteFile = $thumbDir . '/' . $thumbHash . '.jpg';
$spriteUrl = "data/thumbnails/" . $thumbHash . ".jpg";

if (!file_exists($spriteFile)) {
    // Wdrożono z powrotem -skip_frame nokey oraz bezpieczne odciążenie procesora by uniknąć lagów 100% CPU
    $cmd = "nohup bash -c 'nice -n 19 ffmpeg -y -i " . escapeshellarg($filePath) . " -skip_frame nokey -threads 1 -vf \"fps=1/10,scale=160:90,tile=30x30\" -frames:v 1 -q:v 5 " . escapeshellarg($spriteFile . '.part') . " && mv " . escapeshellarg($spriteFile . '.part') . " " . escapeshellarg($spriteFile) . "' >/dev/null 2>&1 &";
    exec($cmd);
}
?>
<!doctype html>
<html lang='pl'>
<head>
    <meta charset='utf-8'>
    <title>VOD: <?=htmlspecialchars($fileName)?></title>
    <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover'>
    <style>
        body, html { height: 100dvh; width: 100vw; margin: 0; padding: 0; background: #080808; overflow: hidden; font-family: -apple-system, sans-serif; touch-action: none; user-select: none; -webkit-user-select: none; }
        .player-wrapper { position: absolute; top:0; left:0; width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: #000; }
        video { width: 100%; height: 100%; object-fit: contain; }
        
        #video-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 5; }

        .controls, .top-bar { position: absolute; left: 0; width: 100%; box-sizing: border-box; display: flex; transition: opacity 0.4s ease; z-index: 10; opacity: 1; pointer-events: none; }
        .player-wrapper.idle .controls, .player-wrapper.idle .top-bar { opacity: 0 !important; cursor: none; }
        body.idle { cursor: none; }
        .controls > *, .top-bar > * { pointer-events: auto; }

        .top-bar { top: 0; background: linear-gradient(rgba(0,0,0,0.9), transparent); padding: 15px; justify-content: space-between; align-items: flex-start; }
        .title-container { flex: 1; display: flex; flex-direction: column; overflow: hidden; margin-right: 15px; }
        .title { color: #fff; font-size: 15px; font-weight: bold; text-shadow: 1px 1px 3px #000; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        
        .controls { bottom: 0; background: linear-gradient(transparent, rgba(0,0,0,0.9)); padding: 40px 15px 15px 15px; gap: 10px; align-items: center; flex-wrap: wrap; }
        .btn-ctrl { background: none; border: none; color: #fff; cursor: pointer; font-size: 20px; padding: 5px; transition: color 0.2s; }
        .btn-ctrl:hover { color: #007bff; }
        .btn-link { background: rgba(40, 167, 69, 0.15); border: 1px solid rgba(40, 167, 69, 0.5); color: #28a745; font-size: 12px; font-weight: bold; border-radius: 4px; padding: 4px 8px; margin-right: 5px; transition: 0.2s; }

        .progress-container { flex-grow: 1; min-width: 30%; height: 35px; cursor: pointer; position: relative; display: flex; align-items: center; touch-action: none; }
        .progress-track { width: 100%; height: 6px; background: rgba(255,255,255,0.2); border-radius: 4px; position: relative; pointer-events: none; }
        .progress-bar { height: 100%; width: 0%; background: #6f42c1; border-radius: 4px; pointer-events: none; }
        
        #scrubBubble { position: absolute; bottom: 35px; transform: translateX(-50%); background: #080808; border: 2px solid #6f42c1; border-radius: 6px; padding: 5px; color: #fff; font-size: 13px; font-family: monospace; text-align: center; display: none; pointer-events: none; z-index: 50; box-shadow: 0 5px 15px rgba(0,0,0,0.8); }
        .sprite-view { width: 160px; height: 90px; background-repeat: no-repeat; margin-bottom: 5px; border-radius: 2px; display: block; background-color: #222; }

        .time-text { color: #fff; font-size: 12px; font-family: monospace; text-shadow: 1px 1px 2px #000; }
        .back-btn { background: rgba(255,255,255,0.1); border: none; font-family: inherit; color: #fff; text-decoration: none; font-size: 12px; padding: 5px 12px; border-radius: 20px; transition: 0.2s; flex-shrink: 0; cursor: pointer; z-index: 99999; }
        .back-btn:hover { background: rgba(255,255,255,0.3); }
        
        .loader { border: 4px solid rgba(255,255,255,0.1); border-top: 4px solid #6f42c1; border-radius: 50%; width: 50px; height: 50px; animation: spin 1s linear infinite; position: absolute; top: 50%; left: 50%; margin-top: -25px; margin-left: -25px; display: none; z-index: 5; }
        .seek-indicator { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(0,0,0,0.6); padding: 15px 25px; border-radius: 50px; font-size: 20px; font-weight: bold; color: #fff; pointer-events: none; opacity: 0; transition: opacity 0.2s; z-index: 20; text-shadow: 1px 1px 3px #000; }
        
        @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
        @media (max-width: 600px) {
            .btn-ctrl { font-size: 18px; padding: 2px; }
            .btn-link { font-size: 10px; padding: 4px 6px; }
            .time-text { font-size: 10px; }
            .title { font-size: 13px; }
            .controls { padding-top: 30px; }
            #volSlider { display: none; }
            .volume-container { margin-right: 0 !important; }
        }
    </style>
</head>
<body>
    <div class="player-wrapper" id="player-wrapper">
        <div class="loader" id="loader" style="display: block;"></div>

        <div id="seekIndL" class="seek-indicator" style="left:10%;">⏪ -10s</div>
        <div id="seekIndR" class="seek-indicator" style="right:10%;">+10s ⏩</div>

        <video id="vid" playsinline webkit-playsinline x5-playsinline="true" x5-video-player-type="h5-page" preload="metadata">
            <?php if($isNative): ?>
                <source src="<?=$streamUrl?>#t=<?=$initialStart?>" type="video/mp4">
            <?php else: ?>
                <source src="<?=$streamUrl?>&start=<?=$initialStart?>" type="video/mp4">
            <?php endif; ?>
        </video>
        
        <div id="video-overlay"></div>

        <div class="top-bar">
            <div class="title-container">
                <span class="title" title="<?=htmlspecialchars($fileName)?>"><?=htmlspecialchars($fileName)?></span>
                <span style="font-size:10px; color:#aaa; margin-top:3px;"><?=$isNative ? '(Natywny MP4)' : '(Transkodowanie Live)'?></span>
            </div>
            <a href="javascript:window.close()" class="back-btn">&#10006; Zamknij</a>
        </div>
        
        <div class="controls">
            <button class="btn-ctrl" id="playBtn">&#9658;</button>
            <div class="time-text" id="timeDisplay">00:00:00</div>
            
            <div class="progress-container" id="progress">
                <div class="progress-track">
                    <div class="progress-bar" id="progressBar"></div>
                </div>
                <div id="scrubBubble">
                    <div class="sprite-view" id="spriteView"></div>
                    <span id="scrubTime">00:00:00</span>
                </div>
            </div>
            
            <div class="time-text" id="durationDisplay"><?=gmdate("H:i:s", (int)$duration)?></div>
            
            <button class="btn-ctrl btn-link" onclick="copyLink24()" title="Skopiuj ukryty link 24h">
                &#128279; Link
            </button>

            <div class="volume-container" style="display:flex; align-items:center; gap:5px; margin-right:10px;">
                <button class="btn-ctrl" id="muteBtn" style="font-size:16px;">&#128266;</button>
                <input type="range" id="volSlider" min="0" max="1" step="0.05" value="1" style="width: 70px; cursor: pointer;">
            </div>

            <button class="btn-ctrl" id="fsBtn">&#9974;</button>
        </div>
    </div>

    <script>
        window.addEventListener("pageshow", function(event) {
            if (event.persisted) { window.location.reload(); }
        });

        function copyLink24() {
            const url = '<?=$fullUrl?>';
            if (navigator.clipboard && window.isSecureContext) navigator.clipboard.writeText(url).then(() => alert('Skopiowano!'));
            else { let dummy = document.createElement('input'); document.body.appendChild(dummy); dummy.value = url; dummy.select(); document.execCommand('copy'); document.body.removeChild(dummy); alert('Skopiowano!'); }
        }

        const vid = document.getElementById('vid');
        const playBtn = document.getElementById('playBtn');
        const progress = document.getElementById('progress');
        const progressBar = document.getElementById('progressBar');
        const scrubBubble = document.getElementById('scrubBubble');
        const spriteView = document.getElementById('spriteView');
        const scrubTime = document.getElementById('scrubTime');
        const timeDisplay = document.getElementById('timeDisplay');
        const fsBtn = document.getElementById('fsBtn');
        const loader = document.getElementById('loader');
        const playerWrapper = document.getElementById('player-wrapper');
        const videoOverlay = document.getElementById('video-overlay');
        const muteBtn = document.getElementById('muteBtn');
        const volSlider = document.getElementById('volSlider');
        const seekIndL = document.getElementById('seekIndL');
        const seekIndR = document.getElementById('seekIndR');

        let isNative = <?=$isNative ? 'true' : 'false'?>;
        let totalDuration = <?=$duration?>;
        let currentOffset = 0;
        let isSeeking = false;
        let isDragging = false;
        let dragTime = 0;
        
        let lastTapTime = 0;
        let clickTimer = null;
        let seekAccumulator = 0;

        let spriteUrlBase = '<?=$spriteUrl?>';
        function checkSprite() {
            let img = new Image();
            img.onload = () => {
                if (img.naturalWidth > 10) {
                    spriteView.style.backgroundImage = "url('" + img.src + "')";
                } else {
                    setTimeout(checkSprite, 2000); 
                }
            };
            img.onerror = () => {
                setTimeout(checkSprite, 2000); 
            };
            img.src = spriteUrlBase + '?t=' + Date.now();
        }
        checkSprite();

        function formatTime(seconds) {
            if (isNaN(seconds) || seconds < 0) return "00:00:00";
            let h = Math.floor(seconds / 3600); let m = Math.floor((seconds % 3600) / 60); let s = Math.floor(seconds % 60);
            return (h > 0 ? (h < 10 ? "0"+h : h) + ":" : "00:") + (m < 10 ? "0"+m : m) + ":" + (s < 10 ? "0"+s : s);
        }

        window.addEventListener('DOMContentLoaded', () => {
            let fd = new FormData();
            fd.append('action', 'get_progress');
            fd.append('file', '<?=htmlspecialchars($fileName, ENT_QUOTES)?>');

            fetch('admin_ajax.php', { method: 'POST', body: fd })
                .then(r => r.json())
                .then(data => {
                    let startT = (data && data.status === 'ok' && data.time > 15) ? data.time : 0;
                    currentOffset = isNative ? 0 : startT;
                    vid.src = isNative ? '<?=$streamUrl?>' : ('<?=$streamUrl?>&start=' + startT);
                    
                    if (isNative && startT > 0) { vid.currentTime = startT; }

                    let playPromise = vid.play();
                    if (playPromise !== undefined) {
                        playPromise.then(() => { loader.style.display = 'none'; }).catch(() => { loader.style.display = 'none'; });
                    }
                }).catch(() => {
                    vid.src = '<?=$streamUrl?>' + (isNative ? '' : '&start=0');
                    vid.play().catch(e => console.log(e));
                });
        });

        function saveProgress() {
            if (vid.paused && vid.currentTime === 0) return;
            let realTime = isNative ? vid.currentTime : currentOffset + vid.currentTime;
            if (isNaN(realTime)) return;
            let fd = new FormData();
            fd.append('action', 'save_progress');
            fd.append('file', '<?=htmlspecialchars($fileName, ENT_QUOTES)?>');
            fd.append('time', realTime);
            fetch('admin_ajax.php', { method: 'POST', body: fd }).catch(e=>console.log(e));
        }
        setInterval(saveProgress, 10000); 

        // --- ZABEZPIECZONA FUNKCJA DOTYKOWA ---
        function getX(e) { 
            if (e.clientX !== undefined) return e.clientX;
            if (e.touches && e.touches.length > 0) return e.touches[0].clientX; 
            if (e.changedTouches && e.changedTouches.length > 0) return e.changedTouches[0].clientX; 
            return 0; 
        }

        function startScrub(e) {
            if (totalDuration <= 0) return;
            isDragging = true;
            updateScrub(getX(e));
        }

        function moveScrub(e) {
            if (!isDragging) return;
            e.preventDefault(); 
            updateScrub(getX(e));
        }

        function endScrub(e) {
            if (!isDragging) return;
            isDragging = false;
            scrubBubble.style.display = 'none';
            triggerSeek(dragTime);
        }

        progress.addEventListener('mousedown', startScrub);
        document.addEventListener('mousemove', moveScrub);
        document.addEventListener('mouseup', endScrub);
        progress.addEventListener('touchstart', startScrub, {passive: false});
        document.addEventListener('touchmove', moveScrub, {passive: false});
        document.addEventListener('touchend', endScrub);

        function updateScrub(x) {
            if (isNaN(x) || x === undefined) return;
            let rect = progress.getBoundingClientRect();
            let perc = (x - rect.left) / rect.width;
            if (isNaN(perc)) perc = 0;
            perc = Math.max(0, Math.min(1, perc));
            dragTime = perc * totalDuration;
            
            if (isNaN(dragTime)) dragTime = 0;

            progressBar.style.width = (perc * 100) + '%';
            scrubBubble.style.display = 'block';
            scrubBubble.style.left = (perc * 100) + '%';
            scrubTime.innerText = formatTime(dragTime);

            // ZABEZPIECZENIE: Zawsze aktualizujemy pozycję background (usunięto stary psujący się warunek IF)
            let frameIdx = Math.floor(dragTime / 10);
            let col = frameIdx % 30; 
            let row = Math.floor(frameIdx / 30);
            spriteView.style.backgroundPosition = `-${col * 160}px -${row * 90}px`;
        }

        videoOverlay.addEventListener('click', (e) => {
            let now = Date.now();
            if (now - lastTapTime < 400) {
                clearTimeout(clickTimer); 
                
                let rect = videoOverlay.getBoundingClientRect();
                let clientX = e.clientX;
                if (clientX === undefined && e.changedTouches && e.changedTouches.length > 0) clientX = e.changedTouches[0].clientX;
                if (clientX === undefined) clientX = 0;

                let isLeft = (clientX - rect.left) < (rect.width / 2);
                
                seekAccumulator += isLeft ? -10 : 10;
                
                if (isLeft) {
                    seekIndL.innerText = seekAccumulator + "s";
                    seekIndL.style.opacity = '1'; setTimeout(() => seekIndL.style.opacity = '0', 500);
                } else {
                    seekIndR.innerText = "+" + seekAccumulator + "s";
                    seekIndR.style.opacity = '1'; setTimeout(() => seekIndR.style.opacity = '0', 500);
                }

                clearTimeout(window.finalSeekTimer);
                window.finalSeekTimer = setTimeout(() => {
                    let targetTime = (isNative ? vid.currentTime : currentOffset + vid.currentTime) + seekAccumulator;
                    triggerSeek(targetTime);
                    seekAccumulator = 0;
                }, 500);
                
            } else {
                clickTimer = setTimeout(() => {
                    if (seekAccumulator === 0) {
                        if (vid.paused) vid.play(); else vid.pause();
                    }
                }, 400);
            }
            lastTapTime = now;
        });

        function triggerSeek(newTime) {
            if (isNaN(newTime)) return;
            if (newTime < 0) newTime = 0;
            if (newTime > totalDuration) newTime = totalDuration;

            isSeeking = true; loader.style.display = 'block';
            currentOffset = isNative ? 0 : newTime;
            
            progressBar.style.width = ((newTime / totalDuration) * 100) + '%';
            timeDisplay.innerText = formatTime(newTime);
            
            if (isNative) {
                vid.currentTime = newTime;
                let playPromise = vid.play(); if (playPromise !== undefined) playPromise.catch(e => console.log(e));
                isSeeking = false;
            } else {
                vid.src = '<?=$streamUrl?>&start=' + newTime;
                let playPromise = vid.play(); if (playPromise !== undefined) playPromise.catch(e => console.log(e));
                isSeeking = false;
            }
        }

        function updateMuteIcon(vol) {
            if(vol == 0 || vid.muted) muteBtn.innerHTML = '&#128263;'; else if(vol < 0.5) muteBtn.innerHTML = '&#128265;'; else muteBtn.innerHTML = '&#128266;';
        }
        let savedVol = localStorage.getItem('vod_volume');
        if(savedVol !== null) { vid.volume = savedVol; volSlider.value = savedVol; updateMuteIcon(savedVol); }
        volSlider.addEventListener('input', (e) => { vid.volume = e.target.value; vid.muted = false; localStorage.setItem('vod_volume', vid.volume); updateMuteIcon(vid.volume); });
        muteBtn.addEventListener('click', () => { vid.muted = !vid.muted; if(vid.muted) { updateMuteIcon(0); volSlider.value = 0; } else { updateMuteIcon(vid.volume); volSlider.value = vid.volume; } });

        vid.addEventListener('timeupdate', () => {
            if ((totalDuration <= 0 || isNaN(totalDuration)) && vid.duration > 0 && vid.duration !== Infinity) {
                totalDuration = vid.duration;
                document.getElementById('durationDisplay').innerText = formatTime(totalDuration);
            }
            if (isSeeking || isDragging) return;
            let realTime = isNative ? vid.currentTime : currentOffset + vid.currentTime;
            if (isNaN(realTime)) return;
            if (realTime > totalDuration) realTime = totalDuration;
            
            if (totalDuration > 0) progressBar.style.width = ((realTime / totalDuration) * 100) + '%';
            timeDisplay.innerText = formatTime(realTime);
        });

        vid.addEventListener('loadedmetadata', () => {
            if (isNative) {
                if (totalDuration <= 0 || isNaN(totalDuration)) totalDuration = vid.duration;
                if (<?=$initialStart?> > 0 && vid.currentTime < 1) vid.currentTime = <?=$initialStart?>;
            }
        });

        vid.addEventListener('waiting', () => { loader.style.display = 'block'; });
        vid.addEventListener('playing', () => { loader.style.display = 'none'; });
        playBtn.addEventListener('click', () => { if (vid.paused) vid.play(); else vid.pause(); });

        let idleTimeout;
        function resetIdleTimer() {
            playerWrapper.classList.remove('idle'); document.body.classList.remove('idle');
            clearTimeout(idleTimeout);
            if (!vid.paused) { idleTimeout = setTimeout(() => { playerWrapper.classList.add('idle'); document.body.classList.add('idle'); }, 2500); }
        }
        document.addEventListener('mousemove', resetIdleTimer);
        document.addEventListener('touchstart', resetIdleTimer, {passive: true});
        
        vid.addEventListener('play', () => { playBtn.innerHTML = '&#10074;&#10074;'; resetIdleTimer(); });
        vid.addEventListener('pause', () => { playBtn.innerHTML = '&#9658;'; clearTimeout(idleTimeout); playerWrapper.classList.remove('idle'); document.body.classList.remove('idle'); });

        fsBtn.addEventListener('click', () => {
            let wrap = document.getElementById('player-wrapper');
            if (!document.fullscreenElement) { if (wrap.requestFullscreen) wrap.requestFullscreen(); } 
            else { if (document.exitFullscreen) document.exitFullscreen(); }
        });

        const nextUrl = "<?=str_replace('&amp;', '&', $nextUrl)?>";
        const nextFile = '<?=htmlspecialchars($nextFile, ENT_QUOTES)?>';

        vid.addEventListener('ended', () => {
            if (nextUrl) {
                if (document.fullscreenElement) { document.exitFullscreen().catch(err => console.log(err)); }
                let overlay = document.createElement('div');
                overlay.id = 'nextOverlay';
                overlay.style.cssText = 'position:absolute; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.95); display:flex; flex-direction:column; align-items:center; justify-content:center; z-index:9999; text-align:center; padding:20px;';
                overlay.innerHTML = `
                    <h2 style="color:#fff; margin-bottom:5px; font-size:24px;">Następny materiał:</h2>
                    <h3 style="color:#007bff; margin-top:0; font-size:18px; word-break:break-all;">${nextFile}</h3>
                    <p style="color:#bbb; font-size:16px;">Odtwarzanie za <span id="nextCd" style="font-weight:bold; color:#fff;">5</span>s...</p>
                    <div style="display:flex; gap:15px; margin-top:25px; flex-wrap:wrap; justify-content:center;">
                        <button onclick="window.location.assign('${nextUrl}')" style="background:#28a745; color:#fff; border:none; padding:12px 24px; font-size:16px; border-radius:6px; cursor:pointer; font-weight:bold; box-shadow:0 4px 10px rgba(0,0,0,0.5);">Odtwórz teraz</button>
                        <button onclick="cancelNext()" style="background:#444; color:#fff; border:none; padding:12px 24px; font-size:16px; border-radius:6px; cursor:pointer; font-weight:bold; box-shadow:0 4px 10px rgba(0,0,0,0.5);">Anuluj</button>
                    </div>
                `;
                document.getElementById('player-wrapper').appendChild(overlay);
                let countdown = 5;
                window.nextInterval = setInterval(() => {
                    countdown--; let cdSpan = document.getElementById('nextCd'); if(cdSpan) cdSpan.innerText = countdown;
                    if(countdown <= 0) { clearInterval(window.nextInterval); window.location.assign(nextUrl); }
                }, 1000);
            }
        });
        window.cancelNext = function() { if(window.nextInterval) clearInterval(window.nextInterval); let overlay = document.getElementById('nextOverlay'); if(overlay) overlay.remove(); }
    </script>
</body>
</html>
EOF

chown web3:client0 play.php
