Вернуться на предыдущую страницу

VideoJS – полноценный видеоплеер для Вашего сайта, который имеет массу дополнений, расширений, возможностей, для реализации любого проекта, который так или иначе связан с видео. Плеер работает на основе технологий HTML5 видео, написанный на основе языка JavaScript, а также использующий современные возможности CSS3.

В данной статье Вы сможете найти код для подключения видео на любую HTML страницу, а также несколько вариантов разных видео HTML5 плееров. Но самое главное – Вы найдете инструкцию для подключения VideoJS, а также подключение к данному плееру плейлистов разного вида. И немного «рюшечек»...

Многие современные проекты содержат на своих страницах видео, которое за годы существования интернет, перерождалось вновь и вновь. Изначально создатели web скорее всего не представляли, что каждый 5 сайт сегодня будет иметь возможность транслировать видео своим пользователям, а каждый 250-тый – возможность показа потокового видео (онлайн трансляции).

Понятно, что многие из них используют популярные видео-хостинги: YouTube, Vimeo и другие... Но, когда создатели проекта хотят использовать свои ресурсы, что же делать? На помощь приходят прямые руки и всего-лишь небольшой код, который позволяет использовать на любой web-странице — видео:

<video class="my_css_class" controls="controls" preload="auto">
  <source src="https://site.org/video/video.mp4" type="video/mp4">
</video>

Но что делать, если этого мало? Ведь при таком подключении, пользователь видит лишь стандартный плеер интернет-обозревателя, который в большинстве своем, даже на сегодняшний день, оставляют желать лучшего:

  • ограниченный функционал;
  • часто недружелюбный интерфейс;
  • возможные ошибки браузерного плеера...;
  • часто отсутствие управления клавишами клавиатуры ПК...

Так выглядит в Opera, а так в FireFox, так – в Chrome, так – Яндекс-браузер.

Поэтому, некоторые задумываются над тем, чтобы написать свой плеер (ссылка 1, ссылка 2, ссылка 3) для своего сайта. Ведь когда ты пишешь свой собственный код – это улучшает твою карму в геометрической прогрессии, а также уровень твоих знаний.

Для примера можно скачать архив 1, скачать архив 2скачать архив 3.

Почему именно VideoJS?

На сегодня существует много решений, которые могут подойти под различные проекты, однако, наш выбор пал именно на данный видео плеер по нескольким причинам:

  • поддерживает много интернет-обозревателей (кроссбраузерный);
  • имеет плейлист, который даже стандартный более-менее работает;
  • поддерживает jQuery;
  • поддержка многих языков мира (переведен);
  • имеет много функций, в том числе:
    • управление воспроизведением с клавиатуры;
    • отображение времени на временной шкале;
    • поддерживает переключение скорости воспроизведения;
    • может регулировать громкость;
    • поддерживает полноэкранный режим;
    • как и все, умеет воспроизводить: OGG+MP4+WEBM
    • имеет обратные функции воспроизведения (например, может что-то сделать после окончания видео);
  • сам код плеера написан почти по-людски и даже читаемо, и даже редактируемо.

Какие другие решения есть?

1. Plyr

[оф. сайт, Скачать Plyr]

2. Projekktor

//Осторожно! Писали видимо Drupal'исты в далеком 2013 году!

[оф. сайт уже не работает, поэтому ссылка на git, Скачать Projekktor]

3. jPlayer (jQuery)

[оф. сайт, Скачать jPlayer]

И другие

Как подключить и использовать VideoJS?

Для этого необходимо скачать сам плеер (ссылка ниже на оф. сайт или кнопка «Скачать»).

Подключить стили и JS код плеера (данное подключение из CDN, Вы можете подключить из своей папки (/script/js/video-js.js)):

<link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.2.0/video-js.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/7.2.0/video.js"></script>

HTML код выглядит так:

<video
    id="my-player"
    class="video-js"
    controls
    preload="auto"
    poster="//vjs.zencdn.net/v/oceans.png"
    data-setup='{}'>
  <source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4"></source>
  <source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm"></source>
  <source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg"></source>
  <p class="vjs-no-js">
    To view this video please enable JavaScript, and consider upgrading to a
    web browser that
    <a target="_blank" href="https://link.inverser.pro/r2?l=http://videojs.com/html5-video-support/" target="_blank">
      supports HTML5 video
    </a>
  </p>
</video>

Каждый, подключаемый на странице плеер, должен иметь свой уникальный ID (<video id="id1">...</video>).
Затем подключаем плеер в JS:

var player = videojs('my-player');

Его также можно подключить, используя различные опции:

var options = {};

var player = videojs('my-player', options, function onPlayerReady() {
  videojs.log('Your player is ready!');

  // In this context, `this` is the player that was created by Video.js.
  this.play();

  // How about an event listener?
  this.on('ended', function() {
    videojs.log('Awww...over so soon?!');
  });
});

Как добиться уникальности?

