playlist_data / index.html
Aynursusuz's picture
Update index.html
fb0bc7f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VYVO - Playlist Portal</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Inter', Roboto, sans-serif;
background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%);
background-attachment: fixed;
color: #ffffff;
min-height: 100vh;
}
.container {
display: flex;
min-height: 100vh;
}
.sidebar {
width: 280px;
background: rgba(15, 12, 41, 0.8);
backdrop-filter: blur(20px);
padding: 32px 20px;
border-right: 1px solid rgba(255, 255, 255, 0.1);
position: fixed;
height: 100vh;
overflow-y: auto;
}
.sidebar::-webkit-scrollbar {
width: 6px;
}
.sidebar::-webkit-scrollbar-track {
background: rgba(255, 255, 255, 0.05);
}
.sidebar::-webkit-scrollbar-thumb {
background: rgba(124, 58, 237, 0.5);
border-radius: 3px;
}
.logo {
font-size: 2rem;
font-weight: 800;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 40px;
letter-spacing: 2px;
text-transform: uppercase;
}
.sidebar h3 {
color: rgba(255, 255, 255, 0.5);
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 16px;
}
.category-item {
padding: 12px 16px;
margin-bottom: 4px;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 0.95rem;
background: transparent;
}
.category-item:hover {
background: rgba(255, 255, 255, 0.08);
transform: translateX(4px);
}
.category-item.active {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.category-name {
display: flex;
align-items: center;
gap: 12px;
font-weight: 500;
}
.category-count {
background: rgba(255, 255, 255, 0.1);
padding: 4px 10px;
border-radius: 12px;
font-size: 0.7rem;
font-weight: 600;
}
.category-item.active .category-count {
background: rgba(255, 255, 255, 0.2);
}
.main-content {
flex: 1;
margin-left: 280px;
padding: 40px 64px;
}
.header {
margin-bottom: 8px;
}
.search-bar {
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.15);
border-radius: 12px;
padding: 16px 24px;
width: 100%;
max-width: 500px;
color: #ffffff;
font-size: 0.95rem;
margin-bottom: 8px;
transition: all 0.3s ease;
}
.search-bar::placeholder {
color: rgba(255, 255, 255, 0.4);
}
.search-bar:focus {
outline: none;
border-color: rgba(102, 126, 234, 0.8);
background: rgba(255, 255, 255, 0.12);
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.15);
}
.category-header {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 16px;
}
.category-header .icon {
font-size: 2.5rem;
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.3));
}
.category-header h2 {
font-size: 2rem;
font-weight: 800;
letter-spacing: -0.5px;
color: #ffffff;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
}
.playlist-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
}
.playlist-card {
background: rgba(255, 255, 255, 0.05);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
overflow: hidden;
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: pointer;
text-decoration: none;
display: block;
}
.playlist-card:hover {
border-color: rgba(102, 126, 234, 0.6);
transform: translateY(-8px);
box-shadow: 0 20px 40px rgba(102, 126, 234, 0.3);
background: rgba(255, 255, 255, 0.08);
}
.playlist-thumbnail {
width: 100%;
height: 200px;
position: relative;
overflow: hidden;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
}
.thumbnail-image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.playlist-card:hover .thumbnail-image {
transform: scale(1.05);
}
.thumbnail-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.thumbnail-placeholder svg {
width: 80px;
height: 80px;
opacity: 0.4;
}
.thumbnail-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 50%);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.3s ease;
}
.playlist-card:hover .thumbnail-overlay {
opacity: 1;
}
.open-button {
padding: 12px 32px;
background: rgba(255, 255, 255, 0.95);
color: #1a1a1a;
border: none;
border-radius: 8px;
font-weight: 600;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.open-button:hover {
background: white;
transform: scale(1.05);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}
.playlist-info {
padding: 20px;
}
.platform-badge {
display: inline-flex;
align-items: center;
gap: 6px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 6px 12px;
border-radius: 8px;
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 12px;
}
.playlist-title {
color: #ffffff;
font-weight: 600;
font-size: 1.1rem;
margin-bottom: 12px;
line-height: 1.4;
}
.playlist-meta {
display: flex;
align-items: center;
gap: 12px;
font-size: 0.85rem;
color: rgba(255, 255, 255, 0.6);
}
.meta-item {
display: flex;
align-items: center;
gap: 6px;
}
.loading {
text-align: center;
padding: 100px;
color: rgba(255, 255, 255, 0.6);
font-size: 1.2rem;
}
.empty-state {
text-align: center;
padding: 80px 20px;
}
.empty-state h3 {
font-size: 1.5rem;
margin-bottom: 8px;
color: #ffffff;
}
.empty-state p {
color: rgba(255, 255, 255, 0.6);
}
.skeleton {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@media (max-width: 1024px) {
.main-content {
padding: 32px 40px;
}
.playlist-grid {
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
}
@media (max-width: 768px) {
.sidebar {
width: 240px;
}
.main-content {
margin-left: 240px;
padding: 24px;
}
}
@media (max-width: 640px) {
.sidebar {
position: static;
width: 100%;
height: auto;
border-right: none;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.main-content {
margin-left: 0;
padding: 24px 20px;
}
.container {
flex-direction: column;
}
.playlist-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="sidebar">
<div class="logo">VYVO</div>
<h3>Categories</h3>
<div id="categoryList"></div>
</div>
<div class="main-content">
<div class="header">
<input type="text" class="search-bar" id="searchBar" placeholder="Search playlists...">
</div>
<div id="content" class="loading">Loading...</div>
</div>
</div>
<script>
let allData = null;
let currentCategory = null;
const thumbnailCache = {};
async function getPlaylistThumbnail(playlistUrl) {
if (thumbnailCache[playlistUrl]) {
return thumbnailCache[playlistUrl];
}
try {
const playlistId = playlistUrl.split('list=')[1];
if (!playlistId) return null;
// YouTube oEmbed API kullanarak thumbnail al
const response = await fetch(`https://www.youtube.com/oembed?url=${encodeURIComponent(playlistUrl)}&format=json`);
const data = await response.json();
const thumbnail = data.thumbnail_url || null;
thumbnailCache[playlistUrl] = thumbnail;
return thumbnail;
} catch (error) {
console.error('Thumbnail fetch error:', error);
return null;
}
}
async function loadData() {
try {
const response = await fetch('playlists.json');
if (!response.ok) throw new Error('Failed to load data');
allData = await response.json();
renderCategories();
if (allData.categories.length > 0) {
showCategory(allData.categories[0].id);
}
} catch (error) {
document.getElementById('content').innerHTML = '<div class="empty-state"><h3>Error</h3><p>' + error.message + '</p></div>';
}
}
function renderCategories() {
const categoryList = document.getElementById('categoryList');
categoryList.innerHTML = allData.categories.map(cat =>
'<div class="category-item" onclick="showCategory(\'' + cat.id + '\')" data-category="' + cat.id + '"><div class="category-name"><span>' + cat.icon + '</span><span>' + cat.name + '</span></div><span class="category-count">' + cat.playlists.length + '</span></div>'
).join('');
}
function createSkeletonCard() {
return '<div class="playlist-card"><div class="playlist-thumbnail skeleton"></div><div class="playlist-info"><div class="platform-badge">▶ YouTube</div><div class="playlist-title">Loading...</div></div></div>';
}
async function showCategory(categoryId) {
currentCategory = categoryId;
const category = allData.categories.find(c => c.id === categoryId);
document.querySelectorAll('.category-item').forEach(item => {
item.classList.remove('active');
if (item.dataset.category === categoryId) item.classList.add('active');
});
const content = document.getElementById('content');
// İlk olarak skeleton göster
content.innerHTML = '<div class="category-header"><span class="icon">' + category.icon + '</span><h2>' + category.name + '</h2></div><div class="playlist-grid">' +
category.playlists.map(() => createSkeletonCard()).join('') + '</div>';
// Thumbnail'leri yükle
const playlistCards = await Promise.all(category.playlists.map(async (pl) => {
const thumbnail = await getPlaylistThumbnail(pl.url);
return '<a href="' + pl.url + '" target="_blank" class="playlist-card"><div class="playlist-thumbnail">' +
(thumbnail
? '<img src="' + thumbnail + '" alt="' + pl.title + '" class="thumbnail-image">'
: '<div class="thumbnail-placeholder"><svg fill="currentColor" viewBox="0 0 24 24"><path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg></div>') +
'<div class="thumbnail-overlay"><button class="open-button">Open</button></div></div><div class="playlist-info"><div class="platform-badge">▶ YouTube</div><div class="playlist-title">' + pl.title + '</div><div class="playlist-meta"><div class="meta-item"><span>📂</span><span>' + category.name + '</span></div></div></div></a>';
}));
content.innerHTML = '<div class="category-header"><span class="icon">' + category.icon + '</span><h2>' + category.name + '</h2></div><div class="playlist-grid">' +
playlistCards.join('') + '</div>';
}
document.addEventListener('DOMContentLoaded', () => {
const searchBar = document.getElementById('searchBar');
searchBar.addEventListener('input', async (e) => {
const searchTerm = e.target.value.toLowerCase();
if (!searchTerm) {
if (currentCategory) showCategory(currentCategory);
return;
}
const results = [];
allData.categories.forEach(cat => {
cat.playlists.forEach(playlist => {
if (playlist.title.toLowerCase().includes(searchTerm)) {
results.push({ ...playlist, category: cat });
}
});
});
const content = document.getElementById('content');
if (results.length === 0) {
content.innerHTML = '<div class="empty-state"><h3>No results found</h3><p>Try searching with different keywords</p></div>';
} else {
// Skeleton göster
content.innerHTML = '<div class="category-header"><span class="icon">🔍</span><h2>Search Results</h2></div><div class="playlist-grid">' +
results.map(() => createSkeletonCard()).join('') + '</div>';
// Thumbnail'leri yükle
const searchCards = await Promise.all(results.map(async (result) => {
const thumbnail = await getPlaylistThumbnail(result.url);
return '<a href="' + result.url + '" target="_blank" class="playlist-card"><div class="playlist-thumbnail">' +
(thumbnail
? '<img src="' + thumbnail + '" alt="' + result.title + '" class="thumbnail-image">'
: '<div class="thumbnail-placeholder"><svg fill="currentColor" viewBox="0 0 24 24"><path d="M10 16.5l6-4.5-6-4.5v9zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8z"/></svg></div>') +
'<div class="thumbnail-overlay"><button class="open-button">Open</button></div></div><div class="playlist-info"><div class="platform-badge">▶ YouTube</div><div class="playlist-title">' + result.title + '</div><div class="playlist-meta"><div class="meta-item"><span>' + result.category.icon + '</span><span>' + result.category.name + '</span></div></div></div></a>';
}));
content.innerHTML = '<div class="category-header"><span class="icon">🔍</span><h2>Search Results</h2></div><div class="playlist-grid">' +
searchCards.join('') + '</div>';
}
});
});
loadData();
</script>
</body>
</html>