diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md index b983bf8..acf8b7a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,81 @@ -# Rewrite -Zorn is still in early stages, yet a rewrite is already happening? This is SudoVanilla's first attempt at a JS library, so I want to get it right and with good practice. +# Zorn -Hopefull this rewrite will go better than what I've been doing, although it was quite functional the codebase was just a mess and I think I can do better. +![Preview of Zorn with default theme set](https://img.sudovanilla.com/dhHcB5l.png) + +> Currently in beta stages. + +## What is Zorn? +A custom HTMl5 video player for the web using vanilla JavaScript. + +## Installation +### Package Manager +With NPM: +```bash +npm install @sudovanilla/bun +``` + +With Yarn: +```bash +yarn add @sudovanilla/bun +``` + +With Bun: +```bash +bun add @sudovanilla/bun +``` + +With PNPM: +```bash +pnpm install @sudovanilla/bun +``` + +### Yarn PnP +If you are using Yarn it's PnP feature enabled, please unplug Zorn Player, add the following to your package.json file: +```json +"dependenciesMeta": { + "@sudovanilla/zorn": { + "unplugged": true + } +} +``` +Also make sure you are setting the correct path to the script and css file when adding them to your website or app. + +## Usage +### HTML5 +Add Zorn at the bottom of your HTML page: +```html + ... + + + +``` + +Then, add a video to the page inside a div with the class name set to `video-container`. Inside this div, will be the video element with the class name set as `zorn-player`. +```html +
+ +
+``` ___ -You check current version by viewing the original [`zorn/dev`](https://sudovanilla.com/code/Korbs/Zorn/src/branch/dev) branch. \ No newline at end of file +### Express +Route the files required for Zorn Player to work in Express (Tested with PokeTube): +``` +const ZornDirectory = "./node_modules/@sudovanilla/zorn/" +module.exports = function (app, config, renderTemplate) { + app.get("/player/zorn.js", function (req, res) {res.sendFile("/dist/index.js", { root: ZornDirectory })}) +} +``` + +Add the stylesheet and script: +```html + +``` + +Then, add a video to the page inside a div with the class name set to `video-container`. Inside this div, will be the video element with the class name set as `zorn-player`. +```html +
+ +
+``` diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000..8037b50 Binary files /dev/null and b/bun.lockb differ diff --git a/dist/zorn.js b/dist/zorn.js new file mode 100644 index 0000000..0f50e54 --- /dev/null +++ b/dist/zorn.js @@ -0,0 +1,46 @@ +(() => { + // src/assets/icons/play-solid.svg + var play_solid_default = ''; + + // src/get.js + var ZornVideoPlayer = document.querySelector(".zorn-player"); + var VideoContainer = document.querySelector(".video-container"); + var PlayIcon = play_solid_default; + + // src/themes/default.js + var Controls = ` +

