ساخت ویدیو پلیر حرفه ای با جاوا اسکریپت ( لیست پخش ، پیش نمایش فریم )

ساخت ویدیو پلیر حرفه ای با جاوا اسکریپت ( لیست پخش ، پیش نمایش فریم )

ویدیو پلیر با جاوا اسکریپت – در این آموزش با javascript خالص بدون هیچ فریمورک و کتابخانه ای ویدیو پلیر حرفه ای و شخصی سازی شده ی خودمان را می سازیم .

ویدیو پلیر اختصاصی با امکان playlist و پیش نمایش هر فریم ثانیه را در زمان بردن ماوس بر روی seekbar را دارد .

برای درک بهتر ویدیوی معرفی را تماشا نمایید :

ویدیوی معرفی پلیر اختصاصی با js


فایل index.html

<!DOCTYPE html>
<html lang="fa" id="xhtml">
<head>
    <meta charset="UTF-8">
    <title>Rapidcode.iR - سورس کد</title>
    <link rel="stylesheet" href="static/css/main.css">
</head>
<body>
    <div class="container">
        <a id="introduce" href="https://rapidcode.ir" target="_blank">رپید کد • کتابخانه مجازی برنامه نویسان</a>
        <div class="video-wrapper">
            <div id="preview-thumbnail">
                <img src="static/video/thumbnails/Lamborghini Urus (1).png">
            </div>
            <ul id="video-playlist">
                <li data-src="static/video/Lamborghini Urus.mp4">لامبورگینی اوروس</li>
                <li data-src="static/video/FORD Mustang GT.mp4">فورد موستانگ نیو</li>
            </ul>
            <div class="video-player-wrapper">
            <video id="my-custom-video-play" src="static/video/Lamborghini Urus.mp4"></video>
            <div class="video-controls">
                <span class="icon-player play-pause"></span>
                <div data-preview="true" id="timer" class="seekbar timer">
                    <div data-preview="true" id="loaded"></div>
                    <div data-preview="true" id="progress"><div data-preview="true" id="handler"></div></div>
                    <span id="remain">-00:00</span>
                </div>
                <span class="icon-player sound-mute"></span>
                <div class="seekbar sound">
                    <div id="loaded"></div>
                    <div id="progress"><div id="handler"></div></div>
                </div>
                <span class="icon-player full-screen"></span>
            </div>
        </div>
        </div>
    </div>
    <script src="static/js/app.js"></script>
</body>
</html>


اسکریپت app.js جهت کنترل بر رویداد ها و کنترل پلیر

// DOM
const videoDOM = document.getElementById('my-custom-video-play');
const seekbarDOM = document.getElementsByClassName('seekbar');
const seekbarTimersDOM = document.querySelector('.seekbar.timer');
const seekbarVolumeDOM = document.querySelector('.seekbar.sound');
const playpauseDOM = document.getElementsByClassName('play-pause')[0];
const soundmuteDOM = document.getElementsByClassName('sound-mute')[0];
const remainDOM = document.getElementById('remain');
const fullScreenDOM = document.getElementsByClassName('full-screen')[0];
const previewThumbnailDOM = document.getElementById('preview-thumbnail');
const videoPlaylistDOM = document.querySelectorAll('#video-playlist li');

// Property
const isMuted = videoDOM.hasAttribute("muted");
const isAutoplay = videoDOM.hasAttribute("autoplay");
var videoInterval;

// init
setInitialVideoOptions();

// set handlers
Array.from(seekbarDOM).forEach((element) => {
    element.addEventListener("click", seekbarDOMHandlerClicked);
    element.addEventListener("mousemove", seekbarDOMHandlerClicked);
    element.addEventListener("mousemove", seekbarDOMHandlerShowThumbnail);
});

Array.from(videoPlaylistDOM).forEach((element) => {
    element.addEventListener("click", videoPlaylistDOMHandlerClicked);
});



videoInterval = setInterval(VideoOnUpdateTime, 100)

playpauseDOM.addEventListener('click', playpauseDOMHandlerClicked);
fullScreenDOM.addEventListener('click', fullScreenDOMHandlerClicked);
window.addEventListener("dblclick", windowHandlerExitFullScreen);
window.addEventListener("keyup", windowHandlerShortcut);
window.addEventListener("mousemove", seekbarDOMHandlerShowThumbnail);


