← Mike Higgins Home
Custom Code Experiment •

GeckoFish MP4

Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gecko Video Player</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            background-color: #000;
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
            overflow: hidden;
            color: #fff;
        }

        #video-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            background-color: #000;
        }

        #video-player {
            width: 100%;
            height: 100%;
            object-fit: contain;
            cursor: pointer;
        }

        .controls {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            background: linear-gradient(transparent, rgba(0, 0, 0, 0.8));
            padding: 20px;
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 15px;
            opacity: 0;
            transition: opacity 0.3s ease;
        }

        #video-container:hover .controls {
            opacity: 1;
        }

        .control-btn {
            background: rgba(30, 30, 30, 0.8);
            border: none;
            color: white;
            width: 50px;
            height: 50px;
            border-radius: 50%;
            cursor: pointer;
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: 20px;
            transition: all 0.2s ease;
        }

        .control-btn:hover {
            background: rgba(50, 50, 50, 0.9);
            transform: scale(1.1);
        }

        .progress-container {
            flex-grow: 1;
            height: 6px;
            background: rgba(255, 255, 255, 0.2);
            border-radius: 3px;
            cursor: pointer;
            margin: 0 15px;
        }

        .progress-bar {
            height: 100%;
            background: #4a9eff;
            border-radius: 3px;
            width: 0%;
            transition: width 0.1s;
        }

        .time-display {
            font-size: 14px;
            min-width: 100px;
            text-align: center;
            font-variant-numeric: tabular-nums;
        }

        #fullscreen-btn {
            margin-left: auto;
        }

        .title {
            position: absolute;
            top: 20px;
            left: 20px;
            font-size: 18px;
            font-weight: 500;
            opacity: 0.9;
        }

        .loading {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            font-size: 18px;
            color: #aaa;
        }

        .error {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
            padding: 30px;
            background: rgba(30, 30, 30, 0.9);
            border-radius: 10px;
            max-width: 80%;
        }

        .error h3 {
            margin-bottom: 15px;
            color: #ff6b6b;
        }

        .error-btn {
            background: #4a9eff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 5px;
            margin-top: 15px;
            cursor: pointer;
            font-weight: 500;
        }

        @media (max-width: 768px) {
            .controls {
                padding: 10px;
                gap: 10px;
            }

            .control-btn {
                width: 44px;
                height: 44px;
                font-size: 18px;
            }

            .time-display {
                min-width: 80px;
                font-size: 13px;
            }
        }
    </style>