+
+
+
+ + + +
+ + +
+
+
+
+ + +
00:00
+
+
+
+
+ + / + +
+ + +
+
+
+`; + + // src/index.js + ZornVideoPlayer.insertAdjacentHTML("afterend", Controls); +})(); diff --git a/esbuild.config.mjs b/esbuild.config.mjs new file mode 100644 index 0000000..094b527 --- /dev/null +++ b/esbuild.config.mjs @@ -0,0 +1,9 @@ +import esbuild from 'esbuild' +import svg from 'esbuild-plugin-svg' + +const context = await esbuild.context({ + entryPoints: ['src/index.js'], + bundle: true, + outfile: 'dist/zorn.js', + plugins: [svg()] +}) \ No newline at end of file diff --git a/esbuild.watch.config.mjs b/esbuild.watch.config.mjs new file mode 100644 index 0000000..f16a0ee --- /dev/null +++ b/esbuild.watch.config.mjs @@ -0,0 +1,12 @@ +import esbuild from 'esbuild' +import svg from 'esbuild-plugin-svg' + +const context = await esbuild.context({ + entryPoints: ['src/index.js'], + bundle: true, + minify: true, + outfile: 'test/js/zorn.js', + plugins: [svg()] +}) + +await context.watch() \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..1f6a3bf --- /dev/null +++ b/package.json @@ -0,0 +1,35 @@ +{ + "name": "zorn", + "version": "2024.02.26", + "description": "HTML5 Web Player", + "main": "src/index.js", + "repository": "https://sudovanilla.com/code/Korbs/Zorn/", + "author": "SudoVanilla ", + "license": "AGPL-3.0-or-later", + "bugs": { + "url": "https://sudovanilla.com/code/Korbs/Zorn/issues", + "email": "support@sudovanilla.com" + }, + "funding": [ + { + "type": "individual", + "url": "https://sudovanilla.com/donate/" + } + ], + "files": [ + "dist/zorn.js", + "README.md", + "LICENSE" + ], + "scripts": { + "start": "concurrently \"http-server ./test/\" \"node esbuild.watch.config.mjs\"", + "build": "node esbuild.config.mjs" + }, + "devDependencies": { + "concurrently": "^8.2.2", + "esbuild": "^0.20.1", + "esbuild-plugin-svg": "^0.1.0", + "http-server": "^14.1.1", + "sass": "^1.71.1" + } +} \ No newline at end of file diff --git a/src/assets/icons/backward15-seconds.svg b/src/assets/icons/backward15-seconds.svg new file mode 100644 index 0000000..a1a61bd --- /dev/null +++ b/src/assets/icons/backward15-seconds.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/closed-captions-tag.svg b/src/assets/icons/closed-captions-tag.svg new file mode 100644 index 0000000..b464861 --- /dev/null +++ b/src/assets/icons/closed-captions-tag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/forward-solid.svg b/src/assets/icons/forward-solid.svg new file mode 100644 index 0000000..8edb35a --- /dev/null +++ b/src/assets/icons/forward-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/forward15-seconds.svg b/src/assets/icons/forward15-seconds.svg new file mode 100644 index 0000000..f060a7c --- /dev/null +++ b/src/assets/icons/forward15-seconds.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/maximize.svg b/src/assets/icons/maximize.svg new file mode 100644 index 0000000..6a34a02 --- /dev/null +++ b/src/assets/icons/maximize.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/pause-solid.svg b/src/assets/icons/pause-solid.svg new file mode 100644 index 0000000..f4c5d3a --- /dev/null +++ b/src/assets/icons/pause-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/play-solid.svg b/src/assets/icons/play-solid.svg new file mode 100644 index 0000000..395e8b1 --- /dev/null +++ b/src/assets/icons/play-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/refresh-double.svg b/src/assets/icons/refresh-double.svg new file mode 100644 index 0000000..2434767 --- /dev/null +++ b/src/assets/icons/refresh-double.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/rewind-solid.svg b/src/assets/icons/rewind-solid.svg new file mode 100644 index 0000000..f3c1c16 --- /dev/null +++ b/src/assets/icons/rewind-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/sound-high.svg b/src/assets/icons/sound-high.svg new file mode 100644 index 0000000..b5bfabe --- /dev/null +++ b/src/assets/icons/sound-high.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/sound-min.svg b/src/assets/icons/sound-min.svg new file mode 100644 index 0000000..c9c65f5 --- /dev/null +++ b/src/assets/icons/sound-min.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/sound-off.svg b/src/assets/icons/sound-off.svg new file mode 100644 index 0000000..402aea8 --- /dev/null +++ b/src/assets/icons/sound-off.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/dialogs/Buffering.js b/src/dialogs/Buffering.js new file mode 100644 index 0000000..23cdd2f --- /dev/null +++ b/src/dialogs/Buffering.js @@ -0,0 +1,9 @@ +import { + RefreshIcon, +} from '../get' + +export var BufferDialog = ` +
+ ${RefreshIcon} +
+` \ No newline at end of file diff --git a/src/events.js b/src/events.js new file mode 100644 index 0000000..2258ac1 --- /dev/null +++ b/src/events.js @@ -0,0 +1,24 @@ +import { ZornVideoPlayer } from "./get" + +export function Events() { + ZornVideoPlayer.addEventListener('error', function(event) { + document.querySelector('#invalid-src').style.display = 'inherit' + document.querySelector('.zorn-player-controls').style.display = 'none' + videoContainer.style.backgroundColor = '#101010' + setTimeout(() => { + ZornVideoPlayer.style.opacity = '0.10' + document.querySelector('#buffering').style.display = 'none' + }, 0o250); + }, true) + + ZornVideoPlayer.onwaiting = (event) => { + document.querySelector('#buffering').style.display = 'inherit' + ZornVideoPlayer.style.transition = '5s opacity' + ZornVideoPlayer.style.opacity = '0.25' + } + ZornVideoPlayer.oncanplaythrough = (event) => { + document.querySelector('#buffering').style.display = 'none' + ZornVideoPlayer.style.transition = '0.3s opacity' + ZornVideoPlayer.style.opacity = '1' + } +} \ No newline at end of file diff --git a/src/functions/Fullscreen.js b/src/functions/Fullscreen.js new file mode 100644 index 0000000..2f51e3b --- /dev/null +++ b/src/functions/Fullscreen.js @@ -0,0 +1,25 @@ +import { VideoContainer } from '../get' + + +export function Fullscreen() { + const Button_Fullscreen = document.getElementById('fullscreen') + function Toggle_Fullscreen() { + if (document.fullscreenElement) {document.exitFullscreen()} + else if (document.webkitFullscreenElement) {document.webkitExitFullscreen()} + else if (VideoContainer.webkitRequestFullscreen) {VideoContainer.webkitRequestFullscreen()} + else {VideoContainer.requestFullscreen()} + } + + Button_Fullscreen.onclick = Toggle_Fullscreen + function Update_FullscreenButton() { + if (document.fullscreenElement) { + Button_Fullscreen.setAttribute('data-title', 'Exit full screen (f)') + Button_Fullscreen.innerHTML = `` + } else { + Button_Fullscreen.setAttribute('data-title', 'Full screen (f)') + Button_Fullscreen.innerHTML = `` + } + } + + VideoContainer.addEventListener('fullscreenchange', Update_FullscreenButton) +} \ No newline at end of file diff --git a/src/functions/PlayPause.js b/src/functions/PlayPause.js new file mode 100644 index 0000000..752a3fe --- /dev/null +++ b/src/functions/PlayPause.js @@ -0,0 +1,34 @@ +import { + ZornVideoPlayer, + PlayIcon, + PauseIcon +} from '../get' + + +export function PlayPause() { + const Button_PlayPause = document.querySelector('.zorn-player-controls #play-pause') + + Button_PlayPause.addEventListener('click', Toggle_PlayPause) + ZornVideoPlayer.addEventListener('click', Toggle_PlayPause) + ZornVideoPlayer.addEventListener('play', Update_PlayPauseButton) + ZornVideoPlayer.addEventListener('pause', Update_PlayPauseButton) + + function Toggle_PlayPause() { + if (ZornVideoPlayer.paused || ZornVideoPlayer.ended) { + ZornVideoPlayer.play() + } + else { + ZornVideoPlayer.pause() + } + } + + function Update_PlayPauseButton() { + if (ZornVideoPlayer.paused) { + Button_PlayPause.setAttribute('data-title', 'Play (K)') + Button_PlayPause.innerHTML = `${PlayIcon}` + } else { + Button_PlayPause.setAttribute('data-title', 'Pause (K)') + Button_PlayPause.innerHTML = `${PauseIcon}` + } + } +} \ No newline at end of file diff --git a/src/functions/Seek.js b/src/functions/Seek.js new file mode 100644 index 0000000..92e1cb9 --- /dev/null +++ b/src/functions/Seek.js @@ -0,0 +1,74 @@ +import { ZornVideoPlayer } from '../get' + + +export function Seek() { + // Duration and Length of Video + const timeElapsed = document.getElementById('time-elapsed'); + const duration = document.getElementById('duration'); + + function formatTime(timeInSeconds) { + const result = new Date(timeInSeconds * 1000).toISOString().substr(11, 8); + + return { + minutes: result.substr(3, 2), + seconds: result.substr(6, 2), + }; + }; + + + function initializeVideo() { + const videoDuration = Math.round(ZornVideoPlayer.duration); + const time = formatTime(videoDuration); + duration.innerText = `${time.minutes}:${time.seconds}`; + duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`); + } + ZornVideoPlayer.addEventListener('loadedmetadata', initializeVideo); + function updateTimeElapsed() { + const time = formatTime(Math.round(ZornVideoPlayer.currentTime)); + timeElapsed.innerText = `${time.minutes}:${time.seconds}`; + timeElapsed.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`); + } + ZornVideoPlayer.addEventListener('timeupdate', updateTimeElapsed); + + // Progress Bar + const progressBar = document.getElementById('progress-bar'); + const seek = document.getElementById('seek'); + function initializeVideo() { + const videoDuration = Math.round(ZornVideoPlayer.duration); + seek.setAttribute('max', videoDuration); + progressBar.setAttribute('max', videoDuration); + const time = formatTime(videoDuration); + duration.innerText = `${time.minutes}:${time.seconds}`; + duration.setAttribute('datetime', `${time.minutes}m ${time.seconds}s`) + } + + function updateProgress() { + seek.value = Math.floor(ZornVideoPlayer.currentTime); + progressBar.value = Math.floor(ZornVideoPlayer.currentTime); + } + + ZornVideoPlayer.addEventListener('timeupdate', updateProgress); + + const seekTooltip = document.getElementById('seek-tooltip'); + + function updateSeekTooltip(event) { + const skipTo = Math.round((event.offsetX / event.target.clientWidth) * parseInt(event.target.getAttribute('max'), 10)); + seek.setAttribute('data-seek', skipTo) + const t = formatTime(skipTo); + seekTooltip.textContent = `${t.minutes}:${t.seconds}`; + const rect = ZornVideoPlayer.getBoundingClientRect(); + seekTooltip.style.left = `${event.pageX - rect.left}px`; + } + + seek.addEventListener('mousemove', updateSeekTooltip); + + function skipAhead(event) { + const skipTo = event.target.dataset.seek ? event.target.dataset.seek : event.target.value; + ZornVideoPlayer.currentTime = skipTo; + progressBar.value = skipTo; + seek.value = skipTo; + } + seek.addEventListener('input', skipAhead); + + initializeVideo() +} \ No newline at end of file diff --git a/src/functions/SkipAround.js b/src/functions/SkipAround.js new file mode 100644 index 0000000..8024766 --- /dev/null +++ b/src/functions/SkipAround.js @@ -0,0 +1,16 @@ +import { ZornVideoPlayer } from '../get' + +export function SkipAround() { + const Button_SkipBack = document.querySelector('.zorn-player-controls #skip-back') + const Button_SkipForth = document.querySelector('.zorn-player-controls #skip-forth') + + Button_SkipBack.addEventListener('click', Toggle_SkipBack) + Button_SkipForth.addEventListener('click', Toggle_SkipForth) + + function Toggle_SkipBack() {Skip(-10)} + function Toggle_SkipForth() {Skip(10)} + + function Skip(value) { + ZornVideoPlayer.currentTime += value + } +} \ No newline at end of file diff --git a/src/functions/Subtitles.js b/src/functions/Subtitles.js new file mode 100644 index 0000000..4d3b204 --- /dev/null +++ b/src/functions/Subtitles.js @@ -0,0 +1,51 @@ +import { VideoContainer, ZornVideoPlayer } from '../get' + + +export function Subtitles() { + var subtitles = document.querySelector('.zorn-player-controls #subtitles') + var subtitleMenuButtons = [] + var createMenuItem = function(id, lang, label) { + var listItem = document.createElement('li') + var button = listItem.appendChild(document.createElement('button')) + button.setAttribute('id', id) + button.className = 'subtitles-button' + if (lang.length > 0) button.setAttribute('lang', lang) + button.value = label + button.setAttribute('data-state', 'inactive') + button.appendChild(document.createTextNode(label)) + button.addEventListener('click', function(e) { + subtitleMenuButtons.map(function(v, i, a) { + subtitleMenuButtons[i].setAttribute('data-state', 'inactive') + }) + var lang = this.getAttribute('lang') + for (var i = 0; i < ZornVideoPlayer.textTracks.length; i++) { + if (ZornVideoPlayer.textTracks[i].language == lang) { + ZornVideoPlayer.textTracks[i].mode = 'showing' + this.setAttribute('data-state', 'active') + } + else { + ZornVideoPlayer.textTracks[i].mode = 'hidden' + } + } + subtitlesMenu.style.display = 'none' + }) + subtitleMenuButtons.push(button) + return listItem + } + var subtitlesMenu + if (ZornVideoPlayer.textTracks) { + var df = document.createDocumentFragment() + var subtitlesMenu = df.appendChild(document.createElement('ul')) + subtitlesMenu.className = 'subtitles-menu' + subtitlesMenu.appendChild(createMenuItem('subtitles-off', '', 'Off')) + for (var i = 0; i < ZornVideoPlayer.textTracks.length; i++) { + subtitlesMenu.appendChild(createMenuItem('subtitles-' + ZornVideoPlayer.textTracks[i].language, ZornVideoPlayer.textTracks[i].language, ZornVideoPlayer.textTracks[i].label)) + } + VideoContainer.appendChild(subtitlesMenu) + } + subtitles.addEventListener('click', function(e) { + if (subtitlesMenu) { + subtitlesMenu.style.display = (subtitlesMenu.style.display == 'block' ? 'none' : 'block') + } + }) +} \ No newline at end of file diff --git a/src/functions/Volume.js b/src/functions/Volume.js new file mode 100644 index 0000000..a0fa946 --- /dev/null +++ b/src/functions/Volume.js @@ -0,0 +1,46 @@ +import { + ZornVideoPlayer, + VolumeOffIcon, + VolumeMinIcon, + VolumeHighIcon +} from '../get' + +export function Volume() { + const Button_Volume = document.getElementById('volume-button') + const volume = document.getElementById('volume') + function Update_Volme() { + if (ZornVideoPlayer.muted) { + ZornVideoPlayer.muted = false + } + ZornVideoPlayer.volume = volume.value + } + + volume.addEventListener('input', Update_Volme) + + function Update_Volume_Icon() { + Button_Volume.setAttribute('data-title', 'Mute (M)') + + if (ZornVideoPlayer.muted || ZornVideoPlayer.volume === 0) { + Button_Volume.innerHTML = `${VolumeOffIcon}` + Button_Volume.setAttribute('data-title', 'Unmute (M)') + } else if (ZornVideoPlayer.volume > 0 && ZornVideoPlayer.volume <= 0.5) { + Button_Volume.innerHTML = `${VolumeMinIcon}` + } else { + Button_Volume.innerHTML = `${VolumeHighIcon}` + } + } + + ZornVideoPlayer.addEventListener('volumechange', Update_Volume_Icon) + + function Toggle_Mute() { + ZornVideoPlayer.muted = !ZornVideoPlayer.muted + + if (ZornVideoPlayer.muted) { + volume.setAttribute('data-volume', volume.value) + volume.value = 0 + } else { + volume.value = volume.dataset.volume + } + } + Button_Volume.addEventListener('click', Toggle_Mute) +} \ No newline at end of file diff --git a/src/get.js b/src/get.js new file mode 100644 index 0000000..9e51c5f --- /dev/null +++ b/src/get.js @@ -0,0 +1,28 @@ +// Set Variables for required video container and video player +export var ZornVideoPlayer = document.querySelector('.zorn-player') +export var VideoContainer = document.querySelector('.video-container') + +// Icons - Iconoir.com +/// Get Icons +import PlaySVG from './assets/icons/play-solid.svg' +import PauseSVG from './assets/icons/pause-solid.svg' +import FullcreenSVG from './assets/icons/maximize.svg' +import CaptionsSVG from './assets/icons/closed-captions-tag.svg' +import Backward15SVG from './assets/icons/backward15-seconds.svg' +import Forward15SVG from './assets/icons/forward15-seconds.svg' +import VolumeHighSVG from './assets/icons/sound-high.svg' +import VolumeMinSVG from './assets/icons/sound-min.svg' +import VolumeOffSVG from './assets/icons/sound-off.svg' +import RefreshSVG from './assets/icons/refresh-double.svg' + +/// Set Icons +export var PlayIcon = PlaySVG +export var PauseIcon = PauseSVG +export var FullcreenIcon = FullcreenSVG +export var CaptionsIcon = CaptionsSVG +export var Backward15Icon = Backward15SVG +export var Forward15Icon = Forward15SVG +export var VolumeHighIcon = VolumeHighSVG +export var VolumeMinIcon = VolumeMinSVG +export var VolumeOffIcon = VolumeOffSVG +export var RefreshIcon = RefreshSVG \ No newline at end of file diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..300af4b --- /dev/null +++ b/src/index.js @@ -0,0 +1,26 @@ +import { ZornVideoPlayer } from "./get" +import { Controls } from "./themes/default" + +// Import Functions +import { Events } from './events' +import { PlayPause } from "./functions/PlayPause" +import { SkipAround } from "./functions/SkipAround" +import { Fullscreen } from "./functions/Fullscreen" +import { Subtitles } from "./functions/Subtitles" +import { Volume } from "./functions/Volume" +import { Seek } from "./functions/Seek" +import { BufferDialog } from "./dialogs/Buffering" + +// Apply Controls +ZornVideoPlayer.insertAdjacentHTML("afterend", Controls) +ZornVideoPlayer.insertAdjacentHTML("afterend", BufferDialog) + +// Init Functions +Events() +PlayPause() +SkipAround() +Fullscreen() +Subtitles() +Volume() +Seek() +Buffering() \ No newline at end of file diff --git a/src/themes/default.js b/src/themes/default.js new file mode 100644 index 0000000..2477d69 --- /dev/null +++ b/src/themes/default.js @@ -0,0 +1,365 @@ +import { + PlayIcon, + PauseIcon, + FullcreenIcon, + CaptionsIcon, + Backward15Icon, + Forward15Icon, + VolumeHighIcon +} from '../get' + +export var Controls = ` +

+
+
+
+ + +
00:00
+
+
+
+
+
+ + +
+
+ + / + +
+
+
+ + + +
+
+ + +
+
+
+ +` \ No newline at end of file diff --git a/test/css/index.css b/test/css/index.css new file mode 100644 index 0000000..a72ad60 --- /dev/null +++ b/test/css/index.css @@ -0,0 +1,4 @@ +body { + background-color: #111111; + color: white; +} \ No newline at end of file diff --git a/test/css/index.scss b/test/css/index.scss new file mode 100644 index 0000000..e69de29 diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..8eaa04c --- /dev/null +++ b/test/index.html @@ -0,0 +1,19 @@ + + + + Zorn + + + +
+ +
+ + + \ No newline at end of file diff --git a/test/js/zorn.js b/test/js/zorn.js new file mode 100644 index 0000000..351e1d2 --- /dev/null +++ b/test/js/zorn.js @@ -0,0 +1,359 @@ +(()=>{var x='';var b='';var y='';var w='';var C='';var z='';var M='';var V='';var L='';var T='';var e=document.querySelector(".zorn-player"),p=document.querySelector(".video-container"),g=x,E=b,B=y,I=w,S=C,H=z,h=M,F=V,P=L,$=T;var j=` +

+
+
+
+ + +
00:00
+
+
+
+
+
+ + +
+
+ + / + +
+
+
+ + + +
+
+ + +
+
+
+ +`;function A(){e.addEventListener("error",function(t){document.querySelector("#invalid-src").style.display="inherit",document.querySelector(".zorn-player-controls").style.display="none",videoContainer.style.backgroundColor="#101010",setTimeout(()=>{e.style.opacity="0.10",document.querySelector("#buffering").style.display="none"},168)},!0),e.onwaiting=t=>{document.querySelector("#buffering").style.display="inherit",e.style.transition="5s opacity",e.style.opacity="0.25"},e.oncanplaythrough=t=>{document.querySelector("#buffering").style.display="none",e.style.transition="0.3s opacity",e.style.opacity="1"}}function U(){let t=document.querySelector(".zorn-player-controls #play-pause");t.addEventListener("click",r),e.addEventListener("click",r),e.addEventListener("play",i),e.addEventListener("pause",i);function r(){e.paused||e.ended?e.play():e.pause()}function i(){e.paused?(t.setAttribute("data-title","Play (K)"),t.innerHTML=`${g}`):(t.setAttribute("data-title","Pause (K)"),t.innerHTML=`${E}`)}}function Z(){let t=document.querySelector(".zorn-player-controls #skip-back"),r=document.querySelector(".zorn-player-controls #skip-forth");t.addEventListener("click",i),r.addEventListener("click",a);function i(){d(-10)}function a(){d(10)}function d(s){e.currentTime+=s}}function _(){let t=document.getElementById("fullscreen");function r(){document.fullscreenElement?document.exitFullscreen():document.webkitFullscreenElement?document.webkitExitFullscreen():p.webkitRequestFullscreen?p.webkitRequestFullscreen():p.requestFullscreen()}t.onclick=r;function i(){document.fullscreenElement?(t.setAttribute("data-title","Exit full screen (f)"),t.innerHTML=''):(t.setAttribute("data-title","Full screen (f)"),t.innerHTML='')}p.addEventListener("fullscreenchange",i)}function q(){var t=document.querySelector(".zorn-player-controls #subtitles"),r=[],i=function(c,k,u){var m=document.createElement("li"),l=m.appendChild(document.createElement("button"));return l.setAttribute("id",c),l.className="subtitles-button",k.length>0&&l.setAttribute("lang",k),l.value=u,l.setAttribute("data-state","inactive"),l.appendChild(document.createTextNode(u)),l.addEventListener("click",function(o){r.map(function(v,O,oe){r[O].setAttribute("data-state","inactive")});for(var n=this.getAttribute("lang"),f=0;f0&&e.volume<=.5?t.innerHTML=`${F}`:t.innerHTML=`${h}`}e.addEventListener("volumechange",a);function d(){e.muted=!e.muted,e.muted?(r.setAttribute("data-volume",r.value),r.value=0):r.value=r.dataset.volume}t.addEventListener("click",d)}function D(){let t=document.getElementById("time-elapsed"),r=document.getElementById("duration");function i(o){let n=new Date(o*1e3).toISOString().substr(11,8);return{minutes:n.substr(3,2),seconds:n.substr(6,2)}}e.addEventListener("loadedmetadata",c);function a(){let o=i(Math.round(e.currentTime));t.innerText=`${o.minutes}:${o.seconds}`,t.setAttribute("datetime",`${o.minutes}m ${o.seconds}s`)}e.addEventListener("timeupdate",a);let d=document.getElementById("progress-bar"),s=document.getElementById("seek");function c(){let o=Math.round(e.duration);s.setAttribute("max",o),d.setAttribute("max",o);let n=i(o);r.innerText=`${n.minutes}:${n.seconds}`,r.setAttribute("datetime",`${n.minutes}m ${n.seconds}s`)}function k(){s.value=Math.floor(e.currentTime),d.value=Math.floor(e.currentTime)}e.addEventListener("timeupdate",k);let u=document.getElementById("seek-tooltip");function m(o){let n=Math.round(o.offsetX/o.target.clientWidth*parseInt(o.target.getAttribute("max"),10));s.setAttribute("data-seek",n);let f=i(n);u.textContent=`${f.minutes}:${f.seconds}`;let v=e.getBoundingClientRect();u.style.left=`${o.pageX-v.left}px`}s.addEventListener("mousemove",m);function l(o){let n=o.target.dataset.seek?o.target.dataset.seek:o.target.value;e.currentTime=n,d.value=n,s.value=n}s.addEventListener("input",l),c()}var R=` +
+ ${$} +
+`;e.insertAdjacentHTML("afterend",j);e.insertAdjacentHTML("afterend",R);A();U();Z();_();q();G();D();Buffering();})(); diff --git a/test/media/Ennie-and-Yoyki-Ungirly-Games.mp4 b/test/media/Ennie-and-Yoyki-Ungirly-Games.mp4 new file mode 100644 index 0000000..0e19984 Binary files /dev/null and b/test/media/Ennie-and-Yoyki-Ungirly-Games.mp4 differ diff --git a/test/media/subtitles/English.vtt b/test/media/subtitles/English.vtt new file mode 100644 index 0000000..70e901c --- /dev/null +++ b/test/media/subtitles/English.vtt @@ -0,0 +1,350 @@ +WEBVTT + +00:00:00.650 --> 00:00:03.030 +[music] + +00:00:03.030 --> 00:00:05.220 +[applause] + +00:00:05.220 --> 00:00:13.880 +[music] [ + +00:00:17.060 --> 00:00:20.750 +music] + +00:00:20.750 --> 00:00:21.930 +bye [ + +00:00:21.930 --> 00:00:24.430 +music] + +00:00:24.430 --> 00:00:26.460 + + +00:00:36.720 --> 00:00:40.380 +why worry, I’m on the mattress + +00:00:40.380 --> 00:00:41.540 +[music] + +00:00:41.540 --> 00:00:43.040 +[applause] [ + +00:00:43.040 --> 00:00:46.180 +music] + +00:00:47.340 --> 00:00:51.030 +this doesn’t happen here + +00:00:56.340 --> 00:00:59.479 +[music] + +00:01:01.260 --> 00:01:04.479 +now if she grabs you she won’t + +00:01:04.479 --> 00:01:08.130 +catch up with me she ki she’s old, + +00:01:08.130 --> 00:01:11.220 +look at + +00:01:13.110 --> 00:01:15.720 +you, + +00:01:15.720 --> 00:01:18.370 +I’m buying you myself now + +00:01:18.370 --> 00:01:21.579 +[music] + +00:01:30.750 --> 00:01:33.970 +what kind of a mess do you have here, it looks like we’re on + +00:01:33.970 --> 00:01:36.790 +that full mom, okay, something came + +00:01:36.790 --> 00:01:40.090 +and all the water came through the door, you’re like, and I almost + +00:01:40.090 --> 00:01:43.420 +drowned, okay, I need to get ready for + +00:01:43.420 --> 00:01:45.939 +grandma's arrival and when you've tidied up, + +00:01:45.939 --> 00:01:48.430 +play some quiet game + +00:01:48.430 --> 00:01:51.460 +okay mom, we'll clean everything up now and wo + +00:01:51.460 --> 00:01:53.259 +n't do this again by the way, have you seen + +00:01:53.259 --> 00:01:55.730 +grandma's soft slippers + +00:01:55.730 --> 00:01:57.750 +[music] haven't you seen it + +00:01:57.750 --> 00:02:01.850 +in years? I + +00:02:01.850 --> 00:02:05.460 +don't want to play a quieter game, how boring is it + +00:02:05.460 --> 00:02:09.179 +mom? she said it means we need + +00:02:09.179 --> 00:02:12.000 +to help and not upset her, + +00:02:12.000 --> 00:02:14.970 +I know one interesting calm game but + +00:02:14.970 --> 00:02:18.570 +what but but we will play it if only + +00:02:18.570 --> 00:02:20.580 +you swear not to tell anyone I + +00:02:20.580 --> 00:02:23.700 +swear why can’t you + +00:02:23.700 --> 00:02:25.320 +tell anyone because + +00:02:25.320 --> 00:02:28.410 +boys don’t play such games like this + +00:02:28.410 --> 00:02:32.450 +how is it considered that she is girly, what is it like + +00:02:32.450 --> 00:02:36.720 +for girls, what happens Igor + +00:02:36.720 --> 00:02:39.360 +especially for girls, this was + +00:02:39.360 --> 00:02:42.209 +lying around, I didn’t notice before that in the + +00:02:42.209 --> 00:02:44.630 +girl’s plan, she will see the light + +00:02:44.630 --> 00:02:55.130 +[music] + +00:02:55.130 --> 00:02:58.890 +she ki, don’t break the children’s bed, otherwise where + +00:02:58.890 --> 00:03:01.380 +will your brothers sleep and please + +00:03:01.380 --> 00:03:05.930 +sit down for dinner We’ve been calling you for a long time, something + +00:03:07.340 --> 00:03:09.860 +useful, + +00:03:09.860 --> 00:03:12.780 +but it’s more reliable, baby, you can + +00:03:12.780 --> 00:03:15.090 +order another dish, no, whatever they + +00:03:15.090 --> 00:03:19.200 +’ve prepared, eat it for me, but it’s better that + +00:03:19.200 --> 00:03:21.390 +I’ll be a dad, who work like this all my life as a + +00:03:21.390 --> 00:03:23.600 +child and a + +00:03:23.600 --> 00:03:26.150 +dad, + +00:03:26.150 --> 00:03:29.730 +the most useful ones for you go to this chicane, it + +00:03:29.730 --> 00:03:33.330 +contains a lot of vitamin C and vitamin to + +00:03:33.330 --> 00:03:38.030 +vitamin A and bubbles or nuts that the + +00:03:38.030 --> 00:03:40.380 +need to run around is all + +00:03:40.380 --> 00:03:43.260 +wrong in the newspaper it’s written mom didn’t + +00:03:43.260 --> 00:03:45.830 + + +00:03:45.830 --> 00:03:49.710 +come up with it myself and now it’s time for the kids to go to bed + +00:03:49.710 --> 00:03:58.790 +[music] + +00:03:58.790 --> 00:04:02.220 +dad who puts the kids to bed like that, + +00:04:02.220 --> 00:04:05.910 +they’ll have a concussion duo and I + +00:04:05.910 --> 00:04:09.390 +think they like it what kind of friend is the media of the same + +00:04:09.390 --> 00:04:11.910 +soul where are all the children who should you play with + +00:04:11.910 --> 00:04:13.730 +dear + +00:04:13.730 --> 00:04:16.500 +ladies let's go visit our neighbors + +00:04:16.500 --> 00:04:20.190 +there will probably be a couple of kids there it's + +00:04:20.190 --> 00:04:22.440 +good that the children fell asleep and mom didn't say + +00:04:22.440 --> 00:04:24.540 +finally we can have a cup of + +00:04:24.540 --> 00:04:26.900 +coffee + +00:04:28.350 --> 00:04:35.839 +[music] + +00:04:38.090 --> 00:04:40.520 +no + +00:04:40.520 --> 00:04:44.310 +it's yours and not a hat hat yeah but a + +00:04:44.310 --> 00:04:45.150 +hat + +00:04:45.150 --> 00:04:47.000 +[music] got it + +00:04:47.000 --> 00:04:50.330 +win + +00:04:50.330 --> 00:04:53.840 +watching the cars you + +00:04:53.840 --> 00:04:57.210 +’ll say it’s also girly and you try sewing a + +00:04:57.210 --> 00:05:00.720 +telegraph yourself a real man’s job yes yes the + +00:05:00.720 --> 00:05:04.169 +girls can’t do it so you know how to + +00:05:04.169 --> 00:05:05.720 +sew yourself + +00:05:05.720 --> 00:05:08.900 +[music] + +00:05:08.900 --> 00:05:11.730 +writing they just came to play with you and + +00:05:11.730 --> 00:05:14.430 +you later come in, our children are sleeping under the + +00:05:14.430 --> 00:05:18.200 +cup, well, let it be yours, but it’s quiet, + +00:05:18.200 --> 00:05:21.090 +why is it quiet, let’s wake up + +00:05:21.090 --> 00:05:23.250 +your game and they will play loudly together, + +00:05:23.250 --> 00:05:26.570 +but they just fell asleep, + +00:05:26.570 --> 00:05:30.210 +it’s you boys and girls, it’s our + +00:05:30.210 --> 00:05:32.090 +twins, + +00:05:32.090 --> 00:05:35.870 +why did anyone drink something important, it’s that they have a + +00:05:35.870 --> 00:05:38.860 +concussion brain happened + +00:05:38.860 --> 00:05:41.660 +useless of you parents for children + +00:05:41.660 --> 00:05:45.250 +ingots and it is necessary and you are sitting here drinking tea, it’s + +00:05:45.250 --> 00:05:49.040 +not true but a good family and not the + +00:05:49.040 --> 00:05:50.380 +best mother in the world + +00:05:50.380 --> 00:05:53.860 +[music] + +00:05:53.860 --> 00:05:58.160 +not me very much even without let me pour you + +00:05:58.160 --> 00:06:01.220 +some tea and jetta my couple + +00:06:01.220 --> 00:06:04.580 +will solve it when it’s time for me to go home, well, they are harmful + +00:06:04.580 --> 00:06:06.790 +, + +00:06:10.640 --> 00:06:13.690 +[music] + +00:06:14.410 --> 00:06:17.830 +well, that’s it + +00:06:21.310 --> 00:06:24.389 +[music] [ + +00:06:28.690 --> 00:06:49.230 +music] + +00:06:49.230 --> 00:06:52.570 +you guys have another flood, I + +00:06:52.570 --> 00:06:56.910 +let him out, sorry mom, he started + +00:06:56.910 --> 00:06:59.860 +saving our twins again, + +00:06:59.860 --> 00:07:03.490 +oh well done, what kind of grandmother’s daddies found + +00:07:03.490 --> 00:07:07.650 +she just arrived + +00:07:11.310 --> 00:07:14.540 +[ music] + +00:07:17.430 --> 00:07:18.940 +boys + +00:07:18.940 --> 00:07:39.520 +[music] + diff --git a/test/media/subtitles/Russian.vtt b/test/media/subtitles/Russian.vtt new file mode 100644 index 0000000..7b4228f --- /dev/null +++ b/test/media/subtitles/Russian.vtt @@ -0,0 +1,352 @@ +WEBVTT +Kind: captions +Language: ru + +00:00:00.650 --> 00:00:03.030 +[музыка] + +00:00:03.030 --> 00:00:05.220 +[аплодисменты] + +00:00:05.220 --> 00:00:17.060 +[музыка] + +00:00:17.060 --> 00:00:20.750 +[музыка] + +00:00:20.750 --> 00:00:21.930 +пока + +00:00:21.930 --> 00:00:24.430 +[музыка] + +00:00:24.430 --> 00:00:36.720 +а + +00:00:36.720 --> 00:00:40.380 +чего волноваться то я же на матрасе + +00:00:40.380 --> 00:00:41.540 +[музыка] + +00:00:41.540 --> 00:00:43.040 +[аплодисменты] + +00:00:43.040 --> 00:00:47.340 +[музыка] + +00:00:47.340 --> 00:00:56.340 +тут такого не водится + +00:00:56.340 --> 00:01:01.260 +[музыка] + +00:01:01.260 --> 00:01:04.479 +сейчас как цапнет тебя она меня не + +00:01:04.479 --> 00:01:08.130 +догонит ей ки она же старая + +00:01:08.130 --> 00:01:13.110 +посмотри на + +00:01:13.110 --> 00:01:15.720 +тебе + +00:01:15.720 --> 00:01:18.370 +я тебя сам сейчас покупаю + +00:01:18.370 --> 00:01:30.750 +[музыка] + +00:01:30.750 --> 00:01:33.970 +что это у вас тут за беспорядок виду у + +00:01:33.970 --> 00:01:36.790 +нас по ту full мама хорошо что-то пришло + +00:01:36.790 --> 00:01:40.090 +и вся вода через дверь вы типа и я чуть + +00:01:40.090 --> 00:01:43.420 +не утонул ну ладно мне надо готовиться к + +00:01:43.420 --> 00:01:45.939 +приезду бабушки а вы когда приберетесь + +00:01:45.939 --> 00:01:48.430 +поиграйте в какую-нибудь спокойную игру + +00:01:48.430 --> 00:01:51.460 +хорошо мам мы сейчас все уберем и больше + +00:01:51.460 --> 00:01:53.259 +так не будем кстати вы не видели + +00:01:53.259 --> 00:01:55.730 +бабушкина мягкие тапочки + +00:01:55.730 --> 00:01:57.750 +[музыка] + +00:01:57.750 --> 00:02:01.850 +не лет не видели ли + +00:02:01.850 --> 00:02:05.460 +не хочу играть спокойнее игры скукотищу + +00:02:05.460 --> 00:02:09.179 +какая яки раз мама сказала значит надо + +00:02:09.179 --> 00:02:12.000 +сделать помогли и не расстраивать ее вот + +00:02:12.000 --> 00:02:14.970 +я знаю одну интересную спокойную игру но + +00:02:14.970 --> 00:02:18.570 +что но но мы в неё поиграем если только + +00:02:18.570 --> 00:02:20.580 +ты поклянешься никому они не + +00:02:20.580 --> 00:02:23.700 +рассказывать клянусь а почему нельзя + +00:02:23.700 --> 00:02:25.320 +никому рассказывать потому что а + +00:02:25.320 --> 00:02:28.410 +мальчишки в такие игры не играют как это + +00:02:28.410 --> 00:02:32.450 +вроде как считается что она девчачья + +00:02:32.450 --> 00:02:36.720 +какая ну для девочек что бывает игорь + +00:02:36.720 --> 00:02:39.360 +специально для девочек вот это было + +00:02:39.360 --> 00:02:42.209 +валяет его раньше не замечал что в плана + +00:02:42.209 --> 00:02:44.630 +девчачьи на свет увидит + +00:02:44.630 --> 00:02:55.130 +[музыка] + +00:02:55.130 --> 00:02:58.890 +ей ки не ломать детям кровать а то где + +00:02:58.890 --> 00:03:01.380 +будут спать твои братики и пожалуйста + +00:03:01.380 --> 00:03:07.340 +садись обедать мы тебя уже давно зовем + +00:03:07.340 --> 00:03:09.860 +полезное около + +00:03:09.860 --> 00:03:12.780 +но надёжней младенец вообще можно + +00:03:12.780 --> 00:03:15.090 +заказать другое блюдо нет что + +00:03:15.090 --> 00:03:19.200 +приготовили то и ешь мне да вот уж лучше + +00:03:19.200 --> 00:03:21.390 +я папой буду которые так всю жизнь + +00:03:21.390 --> 00:03:23.600 +ребенком работаю и + +00:03:23.600 --> 00:03:26.150 +папой + +00:03:26.150 --> 00:03:29.730 +самые полезные к вы идете это шикана + +00:03:29.730 --> 00:03:33.330 +много содержится витамин с и витамин к + +00:03:33.330 --> 00:03:38.030 +витамин а и пузырики или орешка что + +00:03:38.030 --> 00:03:40.380 +необходимость бегать по это все + +00:03:40.380 --> 00:03:43.260 +неправильно в газете написано мама не + +00:03:43.260 --> 00:03:45.830 +сам же я придумал а + +00:03:45.830 --> 00:03:49.710 +теперь малыши пора ложиться спать + +00:03:49.710 --> 00:03:58.790 +[музыка] + +00:03:58.790 --> 00:04:02.220 +папа кто же так детей спать укладывает у + +00:04:02.220 --> 00:04:05.910 +них же сотрясение мозга будет duo а я + +00:04:05.910 --> 00:04:09.390 +думаю нравится какая другу сми единой + +00:04:09.390 --> 00:04:11.910 +души где все дети с кем тебе поиграть + +00:04:11.910 --> 00:04:13.730 +милые + +00:04:13.730 --> 00:04:16.500 +дамы к зайдем в гости к нашим соседям + +00:04:16.500 --> 00:04:20.190 +там наверняка найдется пара детишек как + +00:04:20.190 --> 00:04:22.440 +хорошо что дети уснули и не говорила + +00:04:22.440 --> 00:04:24.540 +мама наконец-то можно выпить чашечку + +00:04:24.540 --> 00:04:28.350 +кофе + +00:04:28.350 --> 00:04:38.090 +[музыка] + +00:04:38.090 --> 00:04:40.520 +нет + +00:04:40.520 --> 00:04:44.310 +это у тебя и не шляпка шляпка ага а + +00:04:44.310 --> 00:04:45.150 +шляпка + +00:04:45.150 --> 00:04:47.000 +[музыка] + +00:04:47.000 --> 00:04:50.330 +поняла выиграть + +00:04:50.330 --> 00:04:53.840 +смотря машин + +00:04:53.840 --> 00:04:57.210 +скажешь тоже девчачью а ты попробуй сама + +00:04:57.210 --> 00:05:00.720 +сшей телеграф настоящая мужская работа + +00:05:00.720 --> 00:05:04.169 +да да девчонкам это не по силам вот ты + +00:05:04.169 --> 00:05:05.720 +сама шить на умеешь + +00:05:05.720 --> 00:05:08.900 +[музыка] + +00:05:08.900 --> 00:05:11.730 +написание к вам поиграть зашли просто а + +00:05:11.730 --> 00:05:14.430 +вы попозже заходите наши дети спят под + +00:05:14.430 --> 00:05:18.200 +cup ну и пусть она твоя играет но тихо + +00:05:18.200 --> 00:05:21.090 +почему это тихо давайте вы разбудите + +00:05:21.090 --> 00:05:23.250 +свои дичь и они вместе проиграет громко + +00:05:23.250 --> 00:05:26.570 +но они только уснули + +00:05:26.570 --> 00:05:30.210 +это у вас мальчики и девочки это у нас + +00:05:30.210 --> 00:05:32.090 +близнецы + +00:05:32.090 --> 00:05:35.870 +а почему любой выпили важный это у них + +00:05:35.870 --> 00:05:38.860 +сотрясение мозга случилось + +00:05:38.860 --> 00:05:41.660 +никудышные из вас родителей за детьми + +00:05:41.660 --> 00:05:45.250 +слитки и надо а вы тут сидите чай пьете + +00:05:45.250 --> 00:05:49.040 +неправда но хорошая семья а и не самая + +00:05:49.040 --> 00:05:50.380 +лучшая мама на свете + +00:05:50.380 --> 00:05:53.860 +[музыка] + +00:05:53.860 --> 00:05:58.160 +не я очень даже без давайте я вам чаю + +00:05:58.160 --> 00:06:01.220 +налью немного чай и jetta мой пара + +00:06:01.220 --> 00:06:04.580 +порешу когда мне домой пора ну и вредные + +00:06:04.580 --> 00:06:10.640 +, + +00:06:10.640 --> 00:06:14.410 +[музыка] + +00:06:14.410 --> 00:06:21.310 +ну все + +00:06:21.310 --> 00:06:28.690 +[музыка] + +00:06:28.690 --> 00:06:49.230 +[музыка] + +00:06:49.230 --> 00:06:52.570 +ребята у вас что опять потоп я же его + +00:06:52.570 --> 00:06:56.910 +выпустила прости мама он опять начался + +00:06:56.910 --> 00:06:59.860 +спасти и наших близнецов + +00:06:59.860 --> 00:07:03.490 +ай молодцы какие папочки бабушкины нашли + +00:07:03.490 --> 00:07:11.310 +она как раз только что приехала + +00:07:11.310 --> 00:07:17.430 +[музыка] + +00:07:17.430 --> 00:07:18.940 +мальчишки + +00:07:18.940 --> 00:07:39.520 +[музыка] +