pandora/static/mobile/js/VideoPlayer.js

428 lines
15 KiB
JavaScript
Raw Normal View History

(function() {
window.VideoPlayer = function(options) {
var self = {}, that;
self.options = {
autoplay: false,
controls: true,
items: [],
loop: false,
muted: false,
playbackRate: 1,
position: 0,
volume: 1
}
Object.assign(self.options, options);
that = VideoElement(options);
self.controls = document.createElement('div')
self.controls.classList.add('mx-controls')
2024-02-17 10:55:11 +00:00
if (options.poster) {
self.controls.classList.add('poster')
}
//self.controls.style.display = "none"
if (self.options.controls) {
var ratio = `aspect-ratio: ${self.options.aspectratio};`
if (!self.options.aspectratio) {
ratio = 'height: 128px;'
}
self.controls.innerHTML = `
<style>
.fullscreen video {
width: 100%;
height: 100%;
}
.mx-controls {
position: absolute;
top: 0px;
left: 0;
right: 0;
display: flex;
${ratio}
max-height: 100vh;
flex-direction: column;
background: rgba(0,0,0,0.3);
color: white;
z-index: 1;
margin: auto;
}
2024-02-17 10:55:11 +00:00
.mx-controls.poster {
background-image: url(${self.options.poster});
background-repeat: no-repeat;
background-size: contain;
background-blend-mode: overlay;
}
.mx-controls .toggle {
display: flex;
flex: 1;
}
.mx-controls .volume:hover,
.mx-controls .fullscreen-btn:hover,
.mx-controls .toggle:hover {
cursor: pointer;
}
.mx-controls .toggle div {
margin: auto;
}
.mx-controls .controls {
display: flex;
flex-direction: row;
width: 100%;
height: 32px;
}
.mx-controls .controls .position {
flex: 1;
}
.mx-controls .toggle svg {
width: 64px;
height: 64px;
}
.mx-controls .toggle .loading svg {
2023-08-23 19:01:33 +00:00
width: 64px;
height: 64px;
}
.mx-controls .controls .volume svg,
.mx-controls .controls .fullscreen-btn svg {
width: 24px;
height: 24px;
margin: 4px;
}
.mx-controls .controls .volume {
padding-left: 4px;
}
.mx-controls.hidden {
display: none;
}
.fullscreen .mx-controls {
height: 100vh;
width: 100%;
}
.fullscreen .mx-controls video {
width: 100%;
max-height: 100%;
max-width: 100%;
}
.fullscreen .mx-controls .controls {
height: 64px;
}
.fullscreen .mx-controls .fullscreen-btn {
padding: 16px;
}
.fullscreen .mx-controls .volume {
padding: 16px;
}
.mx-controls {
transition: opacity 0.3s linear;
}
.mx-controls .controls .position {
display: flex;
justify-content: center;
}
.mx-controls .controls .position .bar {
width: calc(100% - 16px);
height: 4px;
border: solid 1px #B1B1B1;
margin: auto;
}
.fullscreen .mx-controls .controls .position .bar {
}
.mx-controls .controls .position .progress {
width: 0;
background: #B1B1B180;
height: 4px;
}
.mx-controls .controls .time {
display: flex;
justify-content: center;
}
.mx-controls .controls .time div {
margin: auto;
font-size: 12px;
display: flex;
text-align: center;
}
</style>
<div class="toggle" title="Play">
<div>${icon.play}</div>
</div>
<div class="controls">
<div class="volume" title="Mute">
${icon.mute}
</div>
<div class="position">
2024-12-14 19:16:41 +00:00
<div class="seekbar">
<input type="range" value="0" min='0' max='100' step='.25' />
<div class="seekbar-progress">
<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="38" style="width: 0%;"></div>
</div>
</div>
</div>
<div class="time">
<div></div>
</div>
<div class="fullscreen-btn" title="Fullscreen">
${isIOS || !self.options.aspectratio ? "" : icon.enterFullscreen}
</div>
</div>
`
var toggleVideo = event => {
event.preventDefault()
event.stopPropagation()
if (that.paused) {
2024-02-17 10:55:11 +00:00
self.controls.classList.remove('poster')
that.play()
} else {
that.pause()
}
}
async function toggleFullscreen(event) {
if (isIOS) {
return
}
event.preventDefault()
event.stopPropagation()
if (!document.fullscreenElement) {
that.classList.add('fullscreen')
if (that.webkitRequestFullscreen) {
await that.webkitRequestFullscreen()
} else {
await that.requestFullscreen()
}
console.log('entered fullscreen')
var failed = false
if (!screen.orientation.type.startsWith("landscape")) {
await screen.orientation.lock("landscape").catch(err => {
console.log('no luck with lock', err)
/*
document.querySelector('.error').innerHTML = '' + err
that.classList.remove('fullscreen')
document.exitFullscreen();
screen.orientation.unlock()
failed = true
*/
})
}
if (that.paused && !failed) {
2024-02-17 10:55:11 +00:00
self.controls.classList.remove('poster')
that.play()
}
} else {
that.classList.remove('fullscreen')
document.exitFullscreen();
screen.orientation.unlock()
}
}
var toggleSound = event => {
event.preventDefault()
event.stopPropagation()
if (that.muted()) {
that.muted(false)
} else {
that.muted(true)
}
}
var showControls
2024-12-14 19:16:41 +00:00
function hideControlsLater() {
showControls = setTimeout(() => {
if (touching) {
hideControlsLater()
} else {
self.controls.style.opacity = that.paused ? '1' : '0'
showControls = null
}
}, 3000)
}
var toggleControls = event => {
2024-12-14 19:16:41 +00:00
if (event.target.tagName == "INPUT") {
if (showControls) {
clearTimeout(showControls)
}
return
}
if (self.controls.style.opacity == '0') {
event.preventDefault()
event.stopPropagation()
self.controls.style.opacity = '1'
2024-12-14 19:16:41 +00:00
hideControlsLater()
} else {
self.controls.style.opacity = '0'
}
}
self.controls.addEventListener("mousemove", event => {
if (showControls) {
clearTimeout(showControls)
}
self.controls.style.opacity = '1'
2024-12-14 19:16:41 +00:00
hideControlsLater()
})
self.controls.addEventListener("mouseleave", event => {
if (showControls) {
clearTimeout(showControls)
}
self.controls.style.opacity = that.paused ? '1' : '0'
showControls = null
})
2024-12-14 19:16:41 +00:00
self.controls.addEventListener("touchstart", event => {
touching = true
})
self.controls.addEventListener("touchstart", toggleControls)
2024-12-14 19:16:41 +00:00
self.controls.addEventListener("touchend", event => {
touching = false
})
self.controls.querySelector('.toggle').addEventListener("click", toggleVideo)
self.controls.querySelector('.volume').addEventListener("click", toggleSound)
self.controls.querySelector('.fullscreen-btn').addEventListener("click", toggleFullscreen)
document.addEventListener('fullscreenchange', event => {
if (!document.fullscreenElement) {
screen.orientation.unlock()
that.classList.remove('fullscreen')
that.querySelector('.fullscreen-btn').innerHTML = icon.enterFullscreen
} else {
self.controls.querySelector('.fullscreen-btn').innerHTML = icon.exitFullscreen
}
})
that.append(self.controls)
}
function getVideoWidth() {
if (document.fullscreenElement) {
return ''
}
var av = that.querySelector('video.active')
return av ? av.getBoundingClientRect().width + 'px' : '100%'
}
var playOnLoad = false
var unblock = document.createElement("div")
that.addEventListener("requiresusergesture", event => {
unblock.style.position = "absolute"
unblock.style.width = '100%'
unblock.style.height = '100%'
unblock.style.backgroundImage = `url(${self.options.poster})`
unblock.style.zIndex = '1000'
unblock.style.backgroundPosition = "top left"
unblock.style.backgroundRepeat = "no-repeat"
unblock.style.backgroundSize = "cover"
unblock.style.display = 'flex'
unblock.classList.add('mx-controls')
unblock.classList.add('poster')
unblock.innerHTML = `
<div class="toggle">
<div style="margin: auto">${icon.play}</div>
</div>
<div class="controls" style="height: 37px;"></div>
`
self.controls.style.opacity = '0'
unblock.addEventListener("click", event => {
event.preventDefault()
event.stopPropagation()
playOnLoad = true
unblock.querySelector('.toggle').innerHTML = `
<div style="margin: auto" class="loading">${icon.loading}</div>
`
}, {once: true})
that.append(unblock)
})
var loading = true
2024-12-14 19:16:41 +00:00
var touching = false
that.brightness(0)
that.addEventListener("loadedmetadata", event => {
//
})
that.addEventListener("seeked", event => {
if (loading) {
that.brightness(1)
loading = false
}
if (playOnLoad) {
playOnLoad = false
var toggle = self.controls.querySelector('.toggle')
toggle.title = 'Pause'
toggle.querySelector('div').innerHTML = icon.pause
self.controls.style.opacity = '0'
unblock.remove()
2024-02-17 10:55:11 +00:00
self.controls.classList.remove('poster')
that.play()
}
})
2024-12-14 19:16:41 +00:00
var time = that.querySelector('.controls .time div');
const progressbar = that.querySelector('.seekbar div[role="progressbar"]');
function setProgressPosition(value) {
progressbar.style.width = value + '%';
progressbar.setAttribute('aria-valuenow', value);
}
that.querySelector('.controls .position input').addEventListener('input', function(event){
event.preventDefault()
event.stopPropagation()
setProgressPosition(this.value)
var position = this.value/100 * self.options.duration
that.currentTime(position)
hideControlsLater()
})
2024-12-14 19:16:41 +00:00
that.addEventListener("timeupdate", event => {
var currentTime = that.currentTime(),
duration = self.options.duration
if (self.options.position) {
currentTime -= self.options.position
}
2024-12-14 19:16:41 +00:00
setProgressPosition(100 * currentTime / duration)
duration = formatDuration(duration)
currentTime = formatDuration(currentTime)
while (duration && duration.startsWith('00:')) {
duration = duration.slice(3)
}
currentTime = currentTime.slice(currentTime.length - duration.length)
time.innerText = `${currentTime} / ${duration}`
})
that.addEventListener("play", event => {
var toggle = self.controls.querySelector('.toggle')
toggle.title = 'Pause'
toggle.querySelector('div').innerHTML = icon.pause
self.controls.style.opacity = '0'
})
that.addEventListener("pause", event => {
var toggle = self.controls.querySelector('.toggle')
toggle.title = 'Play'
toggle.querySelector('div').innerHTML = icon.play
self.controls.style.opacity = '1'
})
that.addEventListener("ended", event => {
var toggle = self.controls.querySelector('.toggle')
toggle.title = 'Play'
toggle.querySelector('div').innerHTML = icon.play
self.controls.style.opacity = '1'
})
that.addEventListener("seeking", event => {
//console.log("seeking")
})
that.addEventListener("seeked", event => {
//console.log("seeked")
})
that.addEventListener("volumechange", event => {
var volume = self.controls.querySelector('.volume')
if (that.muted()) {
volume.innerHTML = icon.unmute
volume.title = "Unmute"
} else {
volume.innerHTML = icon.mute
volume.title = "Mute"
}
})
window.addEventListener('resize', event => {
//
})
return that
};
})();