mirax-player
Advanced tools
Comparing version 3.1.0 to 3.2.0
const embedTiktok = (playerRef) => { | ||
const videoUrl = playerRef.getAttribute("data-mirax-embed"); | ||
// Fetch oEmbed data from TikTok's API | ||
fetch(`https://www.tiktok.com/oembed?url=${encodeURIComponent(videoUrl)}`) | ||
.then((response) => response.json()) | ||
.then((data) => { | ||
if (data.html) { | ||
data.html = data.html.replace(/<script[^>]*>.*<\/script>/gi,""); | ||
} | ||
playerRef.innerHTML =data.html; | ||
const miraxBinderTikTok = document.createElement("script"); | ||
miraxBinderTikTok.src = "https://www.tiktok.com/embed.js"; | ||
document.body.appendChild(miraxBinderTikTok); | ||
}) | ||
.catch((error) => { | ||
console.error(error); | ||
}); | ||
}; | ||
// Function to extract YouTube video ID from a URL | ||
@@ -11,3 +31,3 @@ const extractYouTubeVideoId = (url) => { | ||
// Check if it's a regular YouTube video URL | ||
// Check if it's a regular YouTube video URL | ||
@@ -139,2 +159,23 @@ const videoIdMatch = url.match(/(\?v=|\/embed\/|\/watch\?v=|\/v\/|\/e\/|youtu.be\/)([^#&?]*).*/); | ||
// Function to extract Vimeo video ID from a URL | ||
@@ -222,3 +263,5 @@ const extractVimeoVideoId = (url) => { | ||
// Function to embed either YouTube or Vimeo video based on the URL | ||
// Function to embed either YouTube or Vimeo or TikTok video based on the URL | ||
const miraxEmbed = (playerRef, params) => { | ||
@@ -231,2 +274,5 @@ const videoUrl = playerRef.getAttribute("data-mirax-embed"); | ||
embedYouTube(playerRef, params); | ||
} | ||
else if (videoUrl.includes("tiktok.com") || videoUrl.includes("tiktok")) { | ||
embedTiktok(playerRef, params); | ||
} else { | ||
@@ -237,3 +283,2 @@ throw new Error("Invalid video URL"); | ||
export default miraxEmbed; |
@@ -5,507 +5,508 @@ import './miraxplayerUI.js'; | ||
// Check if the control elements have already been created | ||
const existingControls = videoClip.parentNode.querySelector('.mirax-theme'); | ||
if (existingControls) { | ||
return; | ||
} | ||
// Create control elements | ||
const controlDiv = document.createElement("div"); | ||
controlDiv.className = "mirax-theme"; | ||
controlDiv.style.backgroundColor = playerTheme; | ||
// Append the control div to the videoClip element's parent node | ||
videoClip.parentNode.appendChild(controlDiv); | ||
const existingControls = videoClip.parentNode.querySelector('.mirax-theme'); | ||
if (existingControls) { | ||
return; | ||
} | ||
// Error Handler if video file not exist or not found | ||
videoClip.addEventListener("error", function (e) { | ||
// Check the networkState | ||
if (this.networkState > 2) { | ||
// Create a text element | ||
const videoText = document.createElement("p"); | ||
// Set the text content | ||
videoText.textContent = "Video not found"; | ||
// Set the class name | ||
videoText.className = "video-text"; | ||
// Append the text element to the video element's parent node | ||
this.parentNode.appendChild(videoText); | ||
} | ||
}); | ||
// Create control elements | ||
const controlDiv = document.createElement("div"); | ||
controlDiv.className = "mirax-theme"; | ||
controlDiv.style.backgroundColor = playerTheme; | ||
// Append the control div to the videoClip element's parent node | ||
videoClip.parentNode.appendChild(controlDiv); | ||
// Error Handler if video file not exist or not found | ||
videoClip.addEventListener("error", function (e) { | ||
// Check the networkState | ||
if (this.networkState > 2) { | ||
// Create a text element | ||
const videoText = document.createElement("p"); | ||
// Set the text content | ||
videoText.textContent = "Video not found"; | ||
// Set the class name | ||
videoText.className = "video-text"; | ||
// Append the text element to the video element's parent node | ||
this.parentNode.appendChild(videoText); | ||
} | ||
}); | ||
//**********************************************// | ||
// | ||
// PIP ( Picture in Picture ) | ||
// | ||
//*********************************************// | ||
const pipButton = document.createElement('mirax'); | ||
pipButton.className = 'pip-button'; | ||
controlDiv.appendChild(pipButton); | ||
pipButton.addEventListener('click', () => { | ||
if (document.pictureInPictureElement) { | ||
// Exit PiP | ||
document.exitPictureInPicture(); | ||
} else if (videoClip !== document.pictureInPictureElement) { | ||
// Request PiP | ||
videoClip.requestPictureInPicture(); | ||
} | ||
}); | ||
videoClip.addEventListener('enterpictureinpicture', handleEnterPiP); | ||
videoClip.addEventListener('leavepictureinpicture', handleLeavePiP); | ||
function handleEnterPiP(event) { | ||
// Update UI or perform actions when entering PiP | ||
console.log('Entered PiP mode'); | ||
//**********************************************// | ||
// | ||
// PIP ( Picture in Picture ) | ||
// | ||
//*********************************************// | ||
const pipButton = document.createElement('mirax'); | ||
pipButton.className = 'pip-button'; | ||
controlDiv.appendChild(pipButton); | ||
pipButton.addEventListener('click', () => { | ||
if (document.pictureInPictureElement) { | ||
// Exit PiP | ||
document.exitPictureInPicture(); | ||
} else if (videoClip !== document.pictureInPictureElement) { | ||
// Request PiP | ||
videoClip.requestPictureInPicture(); | ||
} | ||
function handleLeavePiP(event) { | ||
// Update UI or perform actions when leaving PiP | ||
console.log('Exited PiP mode'); | ||
} | ||
}); | ||
videoClip.addEventListener('enterpictureinpicture', handleEnterPiP); | ||
videoClip.addEventListener('leavepictureinpicture', handleLeavePiP); | ||
document.addEventListener('keydown', (event) => { | ||
// Check if Alt+P is pressed | ||
if (event.altKey && event.code === 'KeyP') { | ||
// Toggle PiP mode | ||
if (document.pictureInPictureElement) { | ||
document.exitPictureInPicture(); | ||
} else { | ||
videoClip.requestPictureInPicture(); | ||
} | ||
} | ||
}); | ||
function handleEnterPiP(event) { | ||
// Update UI or perform actions when entering PiP | ||
console.log('Entered PiP mode'); | ||
} | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Play Button | ||
// | ||
//*********************************************// | ||
// Define event listener and function for the play button | ||
const playButton = document.createElement('mirax'); | ||
playButton.className = 'play-button'; | ||
playButton.addEventListener('click', playerButton); | ||
controlDiv.appendChild(playButton); | ||
function playerButton() { | ||
if (videoClip.paused) { | ||
videoClip.play(); | ||
playButton.classList.add("pause"); // Add the pause class name | ||
function handleLeavePiP(event) { | ||
// Update UI or perform actions when leaving PiP | ||
console.log('Exited PiP mode'); | ||
} | ||
document.addEventListener('keydown', (event) => { | ||
// Check if Alt+P is pressed | ||
if (event.altKey && event.code === 'KeyP') { | ||
// Toggle PiP mode | ||
if (document.pictureInPictureElement) { | ||
document.exitPictureInPicture(); | ||
} else { | ||
videoClip.pause(); | ||
playButton.classList.remove("pause"); // Remove the pause class name | ||
videoClip.requestPictureInPicture(); | ||
} | ||
} | ||
// Add event listener to the video element itself to toggle play state | ||
videoClip.addEventListener('click', () => { | ||
if (videoClip.paused) { | ||
videoClip.play(); | ||
playButton.classList.add("pause"); | ||
} else { | ||
videoClip.pause(); | ||
playButton.classList.remove("pause"); | ||
} | ||
}); | ||
// Update the styles or UI of the play button based on video state | ||
function updatePlayButton() { | ||
if (videoClip.paused) { | ||
playButton.classList.remove("pause"); | ||
} else { | ||
playButton.classList.add("pause"); | ||
} | ||
} | ||
// Listen to video play and pause events | ||
videoClip.addEventListener('play', updatePlayButton); | ||
videoClip.addEventListener('pause', updatePlayButton); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Play video - press space bar | ||
// | ||
//*********************************************// | ||
// Add keydown event listener to the document | ||
document.addEventListener('keydown', function(event) { | ||
// Check if the pressed key is the space bar | ||
if (event.code === 'Space') { | ||
// Prevent the default action of scrolling | ||
event.preventDefault(); | ||
// Call the same function that you use for the play button | ||
playerButton(); | ||
} | ||
}); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Volume slider | ||
// | ||
//*********************************************// | ||
let prevVolume = 1; | ||
// Create the volume input element | ||
const volumeInput = document.createElement('input'); | ||
volumeInput.type = 'range'; | ||
volumeInput.className = 'volume-slider'; | ||
volumeInput.min = '0'; | ||
volumeInput.max = '1'; | ||
volumeInput.step = '0.01'; | ||
volumeInput.defaultValue = '1'; | ||
// Add event listener to update volume | ||
volumeInput.addEventListener('input', function() { | ||
videoClip.volume = parseFloat(this.value); | ||
}); | ||
controlDiv.appendChild(volumeInput); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Speaker UI icon | ||
// | ||
//*********************************************// | ||
// Create a div element for the speaker icon container | ||
const speakerIconContainer = document.createElement('div'); | ||
speakerIconContainer.className = 'speaker-icon'; | ||
speakerIconContainer.style.width = '20px'; | ||
speakerIconContainer.style.height = '50px'; | ||
speakerIconContainer.style.position = 'absolute'; | ||
speakerIconContainer.style.left = '33px'; // Set the left property to 0 | ||
speakerIconContainer.style.right = 'auto'; // Set the right property to auto | ||
speakerIconContainer.style.cursor = 'pointer'; | ||
// Create a div element for the speaker box | ||
const speakerBox = document.createElement('div'); | ||
speakerBox.className = 'box'; | ||
speakerBox.style.width = '6px'; | ||
speakerBox.style.height = '6px'; | ||
speakerBox.style.backgroundColor = 'white'; | ||
speakerBox.style.borderRadius = '2px'; | ||
speakerBox.style.position = 'absolute'; | ||
speakerBox.style.left = '2px'; | ||
speakerBox.style.top = '22px'; | ||
// Create a div element for the speaker cone | ||
const speakerCone = document.createElement('div'); | ||
speakerCone.className = 'cone'; | ||
speakerCone.style.width = '0'; | ||
speakerCone.style.height = '0'; | ||
speakerCone.style.borderTop = '6px solid transparent'; | ||
speakerCone.style.borderBottom = '6px solid transparent'; | ||
speakerCone.style.borderRight = '13px solid white'; // Swap the border-left and border-right properties | ||
speakerCone.style.borderLeft = '0'; // Swap the border-left and border-right properties | ||
speakerCone.style.position = 'absolute'; | ||
speakerCone.style.top = '19px'; | ||
// Append the speaker box and cone to the speaker icon container | ||
speakerIconContainer.appendChild(speakerBox); | ||
speakerIconContainer.appendChild(speakerCone); | ||
// Append the speaker icon container to the controlDiv | ||
controlDiv.appendChild(speakerIconContainer); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Mute - x appear | ||
// | ||
//*********************************************// | ||
// Create a span element for the x symbol | ||
const xSymbol = document.createElement('span'); | ||
xSymbol.className = 'x-symbol'; | ||
xSymbol.textContent = 'x'; | ||
xSymbol.style.fontSize = '12px'; | ||
xSymbol.style.color = 'white'; // Set the color to white once | ||
xSymbol.style.position = 'absolute'; | ||
xSymbol.style.left = '17px'; | ||
xSymbol.style.top = '18px'; | ||
xSymbol.style.fontFamily = 'Arial, Corbel'; | ||
xSymbol.style.display = 'none'; // Hide the x symbol by default | ||
// Append the x symbol to the speaker icon container | ||
speakerIconContainer.appendChild(xSymbol); | ||
// Add event listener to update volume and x symbol | ||
volumeInput.addEventListener('input', function() { | ||
// Use the video element directly | ||
videoClip.volume = parseFloat(this.value); | ||
// If the volume is zero, show the x symbol | ||
if (videoClip.volume === 0) { | ||
xSymbol.style.display = 'block'; | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Play Button | ||
// | ||
//*********************************************// | ||
// Define event listener and function for the play button | ||
const playButton = document.createElement('mirax'); | ||
playButton.className = 'play-button'; | ||
playButton.addEventListener('click', playerButton); | ||
controlDiv.appendChild(playButton); | ||
function playerButton() { | ||
if (videoClip.paused) { | ||
videoClip.play(); | ||
playButton.classList.add("pause"); // Add the pause class name | ||
} else { | ||
videoClip.pause(); | ||
playButton.classList.remove("pause"); // Remove the pause class name | ||
} | ||
// Otherwise, hide the x symbol | ||
else { | ||
xSymbol.style.display = 'none'; | ||
} | ||
// Add event listener to the video element itself to toggle play state | ||
videoClip.addEventListener('click', () => { | ||
if (videoClip.paused) { | ||
videoClip.play(); | ||
playButton.classList.add("pause"); | ||
} else { | ||
videoClip.pause(); | ||
playButton.classList.remove("pause"); | ||
} | ||
}); | ||
// Add event listener to the speaker icon container | ||
speakerIconContainer.addEventListener("click", function() { | ||
// Toggle the mute state of the video element | ||
videoClip.muted = !videoClip.muted; | ||
// Change the color of the speaker icon based on the mute state | ||
if (videoClip.muted) { | ||
// Set the color to gray | ||
speakerBox.style.backgroundColor = "gray"; | ||
speakerCone.style.borderRightColor = "gray"; | ||
xSymbol.style.display = 'block'; | ||
// Store the current volume value before setting it to zero | ||
prevVolume = volumeInput.value; | ||
volumeInput.value = '0'; | ||
videoClip.volume = 0; | ||
// Update the styles or UI of the play button based on video state | ||
function updatePlayButton() { | ||
if (videoClip.paused) { | ||
playButton.classList.remove("pause"); | ||
} else { | ||
// Set the color to white | ||
speakerBox.style.backgroundColor = "white"; | ||
speakerCone.style.borderRightColor = "white"; | ||
xSymbol.style.display = 'none'; | ||
// Restore the previous volume value after unmuting | ||
volumeInput.value = prevVolume; | ||
videoClip.volume = parseFloat(prevVolume); | ||
playButton.classList.add("pause"); | ||
} | ||
}); | ||
//**********************************************// | ||
// | ||
// Progress bar slider | ||
// | ||
//*********************************************// | ||
const progressBar = document.createElement('progress'); | ||
progressBar.className = 'progress-bar'; | ||
progressBar.min = '0'; | ||
progressBar.max = '100'; | ||
progressBar.value = '0'; | ||
controlDiv.appendChild(progressBar); | ||
videoClip.addEventListener('timeupdate', function() { | ||
const percentPlayed = (videoClip.currentTime / videoClip.duration) * 100; | ||
progressBar.value = percentPlayed; | ||
}); | ||
progressBar.addEventListener('mousedown', function(e) { | ||
const rect = progressBar.getBoundingClientRect(); | ||
const offsetX = e.clientX - rect.left; | ||
const newProgress = (offsetX / rect.width) * 100; | ||
videoClip.currentTime = (newProgress / 100) * videoClip.duration; | ||
const onMouseMove = function(e) { | ||
const offsetX = e.clientX - rect.left; | ||
const newProgress = (offsetX / rect.width) * 100; | ||
videoClip.currentTime = (newProgress / 100) * videoClip.duration; | ||
}; | ||
const onMouseUp = function() { | ||
document.removeEventListener('mousemove', onMouseMove); | ||
document.removeEventListener('mouseup', onMouseUp); | ||
}; | ||
document.addEventListener('mousemove', onMouseMove); | ||
document.addEventListener('mouseup', onMouseUp); | ||
}); | ||
// After creating the currentTimeDiv | ||
const currentTimeDiv = document.createElement('div'); | ||
currentTimeDiv.className = 'current-time'; | ||
controlDiv.appendChild(currentTimeDiv); | ||
// Listen to the timeupdate event to update the current time | ||
videoClip.addEventListener('timeupdate', updateCurrentTime); | ||
// Function to update the current time in the currentTimeDiv | ||
function updateCurrentTime() { | ||
const currentTime = formatTime(videoClip.currentTime); | ||
currentTimeDiv.textContent = currentTime; | ||
} | ||
// Function to format time in mm:ss format | ||
function formatTime(timeInSeconds) { | ||
const minutes = Math.floor(timeInSeconds / 60); | ||
const seconds = Math.floor(timeInSeconds % 60); | ||
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; | ||
// Listen to video play and pause events | ||
videoClip.addEventListener('play', updatePlayButton); | ||
videoClip.addEventListener('pause', updatePlayButton); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Play video - press space bar | ||
// | ||
//*********************************************// | ||
// Add keydown event listener to the document | ||
document.addEventListener('keydown', function(event) { | ||
// Check if the pressed key is the space bar | ||
if (event.code === 'Space') { | ||
// Prevent the default action of scrolling | ||
event.preventDefault(); | ||
// Call the same function that you use for the play button | ||
playerButton(); | ||
} | ||
// Function to format time in HH:MM:SS | ||
function formatTime(seconds) { | ||
const hours = Math.floor(seconds / 3600); | ||
const minutes = Math.floor((seconds % 3600) / 60); | ||
const secs = Math.floor(seconds % 60); | ||
}); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Volume slider | ||
// | ||
//*********************************************// | ||
let prevVolume = 1; | ||
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; | ||
// Create the volume input element | ||
const volumeInput = document.createElement('input'); | ||
volumeInput.type = 'range'; | ||
volumeInput.className = 'volume-slider'; | ||
volumeInput.min = '0'; | ||
volumeInput.max = '1'; | ||
volumeInput.step = '0.01'; | ||
volumeInput.defaultValue = '1'; | ||
// Add event listener to update volume | ||
volumeInput.addEventListener('input', function() { | ||
videoClip.volume = parseFloat(this.value); | ||
}); | ||
controlDiv.appendChild(volumeInput); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Speaker UI icon | ||
// | ||
//*********************************************// | ||
// Create a div element for the speaker icon container | ||
const speakerIconContainer = document.createElement('div'); | ||
speakerIconContainer.className = 'speaker-icon'; | ||
speakerIconContainer.style.width = '20px'; | ||
speakerIconContainer.style.height = '50px'; | ||
speakerIconContainer.style.position = 'absolute'; | ||
speakerIconContainer.style.left = '33px'; // Set the left property to 0 | ||
speakerIconContainer.style.right = 'auto'; // Set the right property to auto | ||
speakerIconContainer.style.cursor = 'pointer'; | ||
// Create a div element for the speaker box | ||
const speakerBox = document.createElement('div'); | ||
speakerBox.className = 'box'; | ||
speakerBox.style.width = '6px'; | ||
speakerBox.style.height = '6px'; | ||
speakerBox.style.backgroundColor = 'white'; | ||
speakerBox.style.borderRadius = '2px'; | ||
speakerBox.style.position = 'absolute'; | ||
speakerBox.style.left = '2px'; | ||
speakerBox.style.top = '22px'; | ||
// Create a div element for the speaker cone | ||
const speakerCone = document.createElement('div'); | ||
speakerCone.className = 'cone'; | ||
speakerCone.style.width = '0'; | ||
speakerCone.style.height = '0'; | ||
speakerCone.style.borderTop = '6px solid transparent'; | ||
speakerCone.style.borderBottom = '6px solid transparent'; | ||
speakerCone.style.borderRight = '13px solid white'; // Swap the border-left and border-right properties | ||
speakerCone.style.borderLeft = '0'; // Swap the border-left and border-right properties | ||
speakerCone.style.position = 'absolute'; | ||
speakerCone.style.top = '19px'; | ||
// Append the speaker box and cone to the speaker icon container | ||
speakerIconContainer.appendChild(speakerBox); | ||
speakerIconContainer.appendChild(speakerCone); | ||
// Append the speaker icon container to the controlDiv | ||
controlDiv.appendChild(speakerIconContainer); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Mute - x appear | ||
// | ||
//*********************************************// | ||
// Create a span element for the x symbol | ||
const xSymbol = document.createElement('span'); | ||
xSymbol.className = 'x-symbol'; | ||
xSymbol.textContent = 'x'; | ||
xSymbol.style.fontSize = '12px'; | ||
xSymbol.style.color = 'white'; // Set the color to white once | ||
xSymbol.style.position = 'absolute'; | ||
xSymbol.style.left = '17px'; | ||
xSymbol.style.top = '18px'; | ||
xSymbol.style.fontFamily = 'Arial, Corbel'; | ||
xSymbol.style.display = 'none'; // Hide the x symbol by default | ||
// Append the x symbol to the speaker icon container | ||
speakerIconContainer.appendChild(xSymbol); | ||
// Add event listener to update volume and x symbol | ||
volumeInput.addEventListener('input', function() { | ||
// Use the video element directly | ||
videoClip.volume = parseFloat(this.value); | ||
// If the volume is zero, show the x symbol | ||
if (videoClip.volume === 0) { | ||
xSymbol.style.display = 'block'; | ||
} | ||
// Function to update current time | ||
function updateCurrentTime() { | ||
currentTimeDiv.textContent = formatTime(videoClip.currentTime); | ||
// Otherwise, hide the x symbol | ||
else { | ||
xSymbol.style.display = 'none'; | ||
} | ||
// Set initial content of currentTimeDiv | ||
currentTimeDiv.textContent = formatTime(0); | ||
// Listen to the timeupdate event to update the current time | ||
videoClip.addEventListener('timeupdate', updateCurrentTime); | ||
// Function to format time in HH:MM:SS format | ||
function formatTimex(seconds) { | ||
const date = new Date(null); | ||
date.setSeconds(seconds); | ||
return date.toISOString().substr(11, 8); | ||
}); | ||
// Add event listener to the speaker icon container | ||
speakerIconContainer.addEventListener("click", function() { | ||
// Toggle the mute state of the video element | ||
videoClip.muted = !videoClip.muted; | ||
// Change the color of the speaker icon based on the mute state | ||
if (videoClip.muted) { | ||
// Set the color to gray | ||
speakerBox.style.backgroundColor = "gray"; | ||
speakerCone.style.borderRightColor = "gray"; | ||
xSymbol.style.display = 'block'; | ||
// Store the current volume value before setting it to zero | ||
prevVolume = volumeInput.value; | ||
volumeInput.value = '0'; | ||
videoClip.volume = 0; | ||
} else { | ||
// Set the color to white | ||
speakerBox.style.backgroundColor = "white"; | ||
speakerCone.style.borderRightColor = "white"; | ||
xSymbol.style.display = 'none'; | ||
// Restore the previous volume value after unmuting | ||
volumeInput.value = prevVolume; | ||
videoClip.volume = parseFloat(prevVolume); | ||
} | ||
// Function to update time duration | ||
function updateDuration(videoClip, timeDurationDiv) { | ||
if (!isNaN(videoClip.duration)) { | ||
const formattedDuration = formatTimex(videoClip.duration); | ||
timeDurationDiv.textContent = formattedDuration; | ||
} | ||
}); | ||
//**********************************************// | ||
// | ||
// Progress bar slider | ||
// | ||
//*********************************************// | ||
const progressBar = document.createElement('progress'); | ||
progressBar.className = 'progress-bar'; | ||
progressBar.min = '0'; | ||
progressBar.max = '100'; | ||
progressBar.value = '0'; | ||
controlDiv.appendChild(progressBar); | ||
videoClip.addEventListener('timeupdate', function() { | ||
const percentPlayed = (videoClip.currentTime / videoClip.duration) * 100; | ||
progressBar.value = percentPlayed; | ||
}); | ||
progressBar.addEventListener('mousedown', function(e) { | ||
const rect = progressBar.getBoundingClientRect(); | ||
const offsetX = e.clientX - rect.left; | ||
const newProgress = (offsetX / rect.width) * 100; | ||
videoClip.currentTime = (newProgress / 100) * videoClip.duration; | ||
const onMouseMove = function(e) { | ||
const offsetX = e.clientX - rect.left; | ||
const newProgress = (offsetX / rect.width) * 100; | ||
videoClip.currentTime = (newProgress / 100) * videoClip.duration; | ||
}; | ||
const onMouseUp = function() { | ||
document.removeEventListener('mousemove', onMouseMove); | ||
document.removeEventListener('mouseup', onMouseUp); | ||
}; | ||
document.addEventListener('mousemove', onMouseMove); | ||
document.addEventListener('mouseup', onMouseUp); | ||
}); | ||
//**********************************************// | ||
// | ||
// Timestamp Math System | ||
// | ||
//*********************************************// | ||
const currentTimeDiv = document.createElement('div'); | ||
currentTimeDiv.className = 'current-time'; | ||
controlDiv.appendChild(currentTimeDiv); | ||
const timeDurationDiv = document.createElement('div'); | ||
timeDurationDiv.className = 'time-duration'; | ||
controlDiv.appendChild(timeDurationDiv); | ||
videoClip.addEventListener('timeupdate', updateCurrentTime); | ||
videoClip.addEventListener('loadedmetadata', () => updateDuration(videoClip, timeDurationDiv)); | ||
function updateCurrentTime() { | ||
currentTimeDiv.textContent = formatTime(videoClip.currentTime); | ||
} | ||
function formatTime(timeInSeconds) { | ||
const minutes = Math.floor(timeInSeconds / 60); | ||
const seconds = Math.floor(timeInSeconds % 60); | ||
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`; | ||
} | ||
function formatTimeHHMMSS(totalSeconds) { | ||
const minutes = Math.floor(totalSeconds / 60); | ||
const seconds = Math.floor(totalSeconds % 60); | ||
return `${minutes}:${seconds.toString().padStart(2, '0')}`; | ||
} | ||
// Usage: | ||
const totalDurationInSeconds = videoClip.duration; | ||
const formattedTotalDuration = formatTimeHHMMSS(totalDurationInSeconds); | ||
timeDurationDiv.textContent = formattedTotalDuration; | ||
function updateDuration(videoClip, timeDurationDiv) { | ||
if (!isNaN(videoClip.duration)) { | ||
const formattedDuration = formatTimeHHMMSS(videoClip.duration); | ||
timeDurationDiv.textContent = formattedDuration; | ||
} | ||
const timeDurationDiv = document.createElement('div'); | ||
timeDurationDiv.className = 'time-duration'; | ||
controlDiv.appendChild(timeDurationDiv); | ||
videoClip.addEventListener('loadedmetadata', () => updateDuration(videoClip, timeDurationDiv)); | ||
//**********************************************// | ||
// | ||
// Progress bar value color | ||
// | ||
//*********************************************// | ||
var color_progress_bar = progressTheme; | ||
const inputProgressBar = document.createElement('style'); | ||
document.head.appendChild(inputProgressBar); | ||
const inputProgressBarStyle = ` | ||
progress::-webkit-progress-bar { | ||
background-color: rgba(205, 228, 235, 0.1); | ||
} | ||
progress::-webkit-progress-value { | ||
background-color: ${color_progress_bar}; | ||
} | ||
progress[value]::-moz-progress-bar { | ||
} | ||
//**********************************************// | ||
// | ||
// Progress bar value color | ||
// | ||
//*********************************************// | ||
var color_progress_bar = progressTheme; | ||
const inputProgressBar = document.createElement('style'); | ||
document.head.appendChild(inputProgressBar); | ||
const inputProgressBarStyle = ` | ||
progress::-webkit-progress-bar { | ||
background-color: rgba(205, 228, 235, 0.1); | ||
} | ||
progress::-webkit-progress-value { | ||
background-color: ${color_progress_bar}; | ||
} | ||
progress::-ms-fill { | ||
background-color: ${color_progress_bar}; | ||
} | ||
`; | ||
inputProgressBar.appendChild(document.createTextNode(inputProgressBarStyle)); | ||
//**********************************************// | ||
// | ||
// Double Click Fullscreen | ||
// | ||
//*********************************************// | ||
videoClip.addEventListener('dblclick', toggleFullscreen); | ||
//______________________________________________________________________ | ||
//**********************************************// | ||
// | ||
// Fullscreen Button | ||
// | ||
//*********************************************// | ||
const fullscreenButton = document.createElement('mirax'); | ||
fullscreenButton.className = 'fullscreen'; | ||
controlDiv.appendChild(fullscreenButton); | ||
fullscreenButton.addEventListener('click', toggleFullscreen); | ||
function toggleFullscreen() { | ||
if (videoClip.requestFullscreen) { | ||
if (document.fullscreenElement) { | ||
document.exitFullscreen(); | ||
} else { | ||
videoClip.requestFullscreen(); | ||
} | ||
} | ||
} | ||
} | ||
progress[value]::-moz-progress-bar { | ||
background-color: ${color_progress_bar}; | ||
} | ||
progress::-ms-fill { | ||
background-color: ${color_progress_bar}; | ||
} | ||
`; | ||
inputProgressBar.appendChild(document.createTextNode(inputProgressBarStyle)); | ||
//**********************************************// | ||
// | ||
// Double Click Fullscreen | ||
// | ||
//*********************************************// | ||
/* # Mirax Player core license | ||
Mirax Player is released under the MIT license: | ||
videoClip.addEventListener('dblclick', toggleFullscreen); | ||
MIT License | ||
Copyright (c) [2023-present] [Demjhon Silver] | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
//______________________________________________________________________ | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. */ | ||
//**********************************************// | ||
// | ||
// Fullscreen Button | ||
// | ||
//*********************************************// | ||
const fullscreenButton = document.createElement('mirax'); | ||
fullscreenButton.className = 'fullscreen'; | ||
controlDiv.appendChild(fullscreenButton); | ||
fullscreenButton.addEventListener('click', toggleFullscreen); | ||
function toggleFullscreen() { | ||
if (videoClip.requestFullscreen) { | ||
if (document.fullscreenElement) { | ||
document.exitFullscreen(); | ||
} else { | ||
videoClip.requestFullscreen(); | ||
} | ||
} | ||
} | ||
} | ||
/* # Mirax Player core license | ||
Mirax Player is released under the MIT license: | ||
MIT License | ||
Copyright (c) [2023-present] [Demjhon Silver] | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. */ | ||
export default miraxplayer; |
@@ -19,7 +19,8 @@ | ||
.mirax-player { | ||
max-width: 740px; | ||
min-width:300px !important; | ||
max-width: 800px; | ||
width: 100%; /* This ensures the video fills its container while respecting max-width */ | ||
height: auto; /* This maintains the video's aspect ratio */ | ||
min-height:100px; | ||
max-height:580px; | ||
max-height:450px; | ||
background-color: #000000; | ||
@@ -34,2 +35,3 @@ margin: 0 auto; | ||
display: none; | ||
transition: transform 0.3s; | ||
@@ -43,7 +45,7 @@ } | ||
margin: 0 auto; | ||
float: inherit; | ||
position: relative; | ||
width: 100%; | ||
height: 20px; | ||
max-width:720px; | ||
min-width:300px !important; | ||
max-width:740px; | ||
margin-top:-44px; | ||
@@ -224,3 +226,3 @@ bottom: 0; | ||
float: left; | ||
margin-left:118px; | ||
margin-left:140px; | ||
font-family: "Lucida Console", "Arial", monospace; | ||
@@ -238,3 +240,3 @@ margin-top: 2px; | ||
right: 0; | ||
margin-right:20px; | ||
margin-right:30px; | ||
font-family: "Lucida Console", "Arial", monospace; | ||
@@ -250,6 +252,6 @@ margin-top: 2px; | ||
width: 100%; | ||
max-width:410px; | ||
max-width:380px; | ||
height:10px; | ||
float: left; | ||
margin-left: 196px; | ||
margin-left: 210px; | ||
background-color: rgba(205, 228, 235, 0.1); | ||
@@ -277,3 +279,3 @@ border-style: none; | ||
margin-top: 4px; | ||
margin-right:81px; | ||
margin-right:110px; | ||
height: 20px; | ||
@@ -305,2 +307,3 @@ background: none; | ||
position: absolute; | ||
margin-right:10px; | ||
right:0; | ||
@@ -386,5 +389,4 @@ height: 20px; | ||
margin: 0 auto; | ||
float: inherit; | ||
position: relative; | ||
width: 100%; | ||
width:100%; | ||
height: 20px; | ||
@@ -440,3 +442,3 @@ max-width:96%; | ||
min-width:30px; | ||
width: 17%; | ||
width: 14%; | ||
background-color: rgba(205, 228, 235, 0.1); | ||
@@ -443,0 +445,0 @@ } |
{ | ||
"name": "mirax-player", | ||
"version": "3.1.0", | ||
"description": "Mirax Player is a video player and embedding videos for React, Vue, Angular, and Svelte. That can embed videos like Youtube/Shorts and Vimeo", | ||
"version": "3.2.0", | ||
"description": "Mirax Player is a powerful free video player for React, Vue, Angular, and Svelte that can embed videos from platforms like TikTok, YouTube/Shorts, and Vimeo. This library package enables you to set any URL once within a single embed code tag and dynamically embed videos from any video sites.", | ||
"main": "index.js", | ||
@@ -22,2 +22,3 @@ "types": "mirax-player.d.ts", | ||
"vimeo", | ||
"tiktok", | ||
"javascript", | ||
@@ -24,0 +25,0 @@ "typescript", |
@@ -11,3 +11,3 @@ <p align="center"> | ||
![Downloads](https://img.shields.io/npm/dt/mirax-player.svg?style=flat-square&label=Downloads&color=brightgreen) | ||
![!License](https://img.shields.io/npm/l/mirax-player.svg?style=flat-square&label=License&color=brightgreen) | ||
[![License](https://img.shields.io/npm/l/mirax-player.svg?style=flat-square&label=License&color=green)](https://github.com/demjhonsilver/mirax-player/blob/main/LICENSE.md) | ||
@@ -19,2 +19,16 @@ </div> | ||
--------------------- | ||
<p align="center"> | ||
<img src="https://raw.githubusercontent.com/demjhonsilver/mirax-player/main/img/theme2.png"/> | ||
</p> | ||
Disclaimer: This image is based on an example [TikTok embedded](https://developers.tiktok.com/doc/embed-videos) video clip. | ||
## Table of Contents | ||
@@ -39,3 +53,3 @@ | ||
Mirax Player is an adaptable video player and embedding solution that seamlessly integrates with TypeScript and JavaScript applications across a range of popular front-end libraries and frameworks, including React, Vue, Angular, and Svelte. It was written in pure JavaScript but can be implemented in both TypeScript and JavaScript. | ||
Mirax Player is a powerful free video player for React, Vue, Angular, and Svelte that can embed videos from platforms like TikTok, YouTube/Shorts, and Vimeo. This library package enables you to set any URL once within a single embed code tag and dynamically embed videos from any video sites. It was written in pure JavaScript but can be implemented in both TypeScript and JavaScript. | ||
@@ -47,6 +61,7 @@ ![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) ![Vue.js](https://img.shields.io/badge/vuejs-%2335495e.svg?style=for-the-badge&logo=vuedotjs&logoColor=%234FC08D) ![Angular](https://img.shields.io/badge/angular-%23DD0031.svg?style=for-the-badge&logo=angular&logoColor=white) ![Svelte](https://img.shields.io/badge/svelte-%23f1413d.svg?style=for-the-badge&logo=svelte&logoColor=white) | ||
- You can set any color themes inside to your component for video player. | ||
- You can set dynamic width and height while embedding videos. | ||
- No need to add "embed_clip" for your embed css | ||
- Params for width and height are move to inline embed tag. | ||
- Mirax officially supports TikTok videos. | ||
- The default width for the video player is 800x450 (HD). | ||
- Timestamps for videos reduce the number of digits displayed in the video player. | ||
- I removed the following CSS properties from the `.whatever` class: `margin: 0 auto;` and `width: 100%;` | ||
- The player theme has been fixed for custom float issues. | ||
------- | ||
@@ -56,4 +71,5 @@ ## Features | ||
- The advantage is that this player serves as a universal tool with minimal syntax, distributed to various javascript/typescript libraries and frameworks. | ||
- This video player supports both TypeScript and JavaScript, making it developer-friendly. | ||
- Easy to use and responsive | ||
- Can embed videos like YouTube, YouTube Shorts and Vimeo | ||
- Can embed videos like TikTok, YouTube, YouTube Shorts and Vimeo via `Copy link` | ||
- Capable of playing videos (Portrait or Landscape) | ||
@@ -78,9 +94,14 @@ - Supports 9:16 dimensions (Mobile video) | ||
![YouTube](https://img.shields.io/badge/YouTube-%23FF0000.svg?style=for-the-badge&logo=YouTube&logoColor=white) ![Vimeo](https://camo.githubusercontent.com/2026999d43e099c9c835757e3d2f5f8c574efad153f4e3d5143914223e9cbc24/68747470733a2f2f613131796261646765732e636f6d2f62616467653f6c6f676f3d76696d656f) | ||
![TikTok](https://img.shields.io/badge/TikTok-%23000000.svg?style=for-the-badge&logo=TikTok&logoColor=white) | ||
Sites | Source | Control Params | Links | | ||
------ | -------- | --------- | -------------- | | ||
Sites | Source type | Add-on Params | Links | | ||
------ | -------- | --------- | --------- | | ||
YouTube / Shorts | Iframe Api | https://developers.google.com/youtube/player_parameters | https://developers.google.com/youtube/iframe_api_reference | ||
Vimeo | Player SDK | https://developer.vimeo.com/player/sdk/embed | https://developer.vimeo.com/player/sdk | ||
TikTok | oEmbed API | https://developers.tiktok.com/doc/embed-videos/ | https://developers.tiktok.com/doc/overview/ | ||
-------- | ||
Thanks to these documentations of YouTube, Vimeo, and TikTok, I was able to integrate their APIs seamlessly into this library package. 😆 👏 | ||
--------------- | ||
@@ -101,5 +122,5 @@ | ||
<div ref={embedVideo} | ||
data-mirax-width="640" // you can set any value | ||
data-mirax-height="360" // you can set any value | ||
data-mirax-embed="https://vimeo.com/217499569" // links from youtube/shorts | ||
data-mirax-width="640" // You can set any value, such as 800x450, to make it larger. | ||
data-mirax-height="360" // | ||
data-mirax-embed="https://www.tiktok.com/@scout2015/video/6718335390845095173" // links from TikTok Youtube/Shorts and Vimeo | ||
> | ||
@@ -183,4 +204,4 @@ </div> | ||
<div ref={embedVideo} | ||
data-mirax-width="1040" | ||
data-mirax-height="560" | ||
data-mirax-width="640" | ||
data-mirax-height="360" | ||
data-mirax-embed="https://vimeo.com/217499569"> | ||
@@ -452,5 +473,3 @@ </div> | ||
.whatever { | ||
margin: 0 auto; | ||
position: relative; | ||
width: 100%; | ||
float: left; | ||
@@ -464,3 +483,2 @@ text-align: left; | ||
position: relative; | ||
width: 100%; | ||
text-align: center; | ||
@@ -472,5 +490,3 @@ } | ||
.whatever { | ||
margin: 0 auto; | ||
position: relative; | ||
width: 100%; | ||
float: right; | ||
@@ -480,8 +496,3 @@ text-align: right; | ||
``` | ||
```js | ||
const miraxCustomizer = { | ||
playerTheme: "", | ||
progressTheme: "" | ||
}; | ||
``` | ||
----- | ||
@@ -488,0 +499,0 @@ Examples: |
@@ -27,4 +27,4 @@ ```js | ||
<div ref={embedVideo} | ||
data-mirax-width="1040" | ||
data-mirax-height="560" | ||
data-mirax-width="640" | ||
data-mirax-height="360" | ||
data-mirax-embed="https://vimeo.com/217499569"> | ||
@@ -31,0 +31,0 @@ </div> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
69442
959
539
1