// handlers
function seekbarDOMHandlerClicked(event) {

    if (event.type != 'click' && event.buttons == 0) return false;

    const thisElement = this;
    const isSoundSeekbar = thisElement.classList.contains("sound");
    const isTimerSeekbar = thisElement.classList.contains("timer");

    if (isMuted && isSoundSeekbar) return false;

    const progressElement = thisElement.querySelector("#progress");
    const finallWidth = moveRangeSlider(thisElement , event)

    if (finallWidth < 0 || 100 < finallWidth) return;
    progressElement.style.width = finallWidth + "%";
    if (isTimerSeekbar) {
        videoDOM.currentTime = getNumberByPercent(finallWidth, videoDOM.duration);
        videoDOM.play()
    } else if (isSoundSeekbar) {
        if (thisElement.querySelector('#progress').clientWidth == 0) soundmuteDOM.classList.add("mute")
        else soundmuteDOM.classList.remove("mute")
        videoDOM.volume = finallWidth / 100;
    }
}

function videoPlaylistDOMHandlerClicked(){
    const thisElement = this;
    clearInterval(videoInterval);
    videoDOM.pause();
    const videoSrc = thisElement.dataset.src;
    videoDOM.src = videoSrc;
    videoDOM.load();
    
    videoDOM.onloadeddata = function(){
        setInitialVideoOptions();
        videoInterval = setInterval(VideoOnUpdateTime, 100)
        videoDOM.play()
    }
    makePlaylistElementActive(videoPlaylistDOM);
}

function seekbarDOMHandlerShowThumbnail(event){
    if(typeof event.target.hasAttribute == "undefined") return false;

    var videoFrame = getNumberByPercent(Math.round(moveRangeSlider(seekbarTimersDOM , event)), videoDOM.duration)

    if(!event.target.hasAttribute('data-preview') || (videoFrame < 0 || 100 < videoFrame)){
        previewThumbnailDOM.style.display = "none";
        return false;
    }

    videoFrame = Math.round(Math.abs(videoFrame)) == 0 ? 1 : Math.round(Math.abs(videoFrame));
    var videoName = videoDOM.getAttribute('src');
    videoName = videoName.substring(videoName.lastIndexOf('/'));
    videoName = videoName.replace(/\/|\.mp4/gi , "");
    const frameFileTemplate = `static/video/thumbnails/${videoName} (${videoFrame}).png`;
    
    previewThumbnailDOM.querySelector('img').src = frameFileTemplate;
    
    
    const previewThumbnailXY = {
        x : previewThumbnailDOM.clientWidth,
        y : previewThumbnailDOM.clientHeight,
    }

    previewThumbnailDOM.style.left = event.screenX - previewThumbnailXY.x - 60 + "px";
    previewThumbnailDOM.style.top = event.screenY - previewThumbnailXY.y - 200 + "px";
    previewThumbnailDOM.style.display = "block";

    
    
}

function videoDOMHandlerProgress() {
    const videoBufferedLastTimeRange = (videoDOM.buffered.length - 1);
    if (videoBufferedLastTimeRange < 0) return 0;

    const timeBuffered = videoDOM.buffered.end(videoBufferedLastTimeRange);
    return timeBuffered;
}

function VideoOnUpdateTime() {
    updateVideoPausePlayState();
    const bufferedTimeAll = videoDOM.duration;
    const bufferedTime = videoDOMHandlerProgress();
    seekbarTimersDOM.querySelector('#loaded').style.width = getPercentByNumber(bufferedTime, bufferedTimeAll) + "%";
    seekbarTimersDOM.querySelector('#progress').style.width = getPercentByNumber(videoDOM.currentTime, bufferedTimeAll) + "%";
    remainDOM.textContent = `- ${toMinutes(Math.round(videoDOM.duration - videoDOM.currentTime))}`;
}

function playpauseDOMHandlerClicked() {
    const thisElement = playpauseDOM;
    if (thisElement.classList.contains("pause")) {
        thisElement.classList.remove("pause");
        videoDOM.pause();
    } else {
        thisElement.classList.add("pause");
        videoDOM.play();
    }
}

function fullScreenDOMHandlerClicked() {
    if (videoDOM.requestFullscreen)
        videoDOM.requestFullscreen();
}

function windowHandlerExitFullScreen() {
    if (screen.height == window.innerHeight)
        if (document.exitFullscreen) {
            document.exitFullscreen();
        }
}

function windowHandlerShortcut(event){
    if(event.code == "Space"){
        playpauseDOMHandlerClicked();
    }

    if(event.code == "KeyF"){
        fullScreenDOMHandlerClicked();
    }
}

// helpers
function getPercentByNumber(piece, total) {
    const percent = piece / total * 100;
    return percent;
}

function getNumberByPercent(percent, total) {
    const number = percent * total / 100;
    return number;
}

function setInitialVideoOptions() {
    videoDOM.currentTime = 0;
    if (isAutoplay && isMuted) {
        playpauseDOM.classList.add("pause");
    }

    if (isMuted) {
        soundmuteDOM.classList.add("mute");
        seekbarVolumeDOM.classList.add("disabled");
    } else {
        videoDOM.volume = 0.5;
        document.querySelector('.sound #progress').style.width = '50%';
    }

    makePlaylistElementActive(videoPlaylistDOM);
}

function toMinutes(seconds) {
    let tmpSeconds = seconds;
    let minuteCounter = 0;
    let finallTime = "";
    while (true) {
        if (tmpSeconds < 60) {
            tmpSeconds = tmpSeconds.toString().padStart(2, '0');
            minuteCounter = minuteCounter.toString().padStart(2, '0');
            finallTime = `${minuteCounter}:${tmpSeconds}`;
        }
        tmpSeconds -= 60;
        minuteCounter++;
        if (tmpSeconds <= 0) break;
    }

    return finallTime;
}

function moveRangeSlider(element , event){
    const coordinate = element.getBoundingClientRect();
    const width = coordinate.width;
    const offset = {
        x: Math.round(coordinate.left),
        y: Math.round(coordinate.top),
    }
    const finallWidth = getPercentByNumber(Math.round(event.clientX - offset.x), width);
    return finallWidth;
}

function makePlaylistElementActive(elements){
    Array.from(elements).forEach((element)=>{
        element.classList.remove("active");
        if(decodeURI(videoDOM.src).search(element.dataset.src) != -1){
            element.classList.add("active");
        }
    });
}

function updateVideoPausePlayState(){
    if(videoDOM.paused){
        playpauseDOM.classList.remove("pause");
    }else{
        playpauseDOM.classList.add("pause");
    }
}


فایل main.css گرافیک سفارشی ویدیو پلیر

.container{
    margin: 0 auto;
    width: 80%;
    text-align: center;
    direction: rtl;
}

#introduce{
    display: block;
    width: 100%;
    font-size: 35px;
    font-weight: bold;
    color: white;
    padding-bottom: 5px;
    background-color: #4CAF50;
    text-decoration: none;
    margin-bottom: 15px;
}


/* Page Style */

.video-wrapper{
    position: relative;
}

.video-player-wrapper{
    position: relative;
    display: inline-block;
}

.video-controls{
    width: 100%;
    height: 50px;
    bottom: 4px;
    left: 0;
    position: absolute;
    background-image: linear-gradient(transparent,rgba(0,0,0,.75));
}

#my-custom-video-play{
    width: 720px;
    border-radius: 4px 0 0 4px;
    position: relative;
}

#my-custom-video-play::-webkit-media-controls{
    display:none !important;
}

#video-playlist{
    background-color: #ff9800;
    width: 190px;
    height: 405px;
    margin: 0;
    padding: 5px;
    box-sizing: border-box;
    list-style: none;
    position: absolute;
    right: 58px;
    overflow-y: auto;
}

#video-playlist li{
    text-align: right;
    cursor: pointer;
    margin-bottom: 10px;
    padding: 10px;
}

#video-playlist li:hover, #video-playlist li.active{
    background-color: cyan;
}

.icon-player{
    width: 18px;
    height: 18px;
    cursor: pointer;
    background-repeat: no-repeat;
    background-size: 18px;
    display: inline-block;
    position: absolute;
    bottom: 10px;
}

.icon-player.play-pause{
    background-image: url(../img/play-90.png);
    left: 10px;
}

.icon-player.play-pause.pause{
    background-image: url(../img/pause-90.png);
}

.icon-player.sound-mute{
    background-image: url(../img/sound-90.png);
    right: 140px;
}

.icon-player.sound-mute.mute{
    background-image: url(../img/mute-90.png);
}

.icon-player.full-screen{
    background-image: url(../img/full-screen-90.png);
    right: 10px;
}

.seekbar{
    background-color: rgb(255 255 255 / 22%);
    border-radius: 10px;
    height: 7px;
    bottom: 14px;
    position: absolute;
    cursor: pointer;
}

.seekbar #loaded{
    background-color: rgba(255, 255, 255, 0.726);
    border-radius: 10px;
    position: absolute;
    left: 0;
    bottom: 0;
    width: 1%;
    height: 7px;
    z-index: 5;
}

.seekbar #remain{
    cursor: default;
}

.seekbar #progress{
    background-color: #ff9800;
    border-radius: 10px;
    position: absolute;
    left: 0;
    bottom: 0;
    width: 0%;
    height: 7px;
    z-index: 10;
}

.seekbar #progress #handler{
    position: absolute;
    width: 14px;
    height: 14px;
    border-radius: 14px;
    background-color: #fff;
    bottom: -4px;
    right: -3px;
    z-index: 15;
}

.seekbar.timer{
    width: 455px;
    left: 40px;  
}

.seekbar.timer #remain{
    position: absolute;
    color: white;
    right: -50px;
    bottom: -5px;
    direction: ltr;
    -moz-user-select: -moz-none;
    -khtml-user-select: none;
    -webkit-user-select: none;
}

.seekbar.sound{
    width: 90px;
    right: 34px;
}

.seekbar.sound.disabled{
    cursor: not-allowed;
    opacity: 0.4;
}

.seekbar.sound #progress #handler{
    width: 4px;
}

.seekbar.sound #loaded{
    width: 100%;
}

#preview-thumbnail{
    width: 140px;
    position: absolute;
    left: 15px;
    top: 15px;
    padding: 15px;
    background-color: whitesmoke;
    border-radius: 8px;
    z-index: 20;
    display: none;
}

#preview-thumbnail img{
    width: 100%;
    border-radius: 8px;
}


دانلود ویدیو پلیر سفارشی با جاوا اسکریپت

ارسال نظر

جهت استفاده از کد حتما از تگ pre استفاده نمایید .

لیست نظرات

  1. امین
    امین

    وقت بخیر برای نمایش یک فیلم در سایت با چند کیفیت،منظور گزینه تعیین کیفیت در مدیا پلیر،ایا باد چند نسخه از فیلیم اپلود بشه در هاست یا لازم نیست،ممنون میشم در این زمینه اطلاع دارید راهنمایی بفرمایید با تشکر

    07 دی 1402 | 11:05:34
  • حسین باقری
    حسین باقری

    درود ، بله باید چند کیفیت مختلف آپلود بشه مثلا : 1080 و 720 و 480 که به ترتیب عالی ، خوب ، پایین خواهد بود کیفیت ها و می تونید از کتابخانه plyr برای این مورد کمک بگیرید .

    07 دی 1402 | 11:52:56
    • امین
      امین

      تشکر از راهنمایی شمل...سوال دیگه دارم ممنون میشم اطلاعاتی دارید راهنمایی بفرمایید.در زمان بارگذاری تصویر کاور ویدیو در سایت های که فیلم انلاین نمایش می دهند،مراحل اپلود تصور نمایش داده نمیشه چون کاور حجیم هست به جای تصور یک عکس سفید ظاهر میشه و بعد که عکس کامل لود شود جای تصور سفید نمایش داده میشه،ممنون میشم اطلعاتی دارید راهنمایی بکنید با تشکر خسته نباشید.

      07 دی 1402 | 14:09:12
      • حسین باقری
        حسین باقری

        از قابلیت lazy loading استفاده میشه که آموزش مرتبط در وبسایت هست

        07 دی 1402 | 16:08:16
        • امین
          امین

          با تشکر از راهنمایی خوب شما خسته نباشید.

          08 دی 1402 | 09:01:57
  • علی
    علی

    سلام استاد عزیز. من، کد های این ویدئو پلیر رو کپی کردم ولی بخاطر این خط کد: videoInterval = setInterval(VideoOnUpdateTime, 100) صفحه فقط در حال لود شدنه و بعد از چند ثانیه یه alert میده که صفحه رو ببندیم یا منتظر بمونیم. وقتی هم که کامنتش میکنم seekbar از کار میوفته. میشه راهنمایی کنید🙏

    10 مهر 1402 | 00:43:45
    • حسین باقری
      حسین باقری

      درود ، پیشنهاد میشه از سورس رو دانلود و استفاده کنید اگر به مشکل بر خوردید مجدد کامنت بفرستید .

      10 مهر 1402 | 11:39:49
  • مهدی
    مهدی

    سلام. میشه کاری کرد مسیر فایل روی سرور مخفی باشه یا کاری کرد که فیلم قابل دانلود نباشه؟

    07 دی 1401 | 09:23:33
    • حسین باقری
      حسین باقری

      درود ، میشه این کار با استفاده از ساخت encode انحصاری برای ویدیو انجام داد . اما مبحث پیشرفته ای هست و توی چند تا مقاله نمیشه توضیح داد .

      07 دی 1401 | 09:52:55
  • امیر محمد کلاری
    امیر محمد کلاری

    سلام وقتتون بخیر امکان استفاده از کد‌های مورد نظر برای وردپرس وجود داره؟

    29 خرداد 1401 | 17:33:43
    • حسین باقری
      حسین باقری

      سلام بله

      29 خرداد 1401 | 18:47:59
    contact us