Субтитры...

  1. Формат VTT.
  2. Подключение:
    <div class="vdo">
    <video id="zxc" poster="https://f.inverser.pro/im_pre/1/UseMind.ORG_ploskaya-zemlya-pravda-ne-boitsya-rassledovaniya.jpg" class="vid0 video-js" controls preload="auto" data-setup='{"playbackRates":[0.5,0.7,1,1.2,1.5,1.7,2,3]}'>
    <source src="https://f.inverser.pro/doc/world/f2/UseMind.ORG_ploskaya-zemlya-pravda-ne-boitsya-rassledovaniya.mp4" type="video/mp4" />
    <track kind="captions" src="/files/srt/0/UseMind.ORG_myasnoe-lobbi.vtt" srclang="ru" label="Русский" default />
    </video>
    </div>

Скачать файл пример субтитров

Для генерации и конвертирования может быть использована программа Subtitle Workshop


Часто необходимо, чтобы плеер работал как-то иначе, чем просто воспроизводит одно видео. Чтобы подключить плейлист:

1. Скачать версию с плейлистом.
или npm:

$ npm install videojs-playlist

Bower:

$ bower install videojs-playlist

Немного информации о плагине [EN]...

Использовать так:

var player = videojs('video');

player.playlist([{
  sources: [{
    src: 'http://media.w3.org/2010/05/sintel/trailer.mp4',
    type: 'video/mp4'
  }],
  poster: 'http://media.w3.org/2010/05/sintel/poster.png'
}, {
  sources: [{
    src: 'http://media.w3.org/2010/05/bunny/trailer.mp4',
    type: 'video/mp4'
  }],
  poster: 'http://media.w3.org/2010/05/bunny/poster.png'
}, {
  sources: [{
    src: 'http://vjs.zencdn.net/v/oceans.mp4',
    type: 'video/mp4'
  }],
  poster: 'http://www.videojs.com/img/poster.jpg'
}, {
  sources: [{
    src: 'http://media.w3.org/2010/05/bunny/movie.mp4',
    type: 'video/mp4'
  }],
  poster: 'http://media.w3.org/2010/05/bunny/poster.png'
}, {
  sources: [{
    src: 'http://media.w3.org/2010/05/video/movie_300.mp4',
    type: 'video/mp4'
  }],
  poster: 'http://media.w3.org/2010/05/video/poster.png'
}]);

// Play through the playlist automatically.
player.playlist.autoadvance(0);

Создадим контейнер с плейлистом:

<section class="main-preview-player">
<video id="preview-player" class="video-js vjs-fluid" controls preload="auto" crossorigin="anonymous">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a target="_blank" href="https://link.inverser.pro/r2?l=http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video>

<div class="playlist-container preview-player-dimensions vjs-fluid">
<ol class="vjs-playlist"></ol>
</div>
</section>

Скачайте также VideoJS Playlist UI (ниже).

Добавим стилей:

.main-preview-player {
display: flex;
justify-content: space-between;
}

.video-js,
.playlist-container {
position: relative;
min-width: 300px;
min-height: 150px;
height: 0;
}

.video-js {
flex: 3 1 70%;
}

.playlist-container {
flex: 1 1 30%;
}

.vjs-playlist {
margin: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

На самом деле версия 7+ имеет OOP JS стиль написания, поэтому она, видимо, только для избранных. Мы нашли версию 5.16.0 с плейлистом, который можно подключить. используйте её, если не разбираетесь в OOP JS, на которой 7+ версия, которая в консоли выдает ошибку:

Uncaught SyntaxError: Unexpected token {

Немного magic...  

Используем версию jQuery > 3

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" type="text/javascript"></script>

В общем-то плеер хороший, но список воспроизведения реализован не совсем классно. Поэтому пришлось чуть его переделать.

Вот такую штуку реализовали мы... Здесь нет JS в списке воспроизведения, ведь JS в исходном коде не любят поисковые системы.

How it works?

Мы написали небольшой скриптик, который позволяет вытягивать из HTML необходимые нам данные и воспроизводить видео одно за другим из списка. Его можно расширить, сделав те же кнопки «вперед», «назад», при необходимости. Но давайте вначале разберем как он выполняется...

Мы использовали Slick Slider для того, чтобы пользователь мог легко перемещаться по сериям. На другом проекте, список выглядит немного иначе...

Давайте углубимся, ведь наверняка, Вам интересно)?

Добавляем видео мы совсем ужасным способом: грузим через FTP на сервер с nginx и с поддержкой h264 Streaming module (очень древней версии...), а затем тупо в текстовое поле вставляем такую штуку:

То есть, с первым полем все понятно. Оно содержит ссылку на видео и предварительное изображение для видео...

Второе же поле содержит - ссылку на изображение и затем ссылку на видео. Т.е.:

https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj2.jpg
https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj2.mp4

Обработчик PHP, который загоняет все в базу выглядит так:

$video_link = $_POST["video_link"];
$video_link = strip_tags($video_link);
$video_link = htmlspecialchars($video_link, ENT_QUOTES);
$video_link = trim($video_link);

$v_img = $_POST["v_img"];
$v_img = strip_tags($v_img);
$v_img = htmlspecialchars($v_img, ENT_QUOTES);
$v_img = trim($v_img);

$playlist = $_POST["playlist"];
settype($playlist, "string");
$playlist = strip_tags($playlist);
$playlist = htmlspecialchars($playlist, ENT_QUOTES);
$playlist = trim($playlist);
$playlist = str_replace("\n", "===", $playlist);

$pl_names = $_POST["pl_names"];
settype($pl_names, "string");
$pl_names = strip_tags($pl_names);
$pl_names = htmlspecialchars($pl_names, ENT_QUOTES);
$pl_names = trim($pl_names);
$pl_names = str_replace("\n", "===", $pl_names);

Как видим из последней строки, мы делаем так:

Изначально наша строка:

https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj2.jpg
https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj2.mp4

с плейлистом разделяется переносом строки «\n», мы разделяем (для «Верочки» =) ) вот такой штукой: "===", можно вместо неё использовать любой другой символ, который НЕ ВСТРЕЧАЕТСЯ в URL, например, "*".

Помимо прочих данных, плейлист мы также сохраняем в JSON формате в БД:

$data_array = array('video_link'=>$video_link,'v_img'=>$v_img,'playlist'=>$playlist,'pln'=>$pl_names);

function prepareUTF8($matches){
return json_decode('"'.$matches[1].'"');
}

$data_json = preg_replace_callback('/((\\\u[01-9a-fA-F]{4})+)/', 'prepareUTF8',json_encode( $data_array ));

if (isset($id_item))
{
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$query->update($db->quoteName('#__content'))
->set(array($db->quoteName('item_dt') . '=\''.$data_json.'\''))
->where(array($db->quoteName('id') . '='.$id_item.''));
$db->setQuery($query);
$result = $db->query();
}

И выглядеть он будет так:

{"image_main":"https://f.inverser.pro/img/7/Multik.UseMind.ORG_nash-drug-pishichitaj_myltfilm.jpg","video_link":"https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj.mp4","v_img":"https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj.jpg","playlist":"https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj2.jpg===https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj2.mp4===https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj3.jpg===https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj3.mp4","pln":"Название 1===Название 2===Название 3"}

Вытащим из базы все данные, необходимые для генерации списка воспроизведения (если что, мы используем CMS Joomla):

$db = JFactory::getDBO();
$query = 'SELECT item_dt FROM #__content WHERE state=1 AND id = "'.$id.'"';
$db->setQuery($query);
$rows = $db->loadObjectList();
foreach ($rows as $row){$it_d = $row->item_data;}
$it_d = json_decode($it_d);//вот это и будет нашими данными с плейлистом, которые Вы видите выше...
$get_ttl = $it_d->title_item;

Немного переменных...

$im_get_link = $it_d->image_main;/*нам для данной статьи не нужно, но это как бы главное изображения для мультика...*/
$v_link = $it_d->video_link;/*первая ссылка на видео*/
$v_jpg = $it_d->v_img;/*первая ссылка на превью для видео*/
$pt = $it_d->playlist;/*пллст)*/
$pln = $it_d->pln;/*имена для пллст*/

Вывод именно осуществим так:

if(!empty($pt)){/*если в БД есть плейлист, то мы его выведем:*/
$result0 = explode("===", $pt);/*разобьем строку из БД с плейлистом (видео и превью) на Array (массив)*/
$count_arary = count($result0);/*подсчитаем сколько в массиве элементов*/
/*далее начнем формировать код HTML для вывода его браузеру...*/
$codepl='<div class="s">
<div>

<div class="fc fjsb">
<h4 class="in"><i class="icon-paragraph-left"></i> Список воспроизведения:</h4>
<div class="fc">
<div class="infAut cp"><i class="icon-info-2" title="Автоматически переходить к следующему видео"></i> Автомат</div>
<div class="al cp" data-al="1">
<div class="aChanger"></div>
</div>
</div>
</div>
</div>';
if(($count_arary/2)>2){$codepl.='<div class="plI">
<i class="icon-info-2"></i> Используйте стрелки навигации, чтобы перемещаться по сериям, а также серые кнопки с номером серии.
</div>';}

$plst_names0 = explode("===", $pln);/*имена для плейлиста*/
$count_arary0 = count($result0);/*мы их тоже посчитаем*/
$iwhile0 = 0;/*всякие там переменные для циклов*/
$ititle0 = 2;
$ire0 = 0;
$whplnames0 = 1;
$rtfu = 1;
$idsblocks = 1;
$titleFist=NULL;

if(!empty($pln)){$titleFist= $plst_names0[0];}else{
$titleFist= $this->item->title;/*если имена пустые, то я вывожу название мультика с порядковым номером серии (1. Ну погоди, 2. Ну погоди, 3. Ну погоди...)...*/
}

$codepl.='<div class="slSer"><div class="plformob pl actvv" data-id="'.$idsblocks.'" data-video="'.$v_link.'"><div class="titlmob">'.$rtfu.'. '.$titleFist.'</div><div class="pl_btp fc"><i class="icon-play-circle"></i></div><div class="imgmob"><div style="background-image:url(\''.$v_jpg.'\')"></div></div></div>';
$idsblocks++;

while($iwhile0 < $count_arary0){
$rtfu++;
if(!empty($pln)){
$ire0++;
$codepl.='<div class="plformob pl" data-id="'.$idsblocks.'" data-video="'.$result0[$ire0].'"><div class="titlmob">'.$rtfu.'. '.$plst_names0[$whplnames0].'</div><div class="pl_btp fc"><i class="icon-play-circle"></i></div><div class="imgmob"><div style="background-image:url(\'';
$whplnames0++;
$ire0--;
}else{
$ire0++;
$codepl.='<div class="plformob pl" data-id="'.$idsblocks.'" data-video="'.$result0[$ire0].'"><div class="titlmob">'.$ititle0.'. '.$this->item->title.'</div><div class="pl_btp fc"><i class="icon-play-circle"></i></div><div class="imgmob"><div style="background-image:url(\'';
$ire0--;
}
$codepl.= $result0[$ire0];/*img*/
$codepl.= '\')"></div></div>';
$ire0++;
$codepl.= '</div>';
$ititle0++;
$ire0++;
$idsblocks++;
if($result0[$ire0] === NULL ){$iwhile0 = $count_arary0;}
}
$codepl.='</div></div>';
echo $codepl;
}

Добавим сам плеер:

<div class="player br3">
<link href="/sys/video-js.css" rel="stylesheet">
<script async="" src="/sys/videojs-ie8.min.js"></script>
<script async="" src="/sys/video.js"></script>
<div class="vdo">
<video id="zxc" poster="https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj.jpg" class="vid0 video-js vjs-tech" controls preload="auto">
<source src="https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj.mp4" type="video/mp4" />
</video>
</div>
</div>

Все это работает и выводит следующий HTML код:

<div class="s">
<div>
<div class="fc fjsb">
<h4 class="in"><i class="icon-paragraph-left"></i> Список воспроизведения:</h4>
<div class="fc">
<div class="infAut cp"><i class="icon-info-2" title="Автоматически переходить к следующему видео"></i> Автомат</div>
<div class="al cp" data-al="1">
<div class="aChanger"></div>
</div>
</div>
</div>
</div>
<div class="slSer">
<div class="plformob pl actvv" data-id="1" data-video="https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj.mp4">
<div class="titlmob">1. Наш друг Пишичитай</div>
<div class="pl_btp fc"><i class="icon-play-circle"></i></div>
<div class="imgmob">
<div style="background-image:url('https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj.jpg')"></div>
</div>
</div>
<div class="plformob pl" data-id="2" data-video="https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj2.mp4">
<div class="titlmob">2. Наш друг Пишичитай</div>
<div class="pl_btp fc"><i class="icon-play-circle"></i></div>
<div class="imgmob">
<div style="background-image:url('https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj2.jpg')"></div>
</div>
</div>
<div class="plformob pl" data-id="3" data-video="https://f.inverser.pro/mlt/1/pishi/Multik.UseMind.ORG_nash-drug-pishichitaj3.mp4">
<div class="titlmob">3. Наш друг Пишичитай</div>
<div class="pl_btp fc"><i class="icon-play-circle"></i></div>
<div class="imgmob">
<div style="background-image:url('https://f.inverser.pro/im_pre/1/Multik.UseMind.ORG_nash-drug-pishichitaj3.jpg')"></div>
</div>
</div>
</div>
</div>

Немного стилей (CSS)...

@charset utf-8;
.in{display:inline-block}
.br3{border-radius:3px;-webkit-border-radius:3px}
.fc{display:-webkit-flex;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;-webkit-justify-content:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}
.fjsb{justify-content:space-between}
/*Автовоспроизведение следующего видео*/
.al{position:relative;margin:4px 0;background:#ccc;border-radius:2px;min-width:37px;height:18px;box-shadow:0 0 0 1px #b3b3b3}
.al:active .aChanger{box-shadow:0 0 0 1px #cc3a00,inset 0 2px #000,inset 0 2px 5px #000}
.aChanger{position:absolute;width:18px;height:18px;background:#F11111;border-radius:2px;box-shadow:0 0 0 1px #cc3a00,inset 0 2px #ff9e78,inset 0 2px 3px #fff}
.aChangerCl{transform:translate3d(100%,0,0);filter:grayscale(100%);box-shadow:0 0 0 1px #cc3a00,inset 0 2px #000,inset 0 2px 5px #000}
.aChanger{transition:all .2s linear}

/*player*/
.vdo{position:relative}
.errv{position:absolute;z-index:2;top:0;right:0;left:0;background:red;color:#fff;padding:4px}
.errv i{animation:pz 2s linear infinite;-webkit-animation:pz 2s linear infinite}
@keyframes pz{90%{filter:brightness(1);transform:scale(1)}92%{filter:brightness(2);transform:scale(1.4)}}
@-webkit-keyframes pz{90%{-webkit-filter:brightness(1);-webkit-transform:scale(1)}92%{-webkit-filter:brightness(2);-webkit-transform:scale(1.4)}}
.pl,.player{padding:5px 4px;background:#fff;outline:0!important}
.player{position:relative;background:#5A3E05}
/*\player*/

/*playlist*/
.plI{position:relative;font-size:12px;line-height:12px;background:rgba(0,0,0,.3);padding:4px 4px 4px 18px;border-left:3px solid #f5ba44;margin:0 0 3px 1px}
.plI i{position:absolute;left:3px}
.actvv{box-shadow:0 0 0 1.5px #ff002a!important}
.actvv:before{content:'';width:12px;height:12px;position:absolute;z-index:3;bottom:2px;right:0;background:#fa0;box-shadow:0 0 8px #FFEA00,inset 0 0 0 2px #900101,inset -0.51px 0 #ff002a;border-radius:50%;animation:ad 1.5s 5 ease-in-out;-webkit-animation:ad 1.5s 5 ease-in-out}
@-webkit-keyframes ad{30%,70%{background:#fff}}
@keyframes ad{30%,70%{background:#fff}}
.actvv:after{content:'активное видео';position:absolute;z-index:2;bottom:2px;right:9px;background:#ff002a;color:#fff;padding:0 4px 0 2px;font-size:11px;line-height:12px;box-shadow:-2px 0 #fc0;border-radius:5px 0 0 5px}
.imgmob{position:relative;width:100%;height:135px}
.imgmob div{position:absolute;top:0;right:0;bottom:0;left:0;background-position:50% 50%;background-repeat:no-repeat;background-size:contain}
.pl{position:relative;z-index:3;overflow:hidden;min-height:120px;margin:4px 0;cursor:pointer}
.pl_ttl{font-size:12px;background:rgba(255,255,255,.95);text-align:left}
.pl_btp{position:absolute;z-index:1;top:0;right:0;bottom:0;left:0;color:#fff;font-size:27px;text-shadow:0 4px 5px #000}
.viewed i{position:absolute;z-index:2;bottom:0;left:0;color:#fff;background:#000;padding:4px 2px 2px 0px}
.viewed:before,.viewed i,.viewed:after,.slick-dots li,.slick-slide,.actvv,.slick-prev,.slick-next{transition:all .4s linear}
.viewed:before{content:'просмотрено';position:absolute;z-index:2;bottom:0;left:16px;color:#fff;background:#000;border-radius:0 3px 0 0;padding:0 2px 0;font-size:13px}
.viewed:after{content:'';position:absolute;z-index:1;top:0;right:0;bottom:0;left:0;background:rgba(0,0,0,.5);box-shadow:inset 0 0 0 1px #ce9016}
.pl:hover .viewed i,.pl:hover .viewed:before{transform:translate(0,100%)}
.pl:hover .viewed:after{opacity:0}
.s{background:rgba(25,25,25,.75);color:#fff}
.s .icon-info-2{margin:0}
.s h4{font-weight:normal;line-height:12px;margin:0}
.s .fc{padding:2px}
.slick-prev,.slick-next{position:absolute;left:0;top:0;width:24px;border-radius:0;box-sizing:border-box;border:1px solid #333;height:158px;transform:translate(0);background:#f7a500;color:#000;border-radius:3px 0 0 3px}
.slick-next{border-radius:0 3px 3px 0}
.slSer .slick-next:hover div{animation:pq 1s linear 3}
.slSer .slick-prev:hover div{animation:pw 1s linear 3}
.slSer .slick-next{left:auto;right:0}
.titlmob{font-size:14px}
.titlmob{color:#000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.slSer .slick-dots{color:#000}
.slSer [class^="icon-"],.slice [class^="icon-"]{margin:0}
.slSer .slick-list{margin:0 26px}
@keyframes pq{50%{transform:translate(4px,0)}}
@keyframes pw{50%{transform:translate(-4px,0)}}
.slick-dots{display:block;width:100%;padding:0;margin:0;list-style:none;text-align:center}
.slick-dots li{border-radius:2px;margin:0 1px;display:inline-block;background:#ccc;min-width:17px;line-height:12px;padding:5px;cursor:pointer;border:0;outline:none;cursor:pointer;transform:scale(.8)}
.slick-dots li.slick-active{background:#f7a500;transform:scale(1)}
/*\playlist*/

Долгожданный JS:

Как использовать и подключить Slick Slider:

  1. Скачиваем Slick Slider.
  2. Распаковываем.
  3. Кидаем на сервер файлы JS и CSS (slick.css и slick.js, slick-theme.css, папку fonts [можно использовать min версии]).
  4. Привязываем к странице:
    <link href='/slick.css' rel='stylesheet' />
    <script src="/slick.js"></script>
  5. Подключаем JS код к своей странице в привязанном ранее Вашем JS или прямо на странице (что не любят поисковые системы):
    $('.slSer').slick({
    dots:true,
    infinite:false,
    slidesToShow:3,
    slidesToScroll:3,
    responsive:[
    {
    breakpoint:1024,
    settings:{
    slidesToShow:3,
    slidesToScroll:3,
    infinite:false,
    dots:true
    }
    },
    {
    breakpoint:600,
    settings:{
    slidesToShow:2,
    infinite:false,
    slidesToScroll:2
    }
    },
    {
    breakpoint:480,
    settings:{
    slidesToShow:1,
    infinite:false,
    slidesToScroll:1
    }
    }
    ]
    });

Итак, на данный момент мы уже научились использовать функцию добавления в БД и извлечения из неё данных для плейлиста... Подключили Slick Slider, чтобы наш плейлист имел возможность переключения по сериям, когда их будет много... очень много...

Кстати, данный способ успешно работает, когда в нашем плейлисте есть даже более 100 видео... Т.е. переключение происходит удобно и на ПК, и планшетах, и на мобильных...

Подключенный Slick Slider позволяет легко добавлять слайдер с сериями, а также дает возможность удобно использовать плейлист со всех устройств, подстраиваясь под разрешения, в зависимости от тех условий, которые мы задали ему выше... Идем дальше...

А теперь узнаем как реализовано переключение между сериями, а также много «рюшечек»...

much more...

/*найдем в нашей ссылке (адресной строке) слово #play*/
/*если оно есть, то мы автоматически запустим видео...*/
if((location.href).search('#play') != -1){
w=$('.vjs-tech');
w.attr('autoplay','')
w[0].play();
history.replaceState(null,null,location.origin+location.pathname);
};

Данная штука будет полезна всем тем, кто хочет, чтобы при переходе из категории, человек НЕ нажимал кнопку «Play», а видео воспроизводилось автоматически...

Т.е. например, есть ссылка на мультфильм:

http://site.org/1-multik-one

Вы можете добавить в конец #play:

http://site.org/1-multik-one#play

и когда пользователь по ней перейдет, воспроизведение начнется автоматически... Кстати, эта функция НЕ зависит от того, какой плеер на сайте Вы используете, если это HTML5, то видео начнет воспроизводиться автоматически в любом случае =) только добавьте CSS класс для видео:

<video class="vjs-tech"></video>

Еще один интересный код – это поиск номера серии в hash. Т.е. если серий много и пользователь посмотрел их 10, а еще смотреть 90 (всего – 100 серий), то удобно будет сохранять их в ссылке, чтобы он мог, к примеру, поделиться с кем-то именно той серией, которой хочет, например, 11... И/или если он случайно закрыл браузер или неслучайно и забыл, какую смотрел... Или если я залил новое видео: было 10 серий, а залил 11-тую, то ссылку делаю так:

https://urltosite.org/1-multik#s11

Вот код JS:

/*поиск номера серии в hash*/
if((location.href).search('#s') != -1){
s=location.hash.slice(2),w=$('video'),pl=$('.pl');
s--;
if($('div').is('.slSer')){
$('.slSer').slick('slickGoTo',s);
};
w[0].pause();
w.attr('src',$(pl[s]).attr('data-video'));
w[0].play();
if(localStorage.vvlv !== undefined){
w.prop('volume',localStorage.vvlv);
};
$('.pl').removeClass('actvv');
$(pl[s]).addClass('actvv');
};

Этот код позволит начать воспроизведение видео с необходимой серии (если соблюдается наш код HTML), вывод которого показан выше (после кода PHP).

Функционал для списка воспроизведения:

/*playlist*/
if($('div').is('.slSer')){/*если есть DIV с классом slSer (т.е. если есть список воспроизведения)*/
/*след. функция - проверка для автоматического воспроизведения*/
function Zrem(){($('.infAutTxt').addClass('infAcl'),q=setTimeout(function(){$('.infAutTxt').remove();clearTimeout(q)},450))};
$('.infAut').on('mouseleave',function(){
Zrem()
});
/*если нажали на кнопку "Автомат ON", то мы добавляем соотв. инфу на эту кнопку, если нажали OFF) то убираем эту инфу... и стили добавляем, что она выключена как бы*/
$('.infAut').on('click',function(){
if(!$('div').is('.infAutTxt')){$(this).append('<div class="infAutTxt"><i class="icon-info"></i> Автоматически переходить к следующему видео</div>')}else{Zrem()};
});
/*для слайдера с сериями, если серий много, то мы вешаем на кругляшки, которые могут переключать серии – номера серий, чтобы было удобно переходить пользователю, но хитрость в том, что номеров серий может быть 100, а кругляшков - 34, т.е. отображаются в слайдере то ТРИ серии, то одна сразу, поэтому тут немного замороченно)*/
if($(window).width() > 599){
var i1=1,i2=3,lgz=($('.slSer .pl').length)
$('.slSer .slick-dots > li').each(function(){
i2=parseInt(i1+2);
if(i2>lgz){
var vzc;
if(i1==lgz){vzc=''}else{vzc=('-'+lgz)};
$(this).text(i1+vzc)
}else{
$(this).text(i1+'-'+i2)
}
i1=i2;
i1++;
});
};
/*здесь мы вот этот переключатель переключаем и оставляем в локальном храналище определенные ключи, чтобы потом пользователь ходил по сайту, а мы знали - хочет ли он, чтобы серии воспроизводились одна за другой или ему лучше вручную клацать*/
r=$('.aChanger');
t=$('.al');
/*Change btn and LS*/
t.on('click',function(){
if($(this).attr('data-al') == '1'){
r.addClass('aChangerCl');
$(this).attr('data-al','0');
localStorage.setItem('al','0');
}else{
r.removeClass('aChangerCl');
$(this).attr('data-al','1');
localStorage.setItem('al','1');
};
});
/*переход к следующему видео, используя данные AutoLoad (al–автовоспроизведения кнопки)
/*если кнопка активная - то переходим к след. видео, если отключена - то ничего не делаем.. и проверяем LocalStorage...*/
/*Go to the next video. Data from [al]*/
if(localStorage.al == undefined){
localStorage.setItem('al','1');
}else{
if(localStorage.al == '1'){
r.removeClass('aChangerCl');
t.attr('data-al','1');
localStorage.setItem('al','1');
}else{
r.addClass('aChangerCl');
t.attr('data-al','0');
localStorage.setItem('al','0');
};
};

};//\playlist

Если что, речь идет об этой кнопке и об этих "кругляшках":

Уровень громкости можно сохранять в LocalStorage...

var w=$('video');/*видео*/
/*сохранение уровня громкости в локальном храналище*/
if(localStorage.vvlv != undefined){
w.prop("volume",localStorage.vvlv);/*выставим уровень громкости, если он есть в LS*/
};
/*при изменении уровня громкости, мы записываем данные уровня громкости для ВСЕХ видео (т.е. для видео, которое будет воспроизводиться на конкретном сайте)*/
w.on('volumechange',function(){
localStorage.setItem('vvlv',w.prop('volume'));/*выставим уровень громкости при изменении, чтобы другие видео воспроизводились с таким же уровнем (на других страницах или же на этой же - другие видео)*/
});

Основной функционал нашего списка воспроизведения мы оставили «на потом»... Встречайте...

Весь последующий код можно расположить прямо в videojs.js, внизу файла. Мы тут ловим события из слайдера... со списком серий.

$(function(){

/*playlist func*/
if($('div').is('.pl')){/*если вообще есть плейлист*/

pl=$('.pl');
pl.on('click',function(){/*если нажали на конкретную серию*/

pl.removeClass('actvv');/*убираем у всех класс "активная"*/
$(this).addClass('actvv');/*добавляем этой серии класс "активная"*/
w=$('video');/*это наше новое видео*/
w[0].pause();/*остановим его, чтобы не было ошибки стека воспроизведения в Chromium*/
w.attr('src',$(this).attr('data-video'));/*возьмем ссылку на видео*/
w[0].play();/*воспроизведем видео*/
if(localStorage.vvlv !== undefined){/*уро-нь громоксти*/
w=$('video');
w.prop('volume',localStorage.vvlv);
};
/*ch_v_f();*//*это моя функция проверки видео, Вам она может и не нужна...*/
$('html,body').animate({
scrollTop: $('.vdo').offset().top
},1000);/*подкрутили страницу к видео*/
history.replaceState(null,null,location.origin+location.pathname+'#s'+$(this).attr('data-id'));/*установили серию в ссылке https://site.org/link#s22*/
});
if(localStorage.vvlv !== undefined){/*ур-нь громкости*/
w.prop('volume',localStorage.vvlv);
};
setItem();/*функция установки в LocalStorage номеров серии, которые были просмотрены*/
function setItem(){
if(localStorage.ended!==undefined){
arr=(localStorage.ended).split(',');
}else{
var arr=[parseInt((location.pathname).slice(1))+'.'+$('.actvv').attr('data-id')]
};

w.on('ended',function(){
var arrD=parseInt((location.pathname).slice(1))+'.'+$('.actvv').attr('data-id');
arr.push(arrD);
var unique=arr.filter((v,i,a)=>a.indexOf(v)===i);
localStorage.setItem('ended',unique);
viewed();
/*play next [al]*/
if(localStorage.al!==undefined && localStorage.al==1){

if($('div').is('.s')){
/*следующий в списке*/
var pl=$('.pl'),ttl=$(pl).length-1,indx=$(pl).index($('.actvv'));

if(ttl!=indx){
i=indx+1;
$('.pl').removeClass('actvv');
$(pl[i]).addClass('actvv');
w=$('video');
$('.slSer').slick('slickGoTo',i);
w[0].pause();
w.attr('src',$(pl[i]).attr('data-video'));
w[0].play();
if(localStorage.vvlv !== undefined){
w.prop('volume',localStorage.vvlv);
};
/*ch_v_f();*/
history.replaceState(null,null,location.origin+location.pathname+'#s'+$(pl[i]).attr('data-id'));
}
}
};
});
};

};/*\pl*/

viewed();/*функция, которая показывать какие видео в списке были уже просмотрены*/
function viewed(){
if(localStorage.ended!==undefined){
var arr=(localStorage.ended).split(','),q;
$.each(arr,function(k,v){
q=v.split('.');
if(parseInt((location.pathname).slice(1))==q[0]){
var pl=$('.pl');
$.each($('.pl'),function(i){
if($(this).attr('data-id')==q[1]){
if($(pl[i]).find($('.viewed')).length!==1){
$(pl[i]).append('<div class="viewed"><i class="icon-eye-open"></i></div>')
};
};
});
};
});
};
};/*\viewed*/

/*проверка видео файла*/
/*ch_v_f();*/
function ch_v_f(){
$.ajax({
url:'/sys/check.php',
type:'POST',
data:{flc:$('video source').attr('src')},
success:function(d){
if(d=='4'){
$('.errv').remove();
$('.vdo').append('<div class="errv"><i class="icon-pending"></i> Видео недоступно. Мы знаем об этом. Скоро исправим.</div>');
$.ajax({
url:"/sys/check.php",
type:"POST",
data:{fl:$('video source').attr('src')}
});
}
}
});/*\AJX ch*/
};
/*\check file video*/

/*\*/
});

Подключение VideoJS

Можно в том же файле JS: videojs.js

videojs(document.getElementsByClassName('vid0')[0]).on('ready',function() {
this.hotkeys({
volumeStep: 0.1,/*это шаг громкости 0.1, 0.2, ..., 0.7...*/
seekStep: 5,/*время в секундах, когда проматываем*/
enableMute: true,/*разрешить приглушение звука*/
enableFullscreen: true,/*разрешить полный экран*/
enableNumbers: false,/*что-то =) */
enableVolumeScroll: true,/*слайдер уровня звука*/
/*кнопки для выхода и входа в полноэкранный вид*/
fullscreenKey: function(e) {
/*fullscreen with the F key or Ctrl+Enter*/
return ((e.which === 70) || (e.ctrlKey && e.which === 13));
}})
});

PHP файл, который принимает данные из проверки по AJAX кода выше

/sys/check.php
<?
/*проверка видео файла на существование*/

if ($_SERVER['REQUEST_METHOD']=='POST'){
if(!empty($_POST['flc'])){
/*check video file*/
$url = (string) $_POST['flc'];

$url = strip_tags($url);
$url = htmlspecialchars($url);
$url = mysql_escape_string($url);

/*ответ*/
function response($path) {
$fp = fopen($path,"r");
$inf = stream_get_meta_data($fp);
fclose($fp);
foreach($inf["wrapper_data"] as $v) {
if (stristr($v, "HTTP/1.1")){
$v = explode(" ", $v);
return $response = trim($v[1]);
echo $response;
}
}

}
/*\response*/
if(response($url)===NULL){echo '4';}else{echo '2';}


}/*\check video file*/

/*Запись ошибки в html файл*/
if(!empty($_POST['fl'])){
$fle = (string) $_POST['fl'];

$fle = strip_tags($fle);
$fle = htmlspecialchars($fle);

$er_time=date("Y-m-d H:i:s");
$er_time2=date("m-d");
$U=getenv("HTTP_USER_AGENT");
$H=getenv("HTTP_REFERER");
$R=getenv("REMOTE_ADDR");

$bots = substr($U, 0, 34);

$sty = "
<!DOCTYPE html>
<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"ru-ru\" lang=\"ru-ru\">
<style>
ul {background:#f5f5f5;border-radius:9px; border:1px solid #ccc;list-style:none;margin-bottom:0px;}
ul > li { background:#fff;border-radius:9px; border:1px solid #ccc; padding:1px 10px; list-style:decimal outside;}
</style>
<ul><li>$er_time | <b>IP:</b> $R | <b>Br:</b> $U</li>
<li>Rf: <a target=\"_blank\" href=\"$H\">$H</a><br />$fle</li>
</ul><hr />";


$H=urldecode($H);

$fileSize = $_SERVER['DOCUMENT_ROOT']."/sys/er_video.html";

if (is_file($_SERVER['DOCUMENT_ROOT']."/sys/er_video.html"))
{
$f=fopen($_SERVER['DOCUMENT_ROOT']."/sys/er_video.html",'a');
flock ($f,2);
fwrite($f,"<ul><li>$er_time | <b>IP:</b> $R | <b>Br:</b> $U</li>
<li>Rf: <a target=\"_blank\" href=\"$H\">$H</a><br />$fle</li>
</ul><hr />");
}
else
{
$f=fopen($_SERVER['DOCUMENT_ROOT']."/sys/er_video.html",'a');
flock ($f,2);
fwrite($f,$sty);
fclose($f);
}

}/*\set error*/
}

Попробуем все собрать?

  1. Скопируем HTML в новый файл index.html
  2. Скачаем последнюю версию VideoJS, разархивируем, достанем и подключим нужные файлы...
  3. Скачаем Slick, разархивируем и подключим необходимые JS и CSS.
  4. Используем все «рюшечки», подключим весь JS код, необходимый для запуска VideoJS.
  5. Потестим...

В итоге у нас получилось нечто следующее...

Скачать архив с готовым вариантом подключения плеера версии 7.2.0 и нашим плейлистом.

VideoJS
Официальный сайт

Файлы VideoJS:

Скачать [Video JS v7.2.0]
DEMO
Скачать Video JS [Playlist v4.2.2] с плейлистом
DEMO
Скачать [Video JS v5.16.0] с плейлистом (рабочий вариант)
DEMO
Скачать VideoJS Playlist UI
DEMO
Скачать VideoJS v7.2.0 и наш список воспроизведения
DEMO