commit 6c86d4d202cf08fb02c0b2eb3bd4f9761d082f4a
Author: root <root@hub.scroll.pub>
Date: 2024-12-27 23:15:14 +0000
Subject: Initial commit
diff --git a/body.html b/body.html
new file mode 100644
index 0000000..8ea2de7
--- /dev/null
+++ b/body.html
@@ -0,0 +1,29 @@
+<div class="video-container">
+ <div class="video-wrapper">
+ <video id="main-video" controls>
+ <source src="sample.mp4" type="video/mp4">
+ Your browser does not support the video tag.
+ </video>
+ <div class="controls">
+ <button class="control-btn" id="play-pause">▶️</button>
+ <button class="control-btn" id="fullscreen">⛶</button>
+ <div class="seek-bar">
+ <input type="range" id="seek-slider" min="0" max="100" value="0">
+ </div>
+ </div>
+ </div>
+</div>
+
+<div class="orientation-notice">
+ <p>Rotate your device for different viewing modes!</p>
+</div>
+
+<div class="instructions">
+ <h2>How to Use</h2>
+ <ul>
+ <li>📱 Portrait Mode: Zoom and pan with pinch gestures</li>
+ <li>🖥️ Landscape Mode: Full 16:9 view</li>
+ <li>👆 Double tap to skip 10 seconds</li>
+ <li>🔄 Swipe vertically to change videos</li>
+ </ul>
+</div>
\ No newline at end of file
diff --git a/index.scroll b/index.scroll
new file mode 100644
index 0000000..135c832
--- /dev/null
+++ b/index.scroll
@@ -0,0 +1,8 @@
+buildHtml
+baseUrl https://immersiveplayer.scroll.pub
+metaTags
+editButton /edit.html
+title Immersive Video Player
+style.css
+body.html
+script.js
\ No newline at end of file
diff --git a/prompt.json b/prompt.json
new file mode 100644
index 0000000..aa51f29
--- /dev/null
+++ b/prompt.json
@@ -0,0 +1,25 @@
+{
+ "id": "ffee3901-ca54-4a73-9bdf-21699c370b0b",
+ "object": "chat.completion",
+ "created": 1735341292,
+ "model": "deepseek-chat",
+ "choices": [
+ {
+ "index": 0,
+ "message": {
+ "role": "assistant",
+ "content": "---domain---\nimmersiveplayer.scroll.pub\n---index.scroll---\nbuildHtml\nbaseUrl https://immersiveplayer.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Immersive Video Player\nstyle.css\nbody.html\nscript.js\n---body.html---\n<div class=\"video-container\">\n <div class=\"video-wrapper\">\n <video id=\"main-video\" controls>\n <source src=\"sample.mp4\" type=\"video/mp4\">\n Your browser does not support the video tag.\n </video>\n <div class=\"controls\">\n <button class=\"control-btn\" id=\"play-pause\">▶️</button>\n <button class=\"control-btn\" id=\"fullscreen\">⛶</button>\n <div class=\"seek-bar\">\n <input type=\"range\" id=\"seek-slider\" min=\"0\" max=\"100\" value=\"0\">\n </div>\n </div>\n </div>\n</div>\n\n<div class=\"orientation-notice\">\n <p>Rotate your device for different viewing modes!</p>\n</div>\n\n<div class=\"instructions\">\n <h2>How to Use</h2>\n <ul>\n <li>📱 Portrait Mode: Zoom and pan with pinch gestures</li>\n <li>🖥️ Landscape Mode: Full 16:9 view</li>\n <li>👆 Double tap to skip 10 seconds</li>\n <li>🔄 Swipe vertically to change videos</li>\n </ul>\n</div>\n---style.css---\n:root {\n --primary-color: #1e90ff;\n --control-bg: rgba(0, 0, 0, 0.7);\n --control-size: 40px;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Segoe UI', sans-serif;\n background: #000;\n color: #fff;\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n}\n\n.video-container {\n width: 100%;\n max-width: 800px;\n position: relative;\n overflow: hidden;\n border-radius: 15px;\n box-shadow: 0 0 20px rgba(255, 255, 255, 0.1);\n}\n\n.video-wrapper {\n position: relative;\n padding-top: 56.25%;\n}\n\nvideo {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.controls {\n position: absolute;\n bottom: 0;\n left: 0;\n right: 0;\n background: var(--control-bg);\n padding: 10px;\n display: flex;\n align-items: center;\n gap: 10px;\n opacity: 0;\n transition: opacity 0.3s ease;\n}\n\n.video-container:hover .controls {\n opacity: 1;\n}\n\n.control-btn {\n background: none;\n border: none;\n color: #fff;\n font-size: 1.2rem;\n cursor: pointer;\n padding: 5px;\n transition: transform 0.2s ease;\n}\n\n.control-btn:hover {\n transform: scale(1.1);\n}\n\n.seek-bar {\n flex: 1;\n}\n\n#seek-slider {\n width: 100%;\n cursor: pointer;\n}\n\n.orientation-notice {\n margin-top: 20px;\n text-align: center;\n font-size: 1.1rem;\n color: #aaa;\n}\n\n.instructions {\n margin-top: 30px;\n padding: 20px;\n background: rgba(255, 255, 255, 0.05);\n border-radius: 10px;\n max-width: 800px;\n}\n\n.instructions h2 {\n margin-bottom: 15px;\n color: var(--primary-color);\n}\n\n.instructions ul {\n list-style: none;\n padding: 0;\n}\n\n.instructions li {\n margin: 10px 0;\n font-size: 1rem;\n color: #ddd;\n}\n\n@media (max-width: 768px) {\n .video-container {\n border-radius: 0;\n }\n \n .instructions {\n margin: 20px;\n }\n}\n---script.js---\nconst video = document.getElementById('main-video');\nconst playPauseBtn = document.getElementById('play-pause');\nconst fullscreenBtn = document.getElementById('fullscreen');\nconst seekSlider = document.getElementById('seek-slider');\n\nlet isZoomed = false;\nlet startX, startY, translateX = 0, translateY = 0;\n\n// Play/Pause functionality\nplayPauseBtn.addEventListener('click', () => {\n if (video.paused) {\n video.play();\n playPauseBtn.textContent = '⏸️';\n } else {\n video.pause();\n playPauseBtn.textContent = '▶️';\n }\n});\n\n// Fullscreen functionality\nfullscreenBtn.addEventListener('click', () => {\n if (!document.fullscreenElement) {\n video.requestFullscreen();\n } else {\n document.exitFullscreen();\n }\n});\n\n// Seek functionality\nseekSlider.addEventListener('input', () => {\n const seekTime = video.duration * (seekSlider.value / 100);\n video.currentTime = seekTime;\n});\n\nvideo.addEventListener('timeupdate', () => {\n const value = (100 / video.duration) * video.currentTime;\n seekSlider.value = value;\n});\n\n// Double tap to skip\nlet lastTap = 0;\nvideo.addEventListener('touchend', (e) => {\n const currentTime = new Date().getTime();\n const tapLength = currentTime - lastTap;\n \n if (tapLength < 300 && tapLength > 0) {\n const skipTime = e.touches[0].clientX < window.innerWidth / 2 ? -10 : 10;\n video.currentTime += skipTime;\n }\n \n lastTap = currentTime;\n});\n\n// Handle orientation changes\nwindow.addEventListener('orientationchange', () => {\n if (isZoomed) {\n resetZoom();\n }\n});\n\n// Zoom and pan functionality\nvideo.addEventListener('touchstart', (e) => {\n if (e.touches.length === 2) {\n isZoomed = true;\n startX = (e.touches[0].pageX + e.touches[1].pageX) / 2;\n startY = (e.touches[0].pageY + e.touches[1].pageY) / 2;\n }\n});\n\nvideo.addEventListener('touchmove', (e) => {\n if (isZoomed && e.touches.length === 2) {\n const scale = Math.hypot(\n e.touches[0].pageX - e.touches[1].pageX,\n e.touches[0].pageY - e.touches[1].pageY\n ) / Math.hypot(\n e.touches[0].pageX - e.touches[1].pageX,\n e.touches[0].pageY - e.touches[1].pageY\n );\n\n const currentX = (e.touches[0].pageX + e.touches[1].pageX) / 2;\n const currentY = (e.touches[0].pageY + e.touches[1].pageY) / 2;\n\n translateX += currentX - startX;\n translateY += currentY - startY;\n\n video.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;\n video.style.transformOrigin = 'center center';\n }\n});\n\nvideo.addEventListener('touchend', () => {\n if (isZoomed) {\n isZoomed = false;\n }\n});\n\nfunction resetZoom() {\n video.style.transform = 'none';\n translateX = 0;\n translateY = 0;\n}\n---end---"
+ },
+ "logprobs": null,
+ "finish_reason": "stop"
+ }
+ ],
+ "usage": {
+ "prompt_tokens": 1113,
+ "completion_tokens": 1767,
+ "total_tokens": 2880,
+ "prompt_cache_hit_tokens": 0,
+ "prompt_cache_miss_tokens": 1113
+ },
+ "system_fingerprint": "fp_f1afce2943"
+}
\ No newline at end of file
diff --git a/readme.scroll b/readme.scroll
new file mode 100644
index 0000000..adbb242
--- /dev/null
+++ b/readme.scroll
@@ -0,0 +1,2 @@
+# immersiveplayer.scroll.pub
+Website generated by DeepSeek from prompt: Descrizione del videoplayer: Modalità Portrait (Verticale): • Visualizzazione principale: • Quando un filmato è in formato 16:9, viene zoomato per riempire l’intero schermo in modalità portrait. Questo significa che il video mostra la parte centrale (sia orizzontale che verticale) del contenuto, sacrificando le porzioni laterali del video. • Interazione Zoom: • È possibile zoomare in ulteriormente tramite un gesto di pinch-to-zoom (allargando le dita), mantenendo però il video a schermo intero. Lo zoom non può essere ridotto al punto di lasciare bordi neri o spazi vuoti. • Una volta zoomato, l’utente può muoversi all’interno del video trascinando con il dito per spostare la porzione visibile. • Navigazione video: • L’interfaccia consente di scorrere i video in stile TikTok con uno swipe verso l’alto o il basso per passare al video successivo o precedente. Modalità Landscape (Orizzontale): • Visualizzazione principale: • Quando lo schermo viene girato in modalità landscape, il video si ridimensiona automaticamente per adattarsi al formato 16:9 e riempire l’intero schermo orizzontale. • Non ci sono ritagli o porzioni nascoste in questa modalità: il video viene visualizzato nella sua interezza. • Interazione Zoom: • Anche in modalità landscape, l’utente può effettuare il pinch-to-zoom per ingrandire il video. Come nella modalità portrait, il video rimane fullscreen e le aree ingrandite possono essere esplorate trascinando il dito. • Non è possibile zoomare out a un punto in cui ci siano bordi neri. Comportamenti comuni: 1. Transizione Portrait-Landscape: • Il passaggio tra portrait e landscape è fluido: in modalità portrait, il video torna automaticamente al livello di zoom iniziale (se c’era uno zoom) e centra il contenuto visibile. • In modalità landscape, il video viene adattato al formato 16:9 senza interruzioni o lag. 2. Gestione dei controlli: • Controlli minimalisti per play, pausa, avanti/indietro, volume e modalità schermo intero. • Possibilità di doppio tap per avanzare o riavvolgere di 10 secondi. 3. Funzione Drag-to-Pan: • In entrambe le modalità, quando il video è zoomato, si può trascinare con il dito per esplorare diverse parti della scena. Il movimento è fluido e limitato ai bordi del video, evitando aree nere. 4. Compatibilità video: • Supporta video con aspect ratio diversi (1:1, 9:16, ecc.) adattando lo zoom automaticamente per riempire lo schermo nella modalità corrente. UI e Animazioni: • UI minimalista: • I controlli sono semi-trasparenti e scompaiono automaticamente dopo pochi secondi di inattività, riapparendo con un tap sullo schermo. • Animazioni fluide: • Il passaggio tra zoom e pan è accompagnato da transizioni fluide per una sensazione di naturalezza durante l’interazione. Questo videoplayer è progettato per massimizzare l’esperienza immersiva indipendentemente dall’orientamento del dispositivo, offrendo una combinazione tra flessibilità di zoom e una visualizzazione sempre a schermo intero.
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..21fe920
--- /dev/null
+++ b/script.js
@@ -0,0 +1,101 @@
+const video = document.getElementById('main-video');
+const playPauseBtn = document.getElementById('play-pause');
+const fullscreenBtn = document.getElementById('fullscreen');
+const seekSlider = document.getElementById('seek-slider');
+
+let isZoomed = false;
+let startX, startY, translateX = 0, translateY = 0;
+
+// Play/Pause functionality
+playPauseBtn.addEventListener('click', () => {
+ if (video.paused) {
+ video.play();
+ playPauseBtn.textContent = '⏸️';
+ } else {
+ video.pause();
+ playPauseBtn.textContent = '▶️';
+ }
+});
+
+// Fullscreen functionality
+fullscreenBtn.addEventListener('click', () => {
+ if (!document.fullscreenElement) {
+ video.requestFullscreen();
+ } else {
+ document.exitFullscreen();
+ }
+});
+
+// Seek functionality
+seekSlider.addEventListener('input', () => {
+ const seekTime = video.duration * (seekSlider.value / 100);
+ video.currentTime = seekTime;
+});
+
+video.addEventListener('timeupdate', () => {
+ const value = (100 / video.duration) * video.currentTime;
+ seekSlider.value = value;
+});
+
+// Double tap to skip
+let lastTap = 0;
+video.addEventListener('touchend', (e) => {
+ const currentTime = new Date().getTime();
+ const tapLength = currentTime - lastTap;
+
+ if (tapLength < 300 && tapLength > 0) {
+ const skipTime = e.touches[0].clientX < window.innerWidth / 2 ? -10 : 10;
+ video.currentTime += skipTime;
+ }
+
+ lastTap = currentTime;
+});
+
+// Handle orientation changes
+window.addEventListener('orientationchange', () => {
+ if (isZoomed) {
+ resetZoom();
+ }
+});
+
+// Zoom and pan functionality
+video.addEventListener('touchstart', (e) => {
+ if (e.touches.length === 2) {
+ isZoomed = true;
+ startX = (e.touches[0].pageX + e.touches[1].pageX) / 2;
+ startY = (e.touches[0].pageY + e.touches[1].pageY) / 2;
+ }
+});
+
+video.addEventListener('touchmove', (e) => {
+ if (isZoomed && e.touches.length === 2) {
+ const scale = Math.hypot(
+ e.touches[0].pageX - e.touches[1].pageX,
+ e.touches[0].pageY - e.touches[1].pageY
+ ) / Math.hypot(
+ e.touches[0].pageX - e.touches[1].pageX,
+ e.touches[0].pageY - e.touches[1].pageY
+ );
+
+ const currentX = (e.touches[0].pageX + e.touches[1].pageX) / 2;
+ const currentY = (e.touches[0].pageY + e.touches[1].pageY) / 2;
+
+ translateX += currentX - startX;
+ translateY += currentY - startY;
+
+ video.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;
+ video.style.transformOrigin = 'center center';
+ }
+});
+
+video.addEventListener('touchend', () => {
+ if (isZoomed) {
+ isZoomed = false;
+ }
+});
+
+function resetZoom() {
+ video.style.transform = 'none';
+ translateX = 0;
+ translateY = 0;
+}
\ No newline at end of file
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..2bc0aec
--- /dev/null
+++ b/style.css
@@ -0,0 +1,127 @@
+:root {
+ --primary-color: #1e90ff;
+ --control-bg: rgba(0, 0, 0, 0.7);
+ --control-size: 40px;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', sans-serif;
+ background: #000;
+ color: #fff;
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+}
+
+.video-container {
+ width: 100%;
+ max-width: 800px;
+ position: relative;
+ overflow: hidden;
+ border-radius: 15px;
+ box-shadow: 0 0 20px rgba(255, 255, 255, 0.1);
+}
+
+.video-wrapper {
+ position: relative;
+ padding-top: 56.25%;
+}
+
+video {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.controls {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: var(--control-bg);
+ padding: 10px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.video-container:hover .controls {
+ opacity: 1;
+}
+
+.control-btn {
+ background: none;
+ border: none;
+ color: #fff;
+ font-size: 1.2rem;
+ cursor: pointer;
+ padding: 5px;
+ transition: transform 0.2s ease;
+}
+
+.control-btn:hover {
+ transform: scale(1.1);
+}
+
+.seek-bar {
+ flex: 1;
+}
+
+#seek-slider {
+ width: 100%;
+ cursor: pointer;
+}
+
+.orientation-notice {
+ margin-top: 20px;
+ text-align: center;
+ font-size: 1.1rem;
+ color: #aaa;
+}
+
+.instructions {
+ margin-top: 30px;
+ padding: 20px;
+ background: rgba(255, 255, 255, 0.05);
+ border-radius: 10px;
+ max-width: 800px;
+}
+
+.instructions h2 {
+ margin-bottom: 15px;
+ color: var(--primary-color);
+}
+
+.instructions ul {
+ list-style: none;
+ padding: 0;
+}
+
+.instructions li {
+ margin: 10px 0;
+ font-size: 1rem;
+ color: #ddd;
+}
+
+@media (max-width: 768px) {
+ .video-container {
+ border-radius: 0;
+ }
+
+ .instructions {
+ margin: 20px;
+ }
+}
\ No newline at end of file