|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>YT Search</title> |
|
<style> |
|
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f9f9f9; } |
|
#search-bar { display: flex; justify-content: center; padding: 20px; background-color: #333; } |
|
#search-bar input { width: 70%; padding: 10px; font-size: 16px; } |
|
#search-bar button { padding: 10px; font-size: 16px; } |
|
#results { display: flex; flex-wrap: wrap; gap: 15px; padding: 20px; } |
|
.tile { background: #fff; border: 1px solid #ddd; border-radius: 5px; width: 300px; overflow: hidden; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } |
|
.tile img { width: 100%; height: auto; } |
|
.tile h3 { font-size: 18px; padding: 10px; margin: 0; } |
|
.tile p { padding: 0 10px; margin: 0 0 10px; font-size: 14px; } |
|
.uploader { display: flex; align-items: center; padding: 10px; border-top: 1px solid #eee; } |
|
.uploader img { width: 30px; height: 30px; border-radius: 50%; margin-right: 10px; } |
|
</style> |
|
</head> |
|
<body> |
|
<div id="search-bar"> |
|
<input type="text" id="query" placeholder="Search for videos"> |
|
<button onclick="searchVideos()">Search</button> |
|
</div> |
|
<div id="results"></div> |
|
|
|
<script> |
|
async function searchVideos() { |
|
const query = document.getElementById('query').value; |
|
if (!query) return; |
|
|
|
const response = await fetch(`/search?query=${encodeURIComponent(query)}`); |
|
const data = await response.json(); |
|
|
|
const results = document.getElementById('results'); |
|
results.innerHTML = ''; |
|
|
|
if (data.error) { |
|
results.innerHTML = `<p>Error: ${data.error}</p>`; |
|
return; |
|
} |
|
|
|
data.forEach(video => { |
|
const tile = document.createElement('div'); |
|
tile.className = 'tile'; |
|
|
|
|
|
const thumbnail = video.thumbnail || `https://inv.nadeko.net/vi/${video.url.split('v=')[1]}/mqdefault.jpg`; |
|
|
|
|
|
fetch(`/channel-icon?channel_id=${video.uploader_url.split('/').pop()}`) |
|
.then(response => response.json()) |
|
.then(data => { |
|
const uploaderIcon = data.icon_url || 'https://upload.wikimedia.org/wikipedia/commons/4/42/Gray_chaos_circle.svg'; |
|
|
|
tile.innerHTML = ` |
|
<img src="${thumbnail}" alt="Thumbnail"> |
|
<h3>${video.title}</h3> |
|
<p>Views: ${video.view_count.toLocaleString()}</p> |
|
<p>Duration: ${Math.floor(video.duration / 60)}:${(video.duration % 60).toString().padStart(2, '0')}</p> |
|
<div class="uploader"> |
|
<img src="${uploaderIcon}" alt="Uploader"> |
|
<span>${video.uploader}</span> |
|
</div> |
|
`; |
|
}) |
|
.catch(error => { |
|
console.error('Error fetching channel icon:', error); |
|
const uploaderIcon = 'https://upload.wikimedia.org/wikipedia/commons/4/42/Gray_chaos_circle.svg'; |
|
tile.innerHTML = ` |
|
<img src="${thumbnail}" alt="Thumbnail"> |
|
<h3>${video.title}</h3> |
|
<p>Views: ${video.view_count.toLocaleString()}</p> |
|
<p>Duration: ${Math.floor(video.duration / 60)}:${(video.duration % 60).toString().padStart(2, '0')}</p> |
|
<div class="uploader"> |
|
<img src="${uploaderIcon}" alt="Uploader"> |
|
<span>${video.uploader}</span> |
|
</div> |
|
`; |
|
}); |
|
|
|
tile.addEventListener('click', () => { |
|
window.open(`https://www.youtube.com/watch?v=${video.url.split('v=')[1]}`, '_blank'); |
|
}); |
|
|
|
results.appendChild(tile); |
|
}); |
|
} |
|
</script> |
|
</body> |
|
</html> |
|
|