const video = document.getElementsByClassName("video")[0] video.classList.add("hasCustomCaptions") // Elements const customStyle = document.createElement("style") customStyle.setAttribute("type", "text/css") document.head.appendChild(customStyle) const captionBox = document.createElement("div") captionBox.className = "caption-box" video.parentNode.appendChild(captionBox) const captionInner = document.createElement("div") captionInner.className = "caption-inner" captionBox.appendChild(captionInner) // Parseing const regexVVTSource = /::cue\(([^\. \)])+([^\)]*)\)/g const regexCueText = /<([^\. />]+)([^>]+?)>/g function parseVVTSource(s) { s = s.split("\nStyle:\n")[1].split("\n##\n")[0] for (const match of s.matchAll(regexVVTSource)) { const wholeSelector = match[0] const className = match[1] const details = match[2] || null s = s.replace(wholeSelector, `.caption-box .cue-container ${className}${details ? `[data-details="${details}"]` : ""}`) } return s } function parseCueText(t) { if (!t) return t for (const match of t.matchAll(regexCueText)) { const wholeElement = match[0] const className = match[1] const details = match[2] t = t.replace(wholeElement, `<${className} data-details="${details}">`) } return t } // Detect caption switching let lastTrack = null for (const track of video.textTracks) { track.sourceElement = video.querySelector(`track[label="${track.label}"]`) function parseCaptionStyle(raw) { if (raw.indexOf("\nStyle:\n") > -1) return parseVVTSource(raw) return null } function applyCaptionStyle(track) { customStyle.innerHTML = track.style } // Eugh track.addEventListener("cuechange", () => { lastTrack = track if (track.raw == undefined) { fetch(track.sourceElement.src) .then(r => r.text()).then(r => { track.style = parseCaptionStyle(r) if (lastTrack == track && track.style) applyCaptionStyle(track) }) } else if (track.style) applyCaptionStyle(track) }) } // Functionality for (const track of video.textTracks) track.addEventListener("cuechange", () => { // Remove previous cues TODO: also do when captions turned off captionInner.innerHTML = "" let cues = [] for (const c of track.activeCues) { const cueContainer = document.createElement("div") cueContainer.className = "cue-container" captionInner.appendChild(cueContainer) cueContainer.innerHTML = parseCueText(c.text) console.log(c) // align: "center" // line: "auto" // lineAlign: "start" // position: "auto" / 89 // positionAlign: "auto" // size: 100 // snapToLines: true // // console.log(c.position) // cueContainer.style.position = c.position == "auto" ? "auto" : `${c.position}%` const asHTML = c.getCueAsHTML() // console.log(track.sourceElement) console.log(asHTML) cueContainer.style.textAlign = c.align /* position: absolute; writing-mode: horizontal-tb; top: 86.2709%; left: 78%; width: 22%; height: auto; overflow-wrap: break-word; white-space: pre-line; font: 53.9px sans-serif; color: rgb(255, 255, 255); text-align: center; unicode-bidi: plaintext; */ // for (const child of asHTML.children) // cueContainer.appendChild(child.cloneNode(true)) // captionInner.innerHTML = c.getCueAsHTML() // const cue = document.createElement("span") // cue.className = "cue" // cueContainer.appendChild(cue) // console.log(`NEW CUE:\n${c.align}, ${c.line}, ${c.lineAlign}, ${c.position}, ${c.positionAlign}, ${c.region}, ${c.size}, ${c.snapToLines}, ${c.text}, ${c.vertical}`) // console.log(c.getCueAsHTML()) } // captionRow.innerText = "hi" })