</head>
<body>
    <div id="video-container">
        <div class="loading" id="loading">Loading video...</div>

        <video id="video-player" preload="auto">
            <source src="https://raw.githubusercontent.com/alaric-visi/gecko/main/gecko.mp4" type="video/mp4">
            Your browser does not support the video tag.
        </video>

        <div class="controls">
            <button class="control-btn" id="play-pause-btn">āø</button>
            <div class="time-display"><span id="current-time">0:00</span> / <span id="duration">0:00</span></div>
            <div class="progress-container" id="progress-container">
                <div class="progress-bar" id="progress-bar"></div>
            </div>
            <button class="control-btn" id="mute-btn">šŸ”Š</button>
            <button class="control-btn" id="fullscreen-btn">ā›¶</button>
        </div>

        <div class="error" id="error-message" style="display: none;">
            <h3>Error Loading Video</h3>
            <p id="error-text">Unable to load the video from GitHub. Please check your connection.</p>
            <button class="error-btn" id="retry-btn">Try Again</button>
        </div>
    </div>

    <script>
        const videoPlayer = document.getElementById('video-player');
        const playPauseBtn = document.getElementById('play-pause-btn');
        const muteBtn = document.getElementById('mute-btn');
        const fullscreenBtn = document.getElementById('fullscreen-btn');
        const currentTimeEl = document.getElementById('current-time');
        const durationEl = document.getElementById('duration');
        const progressBar = document.getElementById('progress-bar');
        const progressContainer = document.getElementById('progress-container');
        const loadingEl = document.getElementById('loading');
        const errorMessage = document.getElementById('error-message');
        const errorText = document.getElementById('error-text');
        const retryBtn = document.getElementById('retry-btn');
        const videoUrl = 'https://raw.githubusercontent.com/alaric-visi/gecko/main/gecko.mp4';

        function formatTime(seconds) {
            const mins = Math.floor(seconds / 60);
            const secs = Math.floor(seconds % 60);
            return `${mins}:${secs < 10 ? '0' : ''}${secs}`;
        }

        function updateTime() {
            const currentTime = videoPlayer.currentTime;
            const duration = videoPlayer.duration;

            currentTimeEl.textContent = formatTime(currentTime);

            if (duration) {
                durationEl.textContent = formatTime(duration);
                const progressPercent = (currentTime / duration) * 100;
                progressBar.style.width = `${progressPercent}%`;
            }
        }

        function togglePlayPause() {
            if (videoPlayer.paused || videoPlayer.ended) {
                videoPlayer.play();
                playPauseBtn.textContent = 'āø';
            } else {
                videoPlayer.pause();
                playPauseBtn.textContent = 'ā–¶';
            }
        }

        function toggleMute() {
            videoPlayer.muted = !videoPlayer.muted;
            muteBtn.textContent = videoPlayer.muted ? 'šŸ”‡' : 'šŸ”Š';
        }

        function toggleFullscreen() {
            if (!document.fullscreenElement) {
                if (document.documentElement.requestFullscreen) {
                    document.documentElement.requestFullscreen();
                } else if (document.documentElement.webkitRequestFullscreen) {
                    document.documentElement.webkitRequestFullscreen();
                } else if (document.documentElement.msRequestFullscreen) {
                    document.documentElement.msRequestFullscreen();
                }
            } else {
                if (document.exitFullscreen) {
                    document.exitFullscreen();
                } else if (document.webkitExitFullscreen) {
                    document.webkitExitFullscreen();
                } else if (document.msExitFullscreen) {
                    document.msExitFullscreen();
                }
            }
        }

        function setProgress(e) {
            const width = this.clientWidth;
            const clickX = e.offsetX;
            const duration = videoPlayer.duration;
            videoPlayer.currentTime = (clickX / width) * duration;
        }

        function handleVideoLoad() {
            loadingEl.style.display = 'none';
            errorMessage.style.display = 'none';
            videoPlayer.style.display = 'block';
            videoPlayer.play().catch(e => {
                console.log("Auto-play prevented, waiting for user interaction");
                playPauseBtn.textContent = 'ā–¶';
            });
        }

        function handleVideoError() {
            loadingEl.style.display = 'none';
            videoPlayer.style.display = 'none';
            errorMessage.style.display = 'block';

            if (videoPlayer.error) {
                switch(videoPlayer.error.code) {
                    case 1:
                        errorText.textContent = "Video loading was aborted.";
                        break;
                    case 2:
                        errorText.textContent = "Network error while loading video.";
                        break;
                    case 3:
                        errorText.textContent = "Video decoding error.";
                        break;
                    case 4:
                        errorText.textContent = "Video not supported or file format invalid.";
                        break;
                    default:
                        errorText.textContent = "An error occurred while loading the video.";
                }
            }
        }

        function initPlayer() {
            videoPlayer.src = videoUrl;
            videoPlayer.addEventListener('loadeddata', handleVideoLoad);
            videoPlayer.addEventListener('error', handleVideoError);
            videoPlayer.addEventListener('timeupdate', updateTime);
            videoPlayer.addEventListener('ended', () => {
                playPauseBtn.textContent = '↺';
            });

            playPauseBtn.addEventListener('click', togglePlayPause);
            muteBtn.addEventListener('click', toggleMute);
            fullscreenBtn.addEventListener('click', toggleFullscreen);
            progressContainer.addEventListener('click', setProgress);
            retryBtn.addEventListener('click', () => {
                loadingEl.style.display = 'block';
                errorMessage.style.display = 'none';
                videoPlayer.src = videoUrl;
                videoPlayer.load();
            });

            videoPlayer.addEventListener('click', togglePlayPause);

            document.addEventListener('keydown', (e) => {
                // Space bar toggles play/pause
                if (e.code === 'Space' && e.target === document.body) {
                    e.preventDefault();
                    togglePlayPause();
                }

                if (e.code === 'KeyM' && e.target === document.body) {
                    e.preventDefault();
                    toggleMute();
                }

                if (e.code === 'KeyF' && e.target === document.body) {
                    e.preventDefault();
                    toggleFullscreen();
                }

                if (e.code === 'ArrowLeft' && e.target === document.body) {
                    e.preventDefault();
                    videoPlayer.currentTime = Math.max(0, videoPlayer.currentTime - 5);
                }

                if (e.code === 'ArrowRight' && e.target === document.body) {
                    e.preventDefault();
                    videoPlayer.currentTime = Math.min(videoPlayer.duration, videoPlayer.currentTime + 5);
                }
            });

            videoPlayer.addEventListener('play', () => {
                playPauseBtn.textContent = 'āø';
            });

            videoPlayer.addEventListener('pause', () => {
                playPauseBtn.textContent = 'ā–¶';
            });

            document.addEventListener('fullscreenchange', updateFullscreenButton);
            document.addEventListener('webkitfullscreenchange', updateFullscreenButton);
            document.addEventListener('msfullscreenchange', updateFullscreenButton);

            function updateFullscreenButton() {
                if (document.fullscreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
                    fullscreenBtn.textContent = 'ā›¶';
                } else {
                    fullscreenBtn.textContent = 'ā›¶';
                }
            }
        }

        document.addEventListener('DOMContentLoaded', initPlayer);
    </script>
</body>
</html>