mirror of
https://git.sr.ht/~cadence/cloudtube
synced 2024-09-20 03:07:28 +00:00
118 lines
2.6 KiB
JavaScript
118 lines
2.6 KiB
JavaScript
|
import {q, ElemJS} from "/static/js/elemjs/elemjs.js"
|
||
|
|
||
|
const video = q("#video")
|
||
|
const audio = q("#audio")
|
||
|
|
||
|
const videoFormats = new Map()
|
||
|
const audioFormats = new Map()
|
||
|
for (const f of [].concat(
|
||
|
data.formatStreams.map(f => (f.isAdaptive = false, f)),
|
||
|
data.adaptiveFormats.map(f => (f.isAdaptive = true, f))
|
||
|
)) {
|
||
|
if (f.type.startsWith("video")) {
|
||
|
videoFormats.set(f.itag, f)
|
||
|
} else {
|
||
|
audioFormats.set(f.itag, f)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function getBestAudioFormat() {
|
||
|
let best = null
|
||
|
for (const f of audioFormats.values()) {
|
||
|
if (best === null || f.bitrate > best.bitrate) {
|
||
|
best = f
|
||
|
}
|
||
|
}
|
||
|
return best
|
||
|
}
|
||
|
|
||
|
class FormatLoader {
|
||
|
constructor() {
|
||
|
this.npv = videoFormats.get(q("#video").getAttribute("data-itag"))
|
||
|
this.npa = null
|
||
|
}
|
||
|
|
||
|
play(itag) {
|
||
|
this.npv = videoFormats.get(itag)
|
||
|
if (this.npv.isAdaptive) {
|
||
|
this.npa = getBestAudioFormat()
|
||
|
} else {
|
||
|
this.npa = null
|
||
|
}
|
||
|
this.update()
|
||
|
}
|
||
|
|
||
|
update() {
|
||
|
const lastTime = video.currentTime
|
||
|
video.src = this.npv.url
|
||
|
video.currentTime = lastTime
|
||
|
if (this.npa) {
|
||
|
audio.src = this.npa.url
|
||
|
audio.currentTime = lastTime
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const formatLoader = new FormatLoader()
|
||
|
|
||
|
class QualitySelect extends ElemJS {
|
||
|
constructor() {
|
||
|
super(q("#quality-select"))
|
||
|
this.on("input", this.onInput.bind(this))
|
||
|
}
|
||
|
|
||
|
onInput() {
|
||
|
const itag = this.element.value
|
||
|
formatLoader.play(itag)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const qualitySelect = new QualitySelect()
|
||
|
|
||
|
function playbackIntervention(event) {
|
||
|
console.log(event.target.tagName.toLowerCase(), event.type)
|
||
|
if (audio.src) {
|
||
|
let target = event.target
|
||
|
let targetName = target.tagName.toLowerCase()
|
||
|
let other = (event.target === video ? audio : video)
|
||
|
switch (event.type) {
|
||
|
case "durationchange":
|
||
|
target.ready = false;
|
||
|
break;
|
||
|
case "seeked":
|
||
|
target.ready = false;
|
||
|
target.pause();
|
||
|
other.currentTime = target.currentTime;
|
||
|
break;
|
||
|
case "play":
|
||
|
other.currentTime = target.currentTime;
|
||
|
other.play();
|
||
|
break;
|
||
|
case "pause":
|
||
|
other.currentTime = target.currentTime;
|
||
|
other.pause();
|
||
|
case "playing":
|
||
|
other.currentTime = target.currentTime;
|
||
|
break;
|
||
|
case "ratechange":
|
||
|
other.rate = target.rate;
|
||
|
break;
|
||
|
// case "stalled":
|
||
|
// case "waiting":
|
||
|
// target.pause();
|
||
|
// break;
|
||
|
}
|
||
|
} else {
|
||
|
// @ts-ignore this does exist
|
||
|
// if (event.type == "canplaythrough" && !video.manualPaused) video.play();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (let eventName of ["pause", "play", "seeked"]) {
|
||
|
video.addEventListener(eventName, playbackIntervention)
|
||
|
}
|
||
|
for (let eventName of ["canplaythrough", "waiting", "stalled"]) {
|
||
|
video.addEventListener(eventName, playbackIntervention)
|
||
|
audio.addEventListener(eventName, playbackIntervention)
|
||
|
}
|