|
|
<!DOCTYPE html> |
|
|
<html lang="id"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>API Documentation - ytdl nauval</title> |
|
|
<style> |
|
|
@import url('https://fonts.googleapis.com/css2?family=Roboto:300,400,500,700&display=swap'); |
|
|
body { |
|
|
font-family: 'Roboto', sans-serif; |
|
|
text-align: center; |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
background: #f0f0f0; |
|
|
color: #333; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
min-height: 100vh; |
|
|
padding: 20px; |
|
|
box-sizing: border-box; |
|
|
} |
|
|
.container { |
|
|
width: 100%; |
|
|
max-width: 700px; |
|
|
padding: 30px; |
|
|
background: #fff; |
|
|
border-radius: 10px; |
|
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); |
|
|
animation: fadeIn 1.5s ease-in-out; |
|
|
} |
|
|
h2 { |
|
|
color: #333; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
.tab-buttons { |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
margin-bottom: 20px; |
|
|
} |
|
|
.tab-buttons button { |
|
|
background: #e0e0e0; |
|
|
color: #333; |
|
|
padding: 12px 20px; |
|
|
border: none; |
|
|
border-radius: 5px; |
|
|
font-size: 16px; |
|
|
cursor: pointer; |
|
|
margin: 0 5px; |
|
|
transition: background 0.3s, transform 0.3s; |
|
|
} |
|
|
.tab-buttons button:hover, .tab-buttons button.active { |
|
|
background: #ddd; |
|
|
transform: scale(1.05); |
|
|
} |
|
|
.tab-content { |
|
|
display: none; |
|
|
} |
|
|
.tab-content.active { |
|
|
display: block; |
|
|
} |
|
|
.input-group { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
gap: 10px; |
|
|
margin-bottom: 15px; |
|
|
} |
|
|
input, select, button { |
|
|
width: 100%; |
|
|
padding: 12px; |
|
|
border: 1px solid #ccc; |
|
|
background: #f9f9f9; |
|
|
color: #333; |
|
|
border-radius: 5px; |
|
|
font-size: 16px; |
|
|
transition: transform 0.3s ease-in-out; |
|
|
} |
|
|
input:focus, select:focus, button:focus { |
|
|
transform: scale(1.02); |
|
|
outline: none; |
|
|
border-color: #aaa; |
|
|
} |
|
|
button { |
|
|
background: #007bff; |
|
|
color: white; |
|
|
cursor: pointer; |
|
|
} |
|
|
button:hover { |
|
|
background: #0056b3; |
|
|
} |
|
|
.result-container { |
|
|
margin-top: 20px; |
|
|
padding: 15px; |
|
|
background: #f5f5f5; |
|
|
border-radius: 5px; |
|
|
font-size: 14px; |
|
|
color: #333; |
|
|
min-height: 100px; |
|
|
overflow: auto; |
|
|
word-wrap: break-word; |
|
|
} |
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(-10px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
@media (min-width: 768px) { |
|
|
.input-group { |
|
|
flex-direction: row; |
|
|
align-items: center; |
|
|
} |
|
|
input, select, button { |
|
|
flex: 1; |
|
|
} |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div class="container"> |
|
|
<h2>API Dock - ytdl by nauval</h2> |
|
|
<p>Masukkan data untuk mendapatkan hasil dari API</p> |
|
|
|
|
|
<div class="tab-buttons"> |
|
|
<button class="tab-button active" data-tab="search">Cari</button> |
|
|
<button class="tab-button" data-tab="info-download">Info & Download</button> |
|
|
</div> |
|
|
|
|
|
<div id="search" class="tab-content active"> |
|
|
<div class="input-group"> |
|
|
<input type="text" id="query" placeholder="Masukkan query pencarian..."> |
|
|
<button onclick="fetchData('/search/?query=', 'query')">Cari</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="info-download" class="tab-content"> |
|
|
<div class="input-group"> |
|
|
<input type="text" id="infoUrl" placeholder="Masukkan URL info..."> |
|
|
<button onclick="getInfo()">Dapatkan Info</button> |
|
|
</div> |
|
|
|
|
|
<div class="input-group"> |
|
|
<input type="text" id="downloadUrl" placeholder="Masukkan URL download..."> |
|
|
<select id="resolutionSelect"></select> |
|
|
<button onclick="fetchDownload()">Download Video (Buffer)</button> |
|
|
</div> |
|
|
|
|
|
<div class="input-group"> |
|
|
<input type="text" id="audioUrl" placeholder="Masukkan URL audio..."> |
|
|
<button onclick="downloadAudio()">Download Audio (Buffer)</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="result-container" id="result">Hasil: </div> |
|
|
|
|
|
<button onclick="location.href='/docs'">Dokumentasi API</button> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
const apiBase = "https://ytdlpyton.nvlgroup.my.id"; |
|
|
let videoInfo = null; |
|
|
|
|
|
document.querySelectorAll('.tab-button').forEach(button => { |
|
|
button.addEventListener('click', function() { |
|
|
const tabId = this.dataset.tab; |
|
|
document.querySelectorAll('.tab-content').forEach(tab => tab.classList.remove('active')); |
|
|
document.getElementById(tabId).classList.add('active'); |
|
|
document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active')); |
|
|
this.classList.add('active'); |
|
|
}); |
|
|
}); |
|
|
|
|
|
async function fetchData(endpoint, inputId) { |
|
|
const value = document.getElementById(inputId).value; |
|
|
if (!value) return alert("Harap masukkan input yang benar"); |
|
|
|
|
|
try { |
|
|
const response = await fetch(apiBase + endpoint + encodeURIComponent(value)); |
|
|
if (!response.ok) throw new Error("Gagal mengambil data"); |
|
|
const data = await response.json(); |
|
|
document.getElementById("result").textContent = "Hasil: " + JSON.stringify(data, null, 2); |
|
|
} catch (error) { |
|
|
document.getElementById("result").textContent = "Error: " + error.message; |
|
|
} |
|
|
} |
|
|
|
|
|
async function getInfo() { |
|
|
const url = document.getElementById("infoUrl").value; |
|
|
if (!url) return alert("Harap masukkan URL info yang benar"); |
|
|
|
|
|
try { |
|
|
const response = await fetch(apiBase + `/info/?url=${encodeURIComponent(url)}`); |
|
|
if (!response.ok) throw new Error("Gagal mengambil info"); |
|
|
videoInfo = await response.json(); |
|
|
document.getElementById("result").textContent = "Hasil: " + JSON.stringify(videoInfo, null, 2); |
|
|
populateResolutions(videoInfo.resolutions); |
|
|
} catch (error) { |
|
|
document.getElementById("result").textContent = "Error: " + error.message; |
|
|
} |
|
|
} |
|
|
|
|
|
function populateResolutions(resolutions) { |
|
|
const select = document.getElementById("resolutionSelect"); |
|
|
select.innerHTML = ""; |
|
|
if (resolutions) { |
|
|
resolutions.forEach(res => { |
|
|
const option = document.createElement("option"); |
|
|
option.value = res.resolution.replace("p", ""); |
|
|
option.textContent = res.resolution; |
|
|
select.appendChild(option); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
async function fetchDownload() { |
|
|
const url = document.getElementById("downloadUrl").value; |
|
|
const resolution = document.getElementById("resolutionSelect").value; |
|
|
if (!url) return alert("Harap masukkan URL yang benar"); |
|
|
|
|
|
try { |
|
|
const response = await fetch(apiBase + `/download/?url=${encodeURIComponent(url)}&resolution=${encodeURIComponent(resolution)}&mode=buffer`); |
|
|
if (!response.ok) throw new Error("Gagal mengambil data"); |
|
|
const blob = await response.blob(); |
|
|
const a = document.createElement('a'); |
|
|
a.href = window.URL.createObjectURL(blob); |
|
|
a.download = "video.mp4"; |
|
|
a.click(); |
|
|
} catch (error) { |
|
|
document.getElementById("result").textContent = "Error: " + error.message; |
|
|
} |
|
|
} |
|
|
|
|
|
async function downloadAudio() { |
|
|
const url = document.getElementById("audioUrl").value; |
|
|
if (!url) return alert("Harap masukkan URL audio yang benar"); |
|
|
|
|
|
try { |
|
|
const response = await fetch(apiBase + `/download/audio/?url=${encodeURIComponent(url)}&mode=buffer`); |
|
|
if (!response.ok) throw new Error("Gagal mengambil data"); |
|
|
const blob = await response.blob(); |
|
|
const a = document.createElement('a'); |
|
|
a.href = window.URL.createObjectURL(blob); |
|
|
a.download = "audio.mp3"; |
|
|
a.click(); |
|
|
} catch (error) { |
|
|
document.getElementById("result").textContent = "Error: " + error.message; |
|
|
} |
|
|
} |
|
|
</script> |
|
|
</body> |
|
|
</html> |
|
|
|