
El paquete de soporte y formación y el servicio continuo que nos ofrecieron funcionaron muy bien en la primera línea que compramos, así que fue una extensión obvia elegir a Europlacer para seguir adelante porque su oferta era tan diferente a la de la competencia.
.youtube-playlist-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
margin: 20px 0;
}
.youtube-video-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
overflow: hidden;
transition: all 0.3s ease;
cursor: pointer;
}
.youtube-video-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0,0,0,0.15);
}
.youtube-thumbnail-container {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 aspect ratio */
overflow: hidden;
}
.youtube-thumbnail {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.youtube-play-overlay {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(255,0,0,0.9);
border-radius: 50%;
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 14px;
opacity: 0;
transition: opacity 0.3s ease;
}
.youtube-video-card:hover .youtube-play-overlay {
opacity: 1;
}
.youtube-video-info {
padding: 16px;
}
.youtube-video-title {
font-size: 15px;
font-weight: 400;
margin: 0 0 8px 0;
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
color: #333;
}
.youtube-video-date {
font-size: 11px;
color: #666;
margin: 0;
}
.youtube-loading {
text-align: center;
padding: 40px;
font-size: 18px;
color: #666;
}
.youtube-error {
text-align: center;
padding: 40px;
color: #d32f2f;
background: #ffebee;
border-radius: 8px;
margin: 20px 0;
}
const YOUTUBE_API_KEY = 'AIzaSyBqDDWnweT4aX2OazwRnKKL4rT3HF4Tn74';
const PLAYLIST_ID = 'PL10f3SNoljkra3TPCSYRk24pHTqlJytpO';
async function loadYouTubePlaylist() {
const container = document.getElementById('youtube-playlist-container');
try {
// Fetch playlist items
const response = await fetch(
`https://www.googleapis.com/youtube/v3/playlistItems?` +
`part=snippet&maxResults=100&playlistId=${PLAYLIST_ID}&` +
`order=date&key=${YOUTUBE_API_KEY}`
);
if (!response.ok) {
throw new Error(`YouTube API error: ${response.status}`);
}
const data = await response.json();
if (!data.items || data.items.length === 0) {
container.innerHTML = '
Loading YouTube videos...
No videos found in playlist.
';
return;
}
// Filter out deleted/private videos
const validVideos = data.items.filter(video => {
// Check if video is accessible
const videoId = video.snippet.resourceId?.videoId;
const title = video.snippet.title;
// Filter out deleted/private videos
// They typically have titles like "Deleted video" or "Private video"
// or missing essential data
return videoId &&
title &&
title !== 'Deleted video' &&
title !== 'Private video' &&
!title.startsWith('[Deleted video]') &&
!title.startsWith('[Private video]');
});
// Check if any videos remain after filtering
if (validVideos.length === 0) {
container.innerHTML = 'No available videos found in playlist.
';
return;
}
// Sort by published date (most recent first)
const sortedVideos = validVideos.sort((a, b) =>
new Date(b.snippet.publishedAt) - new Date(a.snippet.publishedAt)
);
// Create grid HTML
const gridHTML = `
${sortedVideos.map(video => {
const videoId = video.snippet.resourceId.videoId;
const title = video.snippet.title;
// Use different thumbnail sources - try multiple options
const thumbnail = video.snippet.thumbnails.maxres?.url ||
video.snippet.thumbnails.standard?.url ||
video.snippet.thumbnails.high?.url ||
video.snippet.thumbnails.medium?.url ||
video.snippet.thumbnails.default?.url ||
`https://img.youtube.com/vi/${videoId}/mqdefault.jpg`;
const publishedDate = new Date(video.snippet.publishedAt)
.toLocaleDateString();
return `
`;
}).join('')}
`;
container.innerHTML = gridHTML;
} catch (error) {
console.error('Error loading YouTube playlist:', error);
container.innerHTML = `
${title}
Error loading videos: ${error.message}
Check your API key and playlist ID
`;
}
}
function openYouTubeVideo(videoId) {
// Open video in new tab
window.open(`https://www.youtube.com/watch?v=${videoId}`, '_blank');
// Alternative: Embed video in modal (uncomment if preferred)
// showVideoModal(videoId);
}
// Optional: Modal functionality for embedded player
function showVideoModal(videoId) {
const modal = document.createElement('div');
modal.style.cssText = `
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.8); z-index: 10000; display: flex;
align-items: center; justify-content: center; cursor: pointer;
`;
const iframe = document.createElement('iframe');
iframe.style.cssText = `
width: 90%; max-width: 800px; height: 450px; border: none;
border-radius: 8px; cursor: default;
`;
iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1`;
iframe.onclick = (e) => e.stopPropagation();
modal.appendChild(iframe);
modal.onclick = () => modal.remove();
document.body.appendChild(modal);
}
// Load the playlist when page loads
document.addEventListener('DOMContentLoaded', loadYouTubePlaylist);
Check your API key and playlist ID



