I’m working to live streaming using AWS server m3u8 format video. I have added an HTML5 video player using plyr.js and hls.js. I want to add a live tag in the control bar if the video is live now. if a random video is placed in the source then the live tag should not come. Also, how can I stop progress bar buffering?
<script src="https://cdn.jsdelivr.net/npm/hls.js"></script>
<script src="https://unpkg.com/plyr@3"></script>
<script>
document.addEventListener("DOMContentLoaded", () => {
const video = document.querySelector("video");
// const unmuteButton = document.getElementById("unmuteButton");
const unmuteButton = document.querySelector("button[data-plyr='mute']");
// menu.querySelectorAll('button[data-plyr="quality"]')
const liveIndicator = document.getElementById("liveIndicator");
const source = video.getElementsByTagName("source")[0].src;
const defaultQuality = 720;
console.log(video)
console.log(unmuteButton)
// console.log(video.muted)
unmuteButton.addEventListener("click", () => {
video.muted = false;
video.volume = 1.0;
video.autoplay = true;
unmuteButton.style.display = "none";
document.getElementById("mutebutton").style.display = 'none';
});
const defaultOptions = {
controls: [
'play-large', 'play', 'progress', 'current-time', 'duration',
'mute', 'volume', 'settings', 'airplay', 'fullscreen'
],
settings: ['quality'],
autoplay: true,
muted: true,
volume : 0,
ratio: '16:9',
seek: 0,
};
function getCustomLabel(qualityValue) {
const qualityLabels = {
1080: 'Full HD (1080p)',
720: 'HD (720p)',
480: 'SD (480p)',
360: 'Low (360p)',
240: 'Very Low (240p)'
};
return qualityLabels[qualityValue] || '';
}
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(source);
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
const availableQualities = hls.levels.map((l) => l.height);
defaultOptions.quality = {
default: availableQualities[0],
options: availableQualities,
forced: true,
onChange: (e) => updateQuality(e),
};
const player = new Plyr(video, defaultOptions);
// Wait for Plyr instance creation
player.on('ready', (event) => {
const plyr = event.detail.plyr; video.muted = true;
video.volume = 0;
unmuteButton.style.display = "block";
console.log(video.volume)
console.log(video.muted)
handleLiveStream(plyr);
// Update quality labels when a quality option is clicked
const qualityMenus = document.querySelectorAll('.plyr__menu__container');
qualityMenus.forEach((menu) => {
if (menu.querySelector('.plyr__menu__value')) {
const qualityButtons = menu.querySelectorAll('button[data-plyr="settings"]');
qualityButtons.forEach((button) => {
button.addEventListener('click', () => {
updateQualityLabels(player);
});
});
}
});
});
player.on('enterfullscreen', () => {
handleFullscreenChange();
});
player.on('exitfullscreen', () => {
handleFullscreenChange();
});
});
hls.attachMedia(video);
window.hls = hls;
} else {
const player = new Plyr(video, defaultOptions);
player.on('enterfullscreen', () => {
handleFullscreenChange();
});
player.on('exitfullscreen', () => {
handleFullscreenChange();
});
}
function handleFullscreenChange() {
if (video.paused) {
return;
}
if (document.fullscreenElement) {
// Fullscreen entered
rotateScreen();
} else {
// Fullscreen exited
rotateScreen();
}
}
function rotateScreen() {
// Simulate orientation changes by forcing reflows
// Get the viewport width and height
const viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
const viewportHeight = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
// Determine the orientation and update styles accordingly
const isLandscape = viewportWidth > viewportHeight;
if (isLandscape) {
// Landscape orientation
document.documentElement.style.transform = 'rotate(0deg)';
document.documentElement.style.width = '100%'; // Set width to viewport height
document.documentElement.style.height = '100%'; // Set height to viewport width
} else {
// Portrait orientation
document.documentElement.style.transform = 'rotate(0deg)';
document.documentElement.style.width = '100%';
document.documentElement.style.height = '100%';
document.documentElement.style.margin = '0'; // Reset margins
}
// Trigger reflow
setTimeout(() => {
window.dispatchEvent(new Event('resize'));
}, 10);
}
function updateQuality(newQuality) {
window.hls.levels.forEach((level, levelIndex) => {
if (level.height === newQuality) {
window.hls.currentLevel = levelIndex;
}
});
}
function updateQualityLabels(player) {
const qualityMenus = document.querySelectorAll('.plyr__menu__container');
qualityMenus.forEach((menu) => {
if (menu.querySelector('.plyr__menu__value')) {
const qualityButtons = menu.querySelectorAll('button[data-plyr="quality"]');
qualityButtons.forEach((button) => {
const qualityValue = parseInt(button.value, 10);
const customLabel = getCustomLabel(qualityValue);
if (customLabel) {
button.querySelector('span').textContent = customLabel;
}
});
}
});
}
function handleLiveStream(player) {
// Customize live stream behavior
player.config.live = true;
player.config.seekTime = 0; // Disable seeking
player.duration = 0; // Set duration to 0 for live
// Show live indicator
liveIndicator.style.display = 'block';
// Hide total duration and set progress to indeterminate
const controls = document.querySelector('.plyr__controls');
const progress = controls.querySelector('.plyr__progress');
const durationElement = controls.querySelector('.plyr__time--duration');
if (durationElement) {
durationElement.style.display = 'none'; // Hide total duration
}
if (progress) {
const liveIndicatorContainer = document.createElement('div');
liveIndicatorContainer.className = 'live-indicator-container';
liveIndicatorContainer.appendChild(liveIndicator);
// Insert live indicator before the progress bar's parent container
if (controls.children.length > 1) {
controls.insertBefore(liveIndicatorContainer, controls.children[1]);
} else {
controls.appendChild(liveIndicatorContainer);
}
}
}
// Check if the video is live
function isLiveStream() {
const urlParams = new URLSearchParams(source.split('?')[1]);
return urlParams.has('live');
}
// Display or hide the live button based on the stream type
if (isLiveStream()) {
liveIndicator.style.display = 'block';
} else {
liveIndicator.style.display = 'none';
}
});
</script>
<div id="player">
<video autoplay muted playsinline poster="<?php echo $vimeo_poster ?>">
<source type="application/x-mpegURL" src="https://3d26876b73d7.us-west-2.playback.live-video.net/api/video/v1/us-west-2.913157848533.channel.rkCBS9iD1eyd.m3u8">
</video>
</div>
<div id="mutebutton" style="<?php if(empty($data['webcast_embed_code'])){ echo 'display:none;'; } ?>">
<button type="button" id="unmuteButton" class="custom-button plyr__control--pressed" aria-label="Unmute" data-plyr="mute">Click here to Unmute</button>
</div>
<div id="liveIndicator" class="live-indicator" style="display: none;">
LIVE
</div>
<style type="text/css">
/* Hide the buffer bar */
.plyr__progress__buffer {
display: none !important;
}
/* Ensure the progress bar takes up the full width */
.plyr__controls {
max-width: 100% !important;
}
/* Style the played progress bar */
.plyr__progress--played {
background: red; /* Change the color as needed */
height: 100%; /* Ensure it covers the full height of the progress bar */
}
/* Style the live indicator */
.live-indicator {
background: red;
color: white;
padding: 0px 6px;
/* font-weight: bold; */
border-radius: 3px;
margin-right: 10px;
font-size: 13px;
}
</style>