pandora_p_for_power/player/html/index.html
2026-02-01 22:12:11 +01:00

476 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>P for Power</title>
<style>
@font-face {
font-family: 'Menlo';
src: url('Menlo-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
html, body {
margin: 0;
padding: 0;
height: 100%;
font-family: Menlo, sans-serif;
}
#click-to-play {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 100%;
height: 100%;
background: #000000;
color: white;
display: flex;
justify-content: center;
z-index: 10;
cursor: pointer;
}
#click-to-play .text {
font-size: 16px;
width: 80%;
margin: auto;
text-align: center;
}
/* Full page container */
.page {
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 24px;
background: rgba(0,0,0, 0.5);
position: fixed;
z-index: 100;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
/* Grid container */
.grid {
display: grid;
grid-template-columns: repeat(10, 70px);
grid-template-rows: repeat(3, 70px);
gap: 12px;
justify-content: center;
align-content: center;
}
/* Individual boxes */
.box {
display: flex;
justify-content: center;
align-items: center;
color: #ffff;
background: rgba(0,0,0, 0.6);
border: 2px solid #333;
cursor: pointer;
font-size: 18px;
transition: background 0.2s ease, transform 0.2s ease;
&.current {
background: #909090;
}
}
.box:hover {
background: #e0e0e0;
transform: translateY(-2px);
}
/* Title display */
.title {
min-height: 24px;
font-size: 20px;
font-weight: bold;
text-align: center;
color: #ffff;
}
body {
background: black;
}
video {
max-width: 100%;
max-height: 100%;
width: 100%;
height: 100%;
margin: auto;
}
#stage {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
video.next {
display: none;
}
#subtitles {
position: relative;
}
@font-face {
font-family: 'Menlo';
src: url('Menlo-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
</style>
<script src="ass.global.min.js"></script>
</head>
<body>
<script>
const random = +new Date
const sub_handlers = {};
var playlist = [
"/01_Life/segment.mp4",
"/02_Seeing/segment.mp4",
"/03_Capacity/segment.mp4",
"/04_Landscape/segment.mp4",
"/05_Relations/segment.mp4",
"/06_Will/segment.mp4",
"/07_Self/segment.mp4",
"/08_Field/segment.mp4",
"/09_Theatre/segment.mp4",
"/10_Substance/segment.mp4",
"/11_Science/segment.mp4",
"/12_Energy/segment.mp4",
"/13_Geopolitics/segment.mp4",
"/14__Chance/segment.mp4",
"/15_Morality/segment.mp4",
"/16_Evolution/segment.mp4",
"/17_Computing/segment.mp4",
"/18_Semi-Conductors/segment.mp4",
"/19_Love/segment.mp4",
"/20_Reversibility/segment.mp4",
"/21_Weakness/segment.mp4",
"/22_Elections/segment.mp4",
"/23_People/segment.mp4",
"/24_Corporation/segment.mp4",
"/25_Instruments/segment.mp4",
"/26_Practice/segment.mp4",
"/27_Weaponry/segment.mp4",
"/28_Art/segment.mp4",
"/29_Help/segment.mp4",
"/30_Self-help/segment.mp4",
]
//var prefix = '../render/'
var prefix = 'https://power.0x2620.org/static/render/'
var current = 0, name
var currentVideo, nextVideo;
function ended(event) {
const next = nextVideo;
currentVideo.classList.add('next')
sub_handlers[currentVideo.id]?.destroy()
nextVideo.classList.remove('next')
console.log("play next", nextVideo)
nextVideo.play()
sub_handlers[nextVideo.id]?.show()
current +=1
if (current >= playlist.length) {
current = 0
}
currentVideo.src = prefix + name + playlist[current]
nextVideo = currentVideo
currentVideo = next
sub_handlers[nextVideo.id]?.destroy()
fetch(nextVideo.src.replace(/.mp4/, '.ass') + '?' +random ).then(async (res) => {
const content = await res.text()
const ass = new ASS(content, nextVideo, {
container: subtitles,
});
sub_handlers[nextVideo.id] = ass
})
}
const titles = [
"Life",
"Seeing",
"Capacity",
"Landscape",
"Relations",
"Will",
"Self",
"Field",
"Theatre",
"Substance",
"Science",
"Energy",
"Geopolitics",
"Chance",
"Morality",
"Evolution",
"Computing",
"Semi-Conductors",
"Love",
"Reversibility",
"Weakness",
"Elections",
"People",
"Corporation",
"Instruments",
"Practice",
"Weaponry",
"Art",
"Help",
"Self-help",
];
function bindFullscreen() {
window.addEventListener('keydown', event => {
console.log('keydown')
event.stopPropagation()
});
window.addEventListener('mousedown', event => {
console.log('mousedown')
toggleFullscreen()
event.preventDefault()
event.stopPropagation()
});
window.addEventListener('touchstart', event => {
event.preventDefault()
event.stopPropagation()
});
}
function toggleFullscreen() {
var body = document.querySelector('body')
if (!(document.fullscreenElement === null || document.webkitFullscreenElement === null)) {
if (document.fullscreenElement) {
document.exitFullscreen()
}
if (document.webkitFullscreenElement) {
document.webkitExitFullscreen()
}
} else {
console.log('enter fullscreen?')
if (body.webkitRequestFullscreen) {
body.webkitRequestFullscreen({navigationUI: 'hide'})
} else {
body.requestFullscreen({navigationUI: 'hide'}).catch(err => {
console.log(`Error attempting to enable full-screen mode: ${err.message} (${err.name})`);
});
}
}
}
function play() {
let blocked = false, loading
document.querySelectorAll("video,audio").forEach(async (media) => {
if (blocked) { return }
return media.play().then(() => {
if (media.classList.contains('next')) {
media.pause()
}
}).catch(error => {
if (blocked) {
return
}
blocked = true
loading = document.createElement('div')
loading.id = 'click-to-play'
loading.innerHTML = '<div class="text"><h1>P for Power</h1><p>click to start</p></div>'
document.body.appendChild(loading)
function removeBehaviorsRestrictions() {
loading.remove()
window.removeEventListener('keydown', removeBehaviorsRestrictions);
window.removeEventListener('mousedown', removeBehaviorsRestrictions);
window.removeEventListener('touchstart', removeBehaviorsRestrictions);
blocked = false
play()
toggleFullscreen()
}
window.addEventListener('keydown', removeBehaviorsRestrictions);
window.addEventListener('mousedown', removeBehaviorsRestrictions);
window.addEventListener('touchstart', removeBehaviorsRestrictions);
})
})
}
function pause() {
document.createElementAll("video,audio").forEach(media => {
media.paused = true
})
}
function render() {
name = document.location.hash.slice(1)
console.log('on load', name)
const parts = name.split('/')
if (parts.length == 2) {
name = parts[0]
current = parseInt(parts[1]) - 1
}
var body = document.querySelector('body')
body.innerHTML = ``
var stage = document.createElement("div")
stage.id = "stage"
body.appendChild(stage)
var overlay = document.createElement("div")
overlay.id = "overlay"
body.appendChild(overlay)
overlay.innerHTML = `
<div class="page">
<div class="grid" id="grid"></div>
<div class="title" id="title"></div>
</div>
`
const grid = overlay.querySelector("#grid");
const titleEl = overlay.querySelector("#title");
for (let i = 1; i <= 30; i++) {
const box = document.createElement("div");
box.className = "box box" + i;
box.textContent = i;
box.addEventListener("mouseenter", () => {
titleEl.textContent = titles[i - 1];
});
box.addEventListener("mouseleave", () => {
titleEl.textContent = "";
});
box.addEventListener("click", () => {
console.log("clicked", box.textContent)
document.querySelector(".page").style.display = ""
current = parseInt(box.textContent) - 1
nextVideo.src = prefix + name + playlist[current]
fetch(nextVideo.src.replace(/.mp4/, '.ass') + '?' +random).then(async (res) => {
const content = await res.text()
const ass = new ASS(content, nextVideo, {
container: subtitles,
});
sub_handlers[nextVideo.id]?.destroy()
sub_handlers[nextVideo.id] = ass
ended()
})
});
grid.appendChild(box);
}
var subtitles = document.createElement("div")
subtitles.id = "subtitles"
stage.appendChild(subtitles)
var video1 = document.createElement("video")
video1.src = prefix + name + playlist[current]
video1.id = 'v1'
video1.controls = false
video1.preload = "auto"
video1.addEventListener('ended', ended)
stage.appendChild(video1)
currentVideo = video1
current +=1
var video2 = document.createElement("video")
video2.src = prefix + name + playlist[current]
video1.id = 'v2'
video2.classList.add("next")
video2.controls = false
video2.preload = "auto"
video2.addEventListener('ended', ended)
stage.appendChild(video2)
nextVideo = video2
var audio2 = document.createElement("audio")
audio2.src = prefix + 'forest-5.1.mp4'
audio2.controls = false
audio2.volume = 1
audio2.loop = true
audio2.classList.add("forest")
body.appendChild(audio2)
var audio3 = document.createElement("audio")
audio3.src = prefix + 'music-5.1.mp4'
audio3.controls = false
audio3.volume = 1
audio3.loop = true
audio3.currentTime = Math.random() * 60
audio3.classList.add("music")
body.appendChild(audio3)
document.querySelectorAll('video,audio').forEach(media => {
media.addEventListener('play', sync)
media.addEventListener('pause', sync)
media.addEventListener('click', () => {
document.querySelector(".page").style.display = "flex"
document.querySelectorAll('.box').forEach(box => { box.classList.remove('current') })
document.querySelector('.box' + current).classList.add('current')
})
})
fetch(video1.src.replace(/.mp4/, '.ass') + '?' +random).then(async (res) => {
const content = await res.text()
const ass = new ASS(content, video1, {
container: subtitles,
});
sub_handlers[video1.id] = ass
sub_handlers[video1.id].show()
play()
fetch(video2.src.replace(/.mp4/, '.ass')+ '?' +random).then(async (res) => {
const content = await res.text()
const ass = new ASS(content, video2, {
container: subtitles,
});
sub_handlers[video2.id] = ass
})
})
}
function sync(event) {
var src = event.target
event.stopPropagation()
event.preventDefault()
return
document.querySelectorAll('video,audio').forEach(media => {
if (media.classList.contains('next')) {
return
}
if (media != src) {
if (!src.paused) {
console.log("play", media.id)
media.play()
} else {
console.log("pause", media.id, src.paused)
media.pause()
}
media.paused = src.paused
if (media.classList.contains('music') || media.classList.contains('forest')) {
//
} else {
//media.currentTime = src.currentTime
console.log(media.currentTime, src.currentTime)
}
console.log(media, media.paused, src.paused)
}
})
}
window.addEventListener("hashchange", event=>{
render()
})
if (!document.location.hash.slice(1).length) {
document.location.hash = '#0'
} else {
render()
}
</script>
</body>
</html>