Spaces:
Running
Running
| <html lang="id"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Video Proxy - Remote Upload Doodstream</title> | |
| <style> | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| padding: 20px; | |
| } | |
| .container { | |
| background: white; | |
| border-radius: 20px; | |
| padding: 40px; | |
| max-width: 700px; | |
| width: 100%; | |
| box-shadow: 0 20px 60px rgba(0,0,0,0.3); | |
| animation: slideUp 0.5s ease; | |
| } | |
| @keyframes slideUp { | |
| from { opacity: 0; transform: translateY(30px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .header { text-align: center; margin-bottom: 30px; } | |
| .header .icon { font-size: 48px; margin-bottom: 10px; } | |
| .header h1 { color: #333; font-size: 28px; font-weight: 700; } | |
| .header p { color: #777; margin-top: 8px; font-size: 14px; } | |
| .form-group { margin-bottom: 20px; } | |
| .form-group label { display: block; color: #555; font-weight: 600; margin-bottom: 8px; font-size: 14px; } | |
| .form-group input, .form-group select { | |
| width: 100%; padding: 12px 16px; border: 2px solid #e0e0e0; border-radius: 10px; | |
| font-size: 14px; transition: all 0.3s; font-family: inherit; | |
| } | |
| .form-group input:focus, .form-group select:focus { | |
| outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,0.1); | |
| } | |
| .form-group input::placeholder { color: #aaa; } | |
| .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; } | |
| .btn { | |
| width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; border: none; border-radius: 10px; font-size: 16px; font-weight: 600; | |
| cursor: pointer; transition: all 0.3s; margin-top: 10px; | |
| } | |
| .btn:hover { transform: translateY(-2px); box-shadow: 0 10px 30px rgba(102,126,234,0.4); } | |
| .btn:active { transform: translateY(0); } | |
| .result { | |
| margin-top: 25px; padding: 20px; background: #f8f9fa; border-radius: 12px; | |
| display: none; animation: slideUp 0.4s ease; | |
| } | |
| .result.show { display: block; } | |
| .result .label { color: #666; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; } | |
| .result .url-box { display: flex; gap: 10px; margin-top: 8px; align-items: center; } | |
| .result .url-box input { | |
| flex: 1; padding: 10px 12px; border: 2px solid #e0e0e0; border-radius: 8px; | |
| font-size: 13px; background: white; font-family: monospace; color: #333; | |
| } | |
| .result .url-box input:focus { outline: none; border-color: #667eea; } | |
| .result .copy-btn { | |
| padding: 10px 18px; background: #667eea; color: white; border: none; border-radius: 8px; | |
| cursor: pointer; font-size: 13px; font-weight: 600; transition: all 0.3s; white-space: nowrap; | |
| } | |
| .result .copy-btn:hover { background: #5a6fd6; } | |
| .result .copy-btn.copied { background: #28a745; } | |
| .result .info { | |
| margin-top: 12px; padding: 12px; background: #fff3cd; border-radius: 8px; | |
| font-size: 13px; color: #856404; | |
| } | |
| .result .info strong { color: #533f03; } | |
| .result .test-link { | |
| margin-top: 12px; display: inline-block; color: #667eea; | |
| text-decoration: none; font-size: 13px; font-weight: 600; | |
| } | |
| .result .test-link:hover { text-decoration: underline; } | |
| .features { | |
| display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; | |
| margin-top: 25px; padding-top: 25px; border-top: 2px solid #f0f0f0; | |
| } | |
| .feature { text-align: center; } | |
| .feature .emoji { font-size: 24px; display: block; } | |
| .feature .name { font-size: 12px; color: #777; margin-top: 5px; } | |
| .alert { padding: 12px 16px; border-radius: 10px; margin-bottom: 20px; font-size: 14px; } | |
| .alert-error { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; } | |
| .alert-success { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; } | |
| @media (max-width: 600px) { | |
| .container { padding: 25px; } | |
| .form-row { grid-template-columns: 1fr; } | |
| .features { grid-template-columns: 1fr; gap: 10px; } | |
| .result .url-box { flex-direction: column; } | |
| .result .url-box input { width: 100%; } | |
| .result .copy-btn { width: 100%; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <div class="header"> | |
| <div class="icon">??</div> | |
| <h1>Video Proxy</h1> | |
| <p>Remote upload untuk Doodstream • Streaming langsung tanpa simpan file</p> | |
| </div> | |
| <div id="alertContainer"></div> | |
| <form id="proxyForm"> | |
| <div class="form-group"> | |
| <label for="videoUrl">?? URL Video</label> | |
| <input type="text" id="videoUrl" placeholder="https://example.com/video.mp4" required> | |
| </div> | |
| <div class="form-row"> | |
| <div class="form-group"> | |
| <label for="referer">Referer (Opsional)</label> | |
| <input type="text" id="referer" placeholder="https://sumber.com/"> | |
| </div> | |
| <div class="form-group"> | |
| <label for="userAgent">User-Agent (Opsional)</label> | |
| <select id="userAgent"> | |
| <option value="default">Default (Chrome)</option> | |
| <option value="mobile">Mobile (Android)</option> | |
| <option value="firefox">Firefox</option> | |
| <option value="safari">Safari</option> | |
| </select> | |
| </div> | |
| </div> | |
| <button type="submit" class="btn">?? Generate Proxy URL</button> | |
| </form> | |
| <div id="result" class="result"> | |
| <div class="label">?? Proxy URL untuk Doodstream</div> | |
| <div class="url-box"> | |
| <input type="text" id="proxyResult" readonly> | |
| <button class="copy-btn" id="copyBtn">?? Copy</button> | |
| </div> | |
| <div class="info"> | |
| ?? <strong>Tips:</strong> Gunakan URL ini di Doodstream untuk remote upload. | |
| Video akan streaming langsung tanpa menyimpan file di server. | |
| </div> | |
| <a href="#" id="testLink" class="test-link" target="_blank">?? Test Proxy URL</a> | |
| </div> | |
| <div class="features"> | |
| <div class="feature"> | |
| <span class="emoji">?</span> | |
| <span class="name">Streaming Langsung</span> | |
| </div> | |
| <div class="feature"> | |
| <span class="emoji">??</span> | |
| <span class="name">Tanpa Simpan File</span> | |
| </div> | |
| <div class="feature"> | |
| <span class="emoji">??</span> | |
| <span class="name">CORS Support</span> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| const form = document.getElementById('proxyForm'); | |
| const videoUrlInput = document.getElementById('videoUrl'); | |
| const refererInput = document.getElementById('referer'); | |
| const userAgentSelect = document.getElementById('userAgent'); | |
| const resultDiv = document.getElementById('result'); | |
| const proxyResultInput = document.getElementById('proxyResult'); | |
| const copyBtn = document.getElementById('copyBtn'); | |
| const testLink = document.getElementById('testLink'); | |
| const alertContainer = document.getElementById('alertContainer'); | |
| const userAgents = { | |
| 'default': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', | |
| 'mobile': 'Mozilla/5.0 (Linux; Android 11; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36', | |
| 'firefox': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0', | |
| 'safari': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15' | |
| }; | |
| form.addEventListener('submit', function(e) { | |
| e.preventDefault(); | |
| let videoUrl = videoUrlInput.value.trim(); | |
| if (!videoUrl) { | |
| showAlert('? Mohon masukkan URL video!', 'error'); | |
| return; | |
| } | |
| try { | |
| new URL(videoUrl); | |
| } catch (_) { | |
| showAlert('? URL tidak valid! Masukkan URL yang benar.', 'error'); | |
| return; | |
| } | |
| const encodedUrl = encodeURIComponent(videoUrl); | |
| let proxyUrl = '/proxy-video?u=' + encodedUrl; | |
| const referer = refererInput.value.trim(); | |
| if (referer) { | |
| proxyUrl += '&referer=' + encodeURIComponent(referer); | |
| } | |
| const uaKey = userAgentSelect.value; | |
| if (uaKey !== 'default') { | |
| proxyUrl += '&ua=' + encodeURIComponent(userAgents[uaKey]); | |
| } | |
| const fullUrl = window.location.origin + proxyUrl; | |
| proxyResultInput.value = fullUrl; | |
| testLink.href = proxyUrl; | |
| resultDiv.classList.add('show'); | |
| copyBtn.textContent = '?? Copy'; | |
| copyBtn.classList.remove('copied'); | |
| showAlert('? Proxy URL berhasil dibuat!', 'success'); | |
| }); | |
| copyBtn.addEventListener('click', function() { | |
| const text = proxyResultInput.value; | |
| navigator.clipboard.writeText(text).then(() => { | |
| copyBtn.textContent = '? Copied!'; | |
| copyBtn.classList.add('copied'); | |
| setTimeout(() => { | |
| copyBtn.textContent = '?? Copy'; | |
| copyBtn.classList.remove('copied'); | |
| }, 2000); | |
| }).catch(() => { | |
| proxyResultInput.select(); | |
| document.execCommand('copy'); | |
| copyBtn.textContent = '? Copied!'; | |
| setTimeout(() => { | |
| copyBtn.textContent = '?? Copy'; | |
| }, 2000); | |
| }); | |
| }); | |
| function showAlert(message, type) { | |
| const alertClass = type === 'error' ? 'alert-error' : 'alert-success'; | |
| alertContainer.innerHTML = '<div class="alert ' + alertClass + '">' + message + '</div>'; | |
| if (type === 'success') { | |
| setTimeout(() => { | |
| const alert = alertContainer.querySelector('.alert'); | |
| if (alert) { | |
| alert.style.transition = 'opacity 0.5s'; | |
| alert.style.opacity = '0'; | |
| setTimeout(() => alert.remove(), 500); | |
| } | |
| }, 5000); | |
| } | |
| } | |
| videoUrlInput.focus(); | |
| </script> | |
| </body> | |
| </html> |