Aynursusuz commited on
Commit
83bac6a
·
verified ·
1 Parent(s): ee6cd2a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +120 -94
index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Playlist Portal</title>
7
  <style>
8
  * {
9
  margin: 0;
@@ -49,14 +49,15 @@
49
  }
50
 
51
  .logo {
52
- font-size: 1.5rem;
53
- font-weight: 700;
54
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
55
  -webkit-background-clip: text;
56
  -webkit-text-fill-color: transparent;
57
  background-clip: text;
58
  margin-bottom: 40px;
59
- letter-spacing: -0.5px;
 
60
  }
61
 
62
  .sidebar h3 {
@@ -113,27 +114,11 @@
113
  .main-content {
114
  flex: 1;
115
  margin-left: 280px;
116
- padding: 48px 64px;
117
  }
118
 
119
  .header {
120
- margin-bottom: 48px;
121
- }
122
-
123
- .header h1 {
124
- font-size: 3rem;
125
- margin-bottom: 12px;
126
- font-weight: 700;
127
- letter-spacing: -1.5px;
128
- background: linear-gradient(135deg, #ffffff 0%, #a0a0a0 100%);
129
- -webkit-background-clip: text;
130
- -webkit-text-fill-color: transparent;
131
- background-clip: text;
132
- }
133
-
134
- .header p {
135
- color: rgba(255, 255, 255, 0.6);
136
- font-size: 1.1rem;
137
  }
138
 
139
  .search-bar {
@@ -146,7 +131,7 @@
146
  max-width: 500px;
147
  color: #ffffff;
148
  font-size: 0.95rem;
149
- margin-top: 24px;
150
  transition: all 0.3s ease;
151
  }
152
 
@@ -181,7 +166,7 @@
181
 
182
  .playlist-grid {
183
  display: grid;
184
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
185
  gap: 24px;
186
  }
187
 
@@ -206,26 +191,36 @@
206
 
207
  .playlist-thumbnail {
208
  width: 100%;
209
- height: 180px;
210
  position: relative;
211
  overflow: hidden;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
213
  }
214
 
215
- .thumbnail-pattern {
216
- position: absolute;
217
- top: 0;
218
- left: 0;
219
- right: 0;
220
- bottom: 0;
221
- background:
222
- linear-gradient(45deg, rgba(255, 255, 255, 0.05) 25%, transparent 25%),
223
- linear-gradient(-45deg, rgba(255, 255, 255, 0.05) 25%, transparent 25%),
224
- linear-gradient(45deg, transparent 75%, rgba(255, 255, 255, 0.05) 75%),
225
- linear-gradient(-45deg, transparent 75%, rgba(255, 255, 255, 0.05) 75%);
226
- background-size: 20px 20px;
227
- background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
228
- opacity: 0.3;
229
  }
230
 
231
  .thumbnail-overlay {
@@ -234,44 +229,40 @@
234
  left: 0;
235
  right: 0;
236
  bottom: 0;
 
237
  display: flex;
238
  align-items: center;
239
  justify-content: center;
240
- background: rgba(0, 0, 0, 0.2);
241
- transition: all 0.3s ease;
242
  }
243
 
244
  .playlist-card:hover .thumbnail-overlay {
245
- background: rgba(0, 0, 0, 0.4);
246
  }
247
 
248
- .play-button {
249
- width: 64px;
250
- height: 64px;
251
  background: rgba(255, 255, 255, 0.95);
252
- border-radius: 50%;
253
- display: flex;
254
- align-items: center;
255
- justify-content: center;
256
- transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
257
- box-shadow: 0 8px 20px rgba(0, 0, 0, 0.3);
 
 
 
258
  }
259
 
260
- .playlist-card:hover .play-button {
261
- transform: scale(1.15);
262
  background: white;
263
- box-shadow: 0 12px 30px rgba(0, 0, 0, 0.5);
264
- }
265
-
266
- .play-button::after {
267
- content: '▶';
268
- color: #1a1a1a;
269
- font-size: 20px;
270
- margin-left: 3px;
271
  }
272
 
273
  .playlist-info {
274
- padding: 24px;
275
  }
276
 
277
  .platform-badge {
@@ -287,7 +278,6 @@
287
  text-transform: uppercase;
288
  letter-spacing: 0.5px;
289
  margin-bottom: 12px;
290
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
291
  }
292
 
293
  .playlist-title {
@@ -334,13 +324,14 @@
334
  color: rgba(255, 255, 255, 0.6);
335
  }
336
 
337
- /* Gradient variations for thumbnails */
338
- .gradient-1 { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
339
- .gradient-2 { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
340
- .gradient-3 { background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); }
341
- .gradient-4 { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
342
- .gradient-5 { background: linear-gradient(135deg, #fa709a 0%, #fee140 100%); }
343
- .gradient-6 { background: linear-gradient(135deg, #30cfd0 0%, #330867 100%); }
 
344
 
345
  @media (max-width: 1024px) {
346
  .main-content {
@@ -361,10 +352,6 @@
361
  margin-left: 240px;
362
  padding: 24px;
363
  }
364
-
365
- .header h1 {
366
- font-size: 2rem;
367
- }
368
  }
369
 
370
  @media (max-width: 640px) {
@@ -388,25 +375,19 @@
388
  .playlist-grid {
389
  grid-template-columns: 1fr;
390
  }
391
-
392
- .header h1 {
393
- font-size: 1.75rem;
394
- }
395
  }
396
  </style>
397
  </head>
398
  <body>
399
  <div class="container">
400
  <div class="sidebar">
401
- <div class="logo">📚 Portal</div>
402
  <h3>Categories</h3>
403
  <div id="categoryList"></div>
404
  </div>
405
 
406
  <div class="main-content">
407
  <div class="header">
408
- <h1 id="mainTitle">Playlist Portal</h1>
409
- <p id="subtitle">Curated educational content</p>
410
  <input type="text" class="search-bar" id="searchBar" placeholder="Search playlists...">
411
  </div>
412
 
@@ -417,11 +398,28 @@
417
  <script>
418
  let allData = null;
419
  let currentCategory = null;
 
420
 
421
- const gradientClasses = ['gradient-1', 'gradient-2', 'gradient-3', 'gradient-4', 'gradient-5', 'gradient-6'];
 
 
 
422
 
423
- function getGradientClass(index) {
424
- return gradientClasses[index % gradientClasses.length];
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  }
426
 
427
  async function loadData() {
@@ -430,8 +428,6 @@
430
  if (!response.ok) throw new Error('Failed to load data');
431
  allData = await response.json();
432
 
433
- document.getElementById('mainTitle').textContent = allData.title;
434
- document.getElementById('subtitle').textContent = allData.subtitle;
435
  renderCategories();
436
 
437
  if (allData.categories.length > 0) {
@@ -449,7 +445,11 @@
449
  ).join('');
450
  }
451
 
452
- function showCategory(categoryId) {
 
 
 
 
453
  currentCategory = categoryId;
454
  const category = allData.categories.find(c => c.id === categoryId);
455
  document.querySelectorAll('.category-item').forEach(item => {
@@ -459,15 +459,28 @@
459
 
460
  const content = document.getElementById('content');
461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  content.innerHTML = '<div class="category-header"><span class="icon">' + category.icon + '</span><h2>' + category.name + '</h2></div><div class="playlist-grid">' +
463
- category.playlists.map((pl, index) => {
464
- return '<a href="' + pl.url + '" target="_blank" class="playlist-card"><div class="playlist-thumbnail ' + getGradientClass(index) + '"><div class="thumbnail-pattern"></div><div class="thumbnail-overlay"><div class="play-button"></div></div></div><div class="playlist-info"><div class="platform-badge"><span>▶</span> 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>';
465
- }).join('') + '</div>';
466
  }
467
 
468
  document.addEventListener('DOMContentLoaded', () => {
469
  const searchBar = document.getElementById('searchBar');
470
- searchBar.addEventListener('input', (e) => {
471
  const searchTerm = e.target.value.toLowerCase();
472
  if (!searchTerm) {
473
  if (currentCategory) showCategory(currentCategory);
@@ -488,10 +501,23 @@
488
  if (results.length === 0) {
489
  content.innerHTML = '<div class="empty-state"><h3>No results found</h3><p>Try searching with different keywords</p></div>';
490
  } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  content.innerHTML = '<div class="category-header"><span class="icon">🔍</span><h2>Search Results</h2></div><div class="playlist-grid">' +
492
- results.map((result, index) => {
493
- return '<a href="' + result.url + '" target="_blank" class="playlist-card"><div class="playlist-thumbnail ' + getGradientClass(index) + '"><div class="thumbnail-pattern"></div><div class="thumbnail-overlay"><div class="play-button"></div></div></div><div class="playlist-info"><div class="platform-badge"><span>▶</span> 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>';
494
- }).join('') + '</div>';
495
  }
496
  });
497
  });
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>VYVO - Playlist Portal</title>
7
  <style>
8
  * {
9
  margin: 0;
 
49
  }
50
 
51
  .logo {
52
+ font-size: 2rem;
53
+ font-weight: 800;
54
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
55
  -webkit-background-clip: text;
56
  -webkit-text-fill-color: transparent;
57
  background-clip: text;
58
  margin-bottom: 40px;
59
+ letter-spacing: 2px;
60
+ text-transform: uppercase;
61
  }
62
 
63
  .sidebar h3 {
 
114
  .main-content {
115
  flex: 1;
116
  margin-left: 280px;
117
+ padding: 40px 64px;
118
  }
119
 
120
  .header {
121
+ margin-bottom: 32px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  }
123
 
124
  .search-bar {
 
131
  max-width: 500px;
132
  color: #ffffff;
133
  font-size: 0.95rem;
134
+ margin-bottom: 40px;
135
  transition: all 0.3s ease;
136
  }
137
 
 
166
 
167
  .playlist-grid {
168
  display: grid;
169
+ grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
170
  gap: 24px;
171
  }
172
 
 
191
 
192
  .playlist-thumbnail {
193
  width: 100%;
194
+ height: 200px;
195
  position: relative;
196
  overflow: hidden;
197
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
198
+ }
199
+
200
+ .thumbnail-image {
201
+ width: 100%;
202
+ height: 100%;
203
+ object-fit: cover;
204
+ transition: transform 0.4s ease;
205
+ }
206
+
207
+ .playlist-card:hover .thumbnail-image {
208
+ transform: scale(1.05);
209
+ }
210
+
211
+ .thumbnail-placeholder {
212
+ width: 100%;
213
+ height: 100%;
214
+ display: flex;
215
+ align-items: center;
216
+ justify-content: center;
217
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
218
  }
219
 
220
+ .thumbnail-placeholder svg {
221
+ width: 80px;
222
+ height: 80px;
223
+ opacity: 0.4;
 
 
 
 
 
 
 
 
 
 
224
  }
225
 
226
  .thumbnail-overlay {
 
229
  left: 0;
230
  right: 0;
231
  bottom: 0;
232
+ background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 50%);
233
  display: flex;
234
  align-items: center;
235
  justify-content: center;
236
+ opacity: 0;
237
+ transition: opacity 0.3s ease;
238
  }
239
 
240
  .playlist-card:hover .thumbnail-overlay {
241
+ opacity: 1;
242
  }
243
 
244
+ .open-button {
245
+ padding: 12px 32px;
 
246
  background: rgba(255, 255, 255, 0.95);
247
+ color: #1a1a1a;
248
+ border: none;
249
+ border-radius: 8px;
250
+ font-weight: 600;
251
+ font-size: 0.9rem;
252
+ cursor: pointer;
253
+ transition: all 0.3s ease;
254
+ text-transform: uppercase;
255
+ letter-spacing: 0.5px;
256
  }
257
 
258
+ .open-button:hover {
 
259
  background: white;
260
+ transform: scale(1.05);
261
+ box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
 
 
 
 
 
 
262
  }
263
 
264
  .playlist-info {
265
+ padding: 20px;
266
  }
267
 
268
  .platform-badge {
 
278
  text-transform: uppercase;
279
  letter-spacing: 0.5px;
280
  margin-bottom: 12px;
 
281
  }
282
 
283
  .playlist-title {
 
324
  color: rgba(255, 255, 255, 0.6);
325
  }
326
 
327
+ .skeleton {
328
+ animation: pulse 1.5s ease-in-out infinite;
329
+ }
330
+
331
+ @keyframes pulse {
332
+ 0%, 100% { opacity: 1; }
333
+ 50% { opacity: 0.5; }
334
+ }
335
 
336
  @media (max-width: 1024px) {
337
  .main-content {
 
352
  margin-left: 240px;
353
  padding: 24px;
354
  }
 
 
 
 
355
  }
356
 
357
  @media (max-width: 640px) {
 
375
  .playlist-grid {
376
  grid-template-columns: 1fr;
377
  }
 
 
 
 
378
  }
379
  </style>
380
  </head>
381
  <body>
382
  <div class="container">
383
  <div class="sidebar">
384
+ <div class="logo">VYVO</div>
385
  <h3>Categories</h3>
386
  <div id="categoryList"></div>
387
  </div>
388
 
389
  <div class="main-content">
390
  <div class="header">
 
 
391
  <input type="text" class="search-bar" id="searchBar" placeholder="Search playlists...">
392
  </div>
393
 
 
398
  <script>
399
  let allData = null;
400
  let currentCategory = null;
401
+ const thumbnailCache = {};
402
 
403
+ async function getPlaylistThumbnail(playlistUrl) {
404
+ if (thumbnailCache[playlistUrl]) {
405
+ return thumbnailCache[playlistUrl];
406
+ }
407
 
408
+ try {
409
+ const playlistId = playlistUrl.split('list=')[1];
410
+ if (!playlistId) return null;
411
+
412
+ // YouTube oEmbed API kullanarak thumbnail al
413
+ const response = await fetch(`https://www.youtube.com/oembed?url=${encodeURIComponent(playlistUrl)}&format=json`);
414
+ const data = await response.json();
415
+
416
+ const thumbnail = data.thumbnail_url || null;
417
+ thumbnailCache[playlistUrl] = thumbnail;
418
+ return thumbnail;
419
+ } catch (error) {
420
+ console.error('Thumbnail fetch error:', error);
421
+ return null;
422
+ }
423
  }
424
 
425
  async function loadData() {
 
428
  if (!response.ok) throw new Error('Failed to load data');
429
  allData = await response.json();
430
 
 
 
431
  renderCategories();
432
 
433
  if (allData.categories.length > 0) {
 
445
  ).join('');
446
  }
447
 
448
+ function createSkeletonCard() {
449
+ 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>';
450
+ }
451
+
452
+ async function showCategory(categoryId) {
453
  currentCategory = categoryId;
454
  const category = allData.categories.find(c => c.id === categoryId);
455
  document.querySelectorAll('.category-item').forEach(item => {
 
459
 
460
  const content = document.getElementById('content');
461
 
462
+ // İlk olarak skeleton göster
463
+ content.innerHTML = '<div class="category-header"><span class="icon">' + category.icon + '</span><h2>' + category.name + '</h2></div><div class="playlist-grid">' +
464
+ category.playlists.map(() => createSkeletonCard()).join('') + '</div>';
465
+
466
+ // Thumbnail'leri yükle
467
+ const playlistCards = await Promise.all(category.playlists.map(async (pl) => {
468
+ const thumbnail = await getPlaylistThumbnail(pl.url);
469
+
470
+ return '<a href="' + pl.url + '" target="_blank" class="playlist-card"><div class="playlist-thumbnail">' +
471
+ (thumbnail
472
+ ? '<img src="' + thumbnail + '" alt="' + pl.title + '" class="thumbnail-image">'
473
+ : '<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>') +
474
+ '<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>';
475
+ }));
476
+
477
  content.innerHTML = '<div class="category-header"><span class="icon">' + category.icon + '</span><h2>' + category.name + '</h2></div><div class="playlist-grid">' +
478
+ playlistCards.join('') + '</div>';
 
 
479
  }
480
 
481
  document.addEventListener('DOMContentLoaded', () => {
482
  const searchBar = document.getElementById('searchBar');
483
+ searchBar.addEventListener('input', async (e) => {
484
  const searchTerm = e.target.value.toLowerCase();
485
  if (!searchTerm) {
486
  if (currentCategory) showCategory(currentCategory);
 
501
  if (results.length === 0) {
502
  content.innerHTML = '<div class="empty-state"><h3>No results found</h3><p>Try searching with different keywords</p></div>';
503
  } else {
504
+ // Skeleton göster
505
+ content.innerHTML = '<div class="category-header"><span class="icon">🔍</span><h2>Search Results</h2></div><div class="playlist-grid">' +
506
+ results.map(() => createSkeletonCard()).join('') + '</div>';
507
+
508
+ // Thumbnail'leri yükle
509
+ const searchCards = await Promise.all(results.map(async (result) => {
510
+ const thumbnail = await getPlaylistThumbnail(result.url);
511
+
512
+ return '<a href="' + result.url + '" target="_blank" class="playlist-card"><div class="playlist-thumbnail">' +
513
+ (thumbnail
514
+ ? '<img src="' + thumbnail + '" alt="' + result.title + '" class="thumbnail-image">'
515
+ : '<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>') +
516
+ '<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>';
517
+ }));
518
+
519
  content.innerHTML = '<div class="category-header"><span class="icon">🔍</span><h2>Search Results</h2></div><div class="playlist-grid">' +
520
+ searchCards.join('') + '</div>';
 
 
521
  }
522
  });
523
  });