mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-10-06 22:32:41 +07:00
fix: Video.js improve browser/device compatibility, fixes and a lot improvements
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
z-index: 100000;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
backdrop-filter: blur(4px);
|
||||
@@ -23,62 +23,104 @@
|
||||
}
|
||||
|
||||
.autoplay-countdown-content {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
border-radius: 12px;
|
||||
padding: 24px;
|
||||
max-width: 400px;
|
||||
background: linear-gradient(135deg, rgba(0, 0, 0, 0.95), rgba(20, 20, 20, 0.9));
|
||||
border-radius: 20px;
|
||||
padding: 50px;
|
||||
max-width: 480px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 20px 60px rgba(0, 0, 0, 0.4),
|
||||
0 8px 32px rgba(0, 0, 0, 0.2),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.autoplay-countdown-content::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
|
||||
}
|
||||
|
||||
.autoplay-countdown-content::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
left: -50%;
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
background: radial-gradient(circle, rgba(255, 0, 0, 0.05) 0%, transparent 70%);
|
||||
animation: backgroundPulse 4s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes backgroundPulse {
|
||||
0%, 100% {
|
||||
opacity: 0.3;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.autoplay-countdown-header {
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.autoplay-countdown-header h3 {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
font-size: 26px;
|
||||
font-weight: 400;
|
||||
margin: 0 0 10px 0;
|
||||
line-height: 1.3;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.autoplay-countdown-header h3 span{
|
||||
font-weight:700;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
color: #ff4444;
|
||||
font-weight: 700;
|
||||
font-size: 20px;
|
||||
animation: countdownPulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes countdownPulse {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.autoplay-countdown-video-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
text-align: left;
|
||||
gap: 20px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin:0 0 50px;
|
||||
}
|
||||
|
||||
.next-video-thumbnail {
|
||||
flex-shrink: 0;
|
||||
width: 80px;
|
||||
height: 45px;
|
||||
border-radius: 6px;
|
||||
width: 180px;
|
||||
height: 101px;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: #333;
|
||||
position: relative;
|
||||
box-shadow:
|
||||
0 12px 32px rgba(0, 0, 0, 0.4),
|
||||
0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
border: 2px solid rgba(255, 255, 255, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.next-video-thumbnail:hover {
|
||||
transform: translateY(-4px) scale(1.02);
|
||||
box-shadow:
|
||||
0 16px 40px rgba(0, 0, 0, 0.5),
|
||||
0 8px 24px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.next-video-thumbnail img {
|
||||
@@ -88,102 +130,250 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
.play-overlay {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 50%;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow:
|
||||
0 8px 24px rgba(0, 0, 0, 0.5),
|
||||
0 4px 16px rgba(0, 0, 0, 0.3);
|
||||
border: 3px solid rgba(255, 255, 255, 0.2);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.play-overlay:hover {
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
transform: translate(-50%, -50%) scale(1.1);
|
||||
box-shadow:
|
||||
0 12px 32px rgba(0, 0, 0, 0.6),
|
||||
0 6px 20px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.play-overlay svg {
|
||||
margin-left: 4px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.next-video-details {
|
||||
flex-grow: 1;
|
||||
min-width: 0; /* Allow text truncation */
|
||||
min-width: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.next-video-title {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 4px 0;
|
||||
line-height: 1.3;
|
||||
color: #999;
|
||||
font-size:18px;
|
||||
font-weight:500;
|
||||
margin: 0 0;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.next-video-author {
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
margin: 0 0 2px 0;
|
||||
line-height: 1.2;
|
||||
color: #bbb;
|
||||
font-size: 16px;
|
||||
margin: 0 0 8px 0;
|
||||
line-height: 1.3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.next-video-duration {
|
||||
color: #ccc;
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.autoplay-countdown-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
gap: 24px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.autoplay-play-button,
|
||||
.autoplay-cancel-button {
|
||||
button.autoplay-play-button,
|
||||
button.autoplay-cancel-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 10px 18px;
|
||||
gap: 8px;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
min-width: 100px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
min-width: 140px;
|
||||
height: 48px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.3px;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.autoplay-play-button {
|
||||
background: #ff0000;
|
||||
button.autoplay-play-button {
|
||||
background: linear-gradient(135deg, #ff0000, #e60000);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.autoplay-play-button::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
|
||||
transition: left 0.5s ease;
|
||||
}
|
||||
|
||||
.autoplay-play-button:hover {
|
||||
background: #e60000;
|
||||
transform: translateY(-1px);
|
||||
background: linear-gradient(135deg, #ff1a1a, #cc0000);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(255, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.autoplay-play-button:hover::before {
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.autoplay-play-button:active {
|
||||
transform: translateY(0);
|
||||
transform: translateY(-1px);
|
||||
box-shadow:
|
||||
0 4px 15px rgba(255, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.autoplay-cancel-button {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
background: linear-gradient(135deg, #404040, #2a2a2a);
|
||||
color: #fff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow:
|
||||
0 6px 20px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.autoplay-cancel-button:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
background: linear-gradient(135deg, #505050, #3a3a3a);
|
||||
transform: translateY(-2px);
|
||||
box-shadow:
|
||||
0 8px 25px rgba(0, 0, 0, 0.4),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.autoplay-cancel-button:active {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
transform: translateY(-1px);
|
||||
box-shadow:
|
||||
0 4px 15px rgba(0, 0, 0, 0.3),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
|
||||
.autoplay-play-button svg,
|
||||
.autoplay-cancel-button svg {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex-shrink: 0;
|
||||
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3));
|
||||
transition: transform 0.3s ease;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.autoplay-play-button:hover svg {
|
||||
transform: scale(1.05);
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
|
||||
}
|
||||
|
||||
.autoplay-cancel-button svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.autoplay-cancel-button:hover svg {
|
||||
transform: scale(1.05) rotate(90deg);
|
||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4));
|
||||
}
|
||||
|
||||
.autoplay-play-button span,
|
||||
.autoplay-cancel-button span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
letter-spacing: inherit;
|
||||
text-transform: inherit;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* Ensure perfect alignment of button content */
|
||||
.autoplay-play-button > *,
|
||||
.autoplay-cancel-button > * {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Fix any baseline alignment issues */
|
||||
.autoplay-play-button,
|
||||
.autoplay-cancel-button {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Ensure text and icons are perfectly aligned */
|
||||
.autoplay-play-button svg,
|
||||
.autoplay-cancel-button svg {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* Autoplay Toggle Button Styles */
|
||||
.vjs-autoplay-toggle {
|
||||
/* .vjs-autoplay-toggle {
|
||||
width: 3em !important;
|
||||
height: 3em !important;
|
||||
flex: none;
|
||||
@@ -191,16 +381,16 @@
|
||||
margin: 0 4px !important;
|
||||
line-height: 3em !important;
|
||||
position: relative;
|
||||
}
|
||||
} */
|
||||
|
||||
.vjs-autoplay-toggle .vjs-autoplay-icon {
|
||||
/* .vjs-autoplay-toggle .vjs-autoplay-icon {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: auto;
|
||||
}
|
||||
} */
|
||||
|
||||
.vjs-autoplay-toggle .vjs-autoplay-icon svg {
|
||||
width: 100%;
|
||||
@@ -209,40 +399,135 @@
|
||||
}
|
||||
|
||||
/* Responsive design */
|
||||
@media (max-width: 480px) {
|
||||
@media (max-width: 767px) {
|
||||
.autoplay-countdown-video-info {
|
||||
margin-bottom:20px;
|
||||
}
|
||||
.autoplay-countdown-content {
|
||||
padding: 20px;
|
||||
max-width: 320px;
|
||||
padding: 24px;
|
||||
max-width: 400px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-header h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-video-info {
|
||||
gap: 10px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.next-video-thumbnail {
|
||||
width: 60px;
|
||||
height: 34px;
|
||||
width: 140px;
|
||||
height: 78px;
|
||||
}
|
||||
|
||||
.play-overlay {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.play-overlay svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.next-video-title {
|
||||
font-size: 13px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-actions {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
.next-video-author {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.autoplay-play-button,
|
||||
.autoplay-cancel-button {
|
||||
width: 100%;
|
||||
padding: 12px 24px;
|
||||
font-size: 14px;
|
||||
min-width: 120px;
|
||||
height: 44px;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.autoplay-play-button svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.autoplay-cancel-button svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.autoplay-countdown-content {
|
||||
padding: 20px;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-header h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.countdown-timer {
|
||||
font-size: 24px;
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-video-info {
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.next-video-thumbnail {
|
||||
width: 120px;
|
||||
height: 68px;
|
||||
}
|
||||
|
||||
.play-overlay {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.play-overlay svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.next-video-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.next-video-author {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.autoplay-countdown-actions {
|
||||
gap: 5px;
|
||||
padding: 0;
|
||||
}
|
||||
button.autoplay-play-button, button.autoplay-cancel-button { padding:10px 20px; width:120px; height:40px; min-width:120px; }
|
||||
|
||||
.autoplay-play-button,
|
||||
.autoplay-cancel-button {
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
height: 46px;
|
||||
gap: 6px;
|
||||
padding: 10px 20px;
|
||||
font-size: 13px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.autoplay-play-button svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.autoplay-cancel-button svg {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,11 @@ class AutoplayCountdownOverlay extends Component {
|
||||
nextVideoThumbnail
|
||||
? `<div class="next-video-thumbnail">
|
||||
<img src="${nextVideoThumbnail}" alt="${nextVideoTitle}" />
|
||||
<div class="play-overlay">
|
||||
<svg viewBox="0 0 24 24" width="28" height="28" fill="currentColor">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
@@ -199,6 +204,11 @@ class AutoplayCountdownOverlay extends Component {
|
||||
nextVideoThumbnail
|
||||
? `<div class="next-video-thumbnail">
|
||||
<img src="${nextVideoThumbnail}" alt="${nextVideoTitle}" />
|
||||
<div class="play-overlay">
|
||||
<svg viewBox="0 0 24 24" width="28" height="28" fill="currentColor">
|
||||
<path d="M8 5v14l11-7z"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
|
||||
@@ -26,10 +26,9 @@ class EndScreenOverlay extends Component {
|
||||
// Get relatedVideos from options since createEl is called during super()
|
||||
const relatedVideos = this.options_ && this.options_._relatedVideos ? this.options_._relatedVideos : [];
|
||||
|
||||
// Limit items based on screen size
|
||||
const isMobile = window.innerWidth <= 768;
|
||||
const maxItems = isMobile ? 6 : 12;
|
||||
const videosToShow = relatedVideos.slice(0, maxItems);
|
||||
// Limit videos based on screen size to fit grid properly
|
||||
const maxVideos = this.getMaxVideosForScreen();
|
||||
const videosToShow = relatedVideos.slice(0, maxVideos);
|
||||
|
||||
// console.log(
|
||||
// 'Creating end screen with',
|
||||
@@ -41,6 +40,12 @@ class EndScreenOverlay extends Component {
|
||||
className: 'vjs-end-screen-overlay',
|
||||
});
|
||||
|
||||
// Create title
|
||||
const title = videojs.dom.createEl('div', {
|
||||
className: 'vjs-related-videos-title',
|
||||
});
|
||||
title.textContent = 'More Videos';
|
||||
|
||||
// Create grid container
|
||||
const grid = videojs.dom.createEl('div', {
|
||||
className: 'vjs-related-videos-grid',
|
||||
@@ -53,16 +58,15 @@ class EndScreenOverlay extends Component {
|
||||
grid.appendChild(videoItem);
|
||||
});
|
||||
} else {
|
||||
// Fallback message if no related videos
|
||||
const noVideos = videojs.dom.createEl('div', {
|
||||
className: 'vjs-no-related-videos',
|
||||
// Create sample videos for testing if no related videos provided
|
||||
const sampleVideos = this.createSampleVideos();
|
||||
sampleVideos.slice(0, this.getMaxVideosForScreen()).forEach((video) => {
|
||||
const videoItem = this.createVideoItem(video);
|
||||
grid.appendChild(videoItem);
|
||||
});
|
||||
noVideos.textContent = 'No related videos available';
|
||||
noVideos.style.color = 'white';
|
||||
noVideos.style.textAlign = 'center';
|
||||
grid.appendChild(noVideos);
|
||||
}
|
||||
|
||||
overlay.appendChild(title);
|
||||
overlay.appendChild(grid);
|
||||
|
||||
return overlay;
|
||||
@@ -73,10 +77,18 @@ class EndScreenOverlay extends Component {
|
||||
className: 'vjs-related-video-item',
|
||||
});
|
||||
|
||||
// Use real YouTube thumbnail or fallback to placeholder
|
||||
const thumbnailSrc = video.thumbnail || this.getPlaceholderImage(video.title);
|
||||
|
||||
const thumbnail = videojs.dom.createEl('img', {
|
||||
className: 'vjs-related-video-thumbnail',
|
||||
src: video.thumbnail,
|
||||
src: thumbnailSrc,
|
||||
alt: video.title,
|
||||
loading: 'lazy', // Lazy load for better performance
|
||||
onerror: () => {
|
||||
// Fallback to placeholder if image fails to load
|
||||
thumbnail.src = this.getPlaceholderImage(video.title);
|
||||
}
|
||||
});
|
||||
|
||||
const overlay = videojs.dom.createEl('div', {
|
||||
@@ -142,6 +154,166 @@ class EndScreenOverlay extends Component {
|
||||
return item;
|
||||
}
|
||||
|
||||
getPlaceholderImage(title) {
|
||||
// Generate a placeholder image using a service or create a data URL
|
||||
// For now, we'll use a simple colored placeholder based on the title
|
||||
const colors = [
|
||||
'#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7',
|
||||
'#DDA0DD', '#98D8C8', '#F7DC6F', '#BB8FCE', '#85C1E9'
|
||||
];
|
||||
|
||||
// Use title hash to consistently assign colors
|
||||
let hash = 0;
|
||||
for (let i = 0; i < title.length; i++) {
|
||||
hash = title.charCodeAt(i) + ((hash << 5) - hash);
|
||||
}
|
||||
const colorIndex = Math.abs(hash) % colors.length;
|
||||
const color = colors[colorIndex];
|
||||
|
||||
// Create a simple placeholder with the first letter of the title
|
||||
const firstLetter = title.charAt(0).toUpperCase();
|
||||
|
||||
// Create a data URL for a simple placeholder image
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 320;
|
||||
canvas.height = 180;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// Background
|
||||
ctx.fillStyle = color;
|
||||
ctx.fillRect(0, 0, 320, 180);
|
||||
|
||||
// Add a subtle pattern
|
||||
ctx.fillStyle = 'rgba(255, 255, 255, 0.1)';
|
||||
for (let i = 0; i < 20; i++) {
|
||||
ctx.fillRect(Math.random() * 320, Math.random() * 180, 2, 2);
|
||||
}
|
||||
|
||||
// Add the first letter
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.font = 'bold 48px Arial';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText(firstLetter, 160, 90);
|
||||
|
||||
return canvas.toDataURL();
|
||||
}
|
||||
|
||||
getMaxVideosForScreen() {
|
||||
const width = window.innerWidth;
|
||||
|
||||
if (width >= 1200) {
|
||||
return 12; // 4x3 grid for large desktop
|
||||
} else if (width >= 1024) {
|
||||
return 9; // 3x3 grid for desktop
|
||||
} else if (width >= 768) {
|
||||
return 6; // 3x2 grid for tablet
|
||||
} else {
|
||||
return 4; // 2x2 grid for mobile
|
||||
}
|
||||
}
|
||||
|
||||
createSampleVideos() {
|
||||
return [
|
||||
{
|
||||
id: 'sample1',
|
||||
title: 'React Full Course for Beginners',
|
||||
author: 'Bro Code',
|
||||
views: '2.1M views',
|
||||
duration: 1800,
|
||||
thumbnail: 'https://img.youtube.com/vi/dGcsHMXbSOA/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample2',
|
||||
title: 'JavaScript ES6+ Features',
|
||||
author: 'Tech Tutorials',
|
||||
views: '850K views',
|
||||
duration: 1200,
|
||||
thumbnail: 'https://img.youtube.com/vi/WZQc7RUAg18/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample3',
|
||||
title: 'CSS Grid Layout Masterclass',
|
||||
author: 'Web Dev Academy',
|
||||
views: '1.2M views',
|
||||
duration: 2400,
|
||||
thumbnail: 'https://img.youtube.com/vi/0xMQfnTU6oo/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample4',
|
||||
title: 'Node.js Backend Development',
|
||||
author: 'Code Master',
|
||||
views: '650K views',
|
||||
duration: 3600,
|
||||
thumbnail: 'https://img.youtube.com/vi/fBNz6F-Cowg/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample5',
|
||||
title: 'Vue.js Complete Guide',
|
||||
author: 'Frontend Pro',
|
||||
views: '980K views',
|
||||
duration: 2800,
|
||||
thumbnail: 'https://img.youtube.com/vi/qZXt1Aom3Cs/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample6',
|
||||
title: 'Python Data Science',
|
||||
author: 'Data Academy',
|
||||
views: '1.5M views',
|
||||
duration: 4200,
|
||||
thumbnail: 'https://img.youtube.com/vi/ua-CiDNNj30/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample7',
|
||||
title: 'TypeScript Fundamentals',
|
||||
author: 'TypeScript Expert',
|
||||
views: '720K views',
|
||||
duration: 2100,
|
||||
thumbnail: 'https://img.youtube.com/vi/BwuLxPH8IDs/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample8',
|
||||
title: 'MongoDB Database Tutorial',
|
||||
author: 'Database Pro',
|
||||
views: '890K views',
|
||||
duration: 1800,
|
||||
thumbnail: 'https://img.youtube.com/vi/-56x56UppqQ/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample9',
|
||||
title: 'Docker Containerization',
|
||||
author: 'DevOps Master',
|
||||
views: '1.1M views',
|
||||
duration: 3200,
|
||||
thumbnail: 'https://img.youtube.com/vi/pTFZFxd4hOI/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample10',
|
||||
title: 'AWS Cloud Services',
|
||||
author: 'Cloud Expert',
|
||||
views: '1.3M views',
|
||||
duration: 4500,
|
||||
thumbnail: 'https://img.youtube.com/vi/ITcXLS3h2qU/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample11',
|
||||
title: 'GraphQL API Design',
|
||||
author: 'API Specialist',
|
||||
views: '680K views',
|
||||
duration: 2600,
|
||||
thumbnail: 'https://img.youtube.com/vi/ed8SzALpx1Q/maxresdefault.jpg'
|
||||
},
|
||||
{
|
||||
id: 'sample12',
|
||||
title: 'Machine Learning Basics',
|
||||
author: 'AI Academy',
|
||||
views: '2.3M views',
|
||||
duration: 5400,
|
||||
thumbnail: 'https://img.youtube.com/vi/i_LwzRVP7bg/maxresdefault.jpg'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
show() {
|
||||
this.el().style.display = 'flex';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user