147 lines
4.2 KiB
TypeScript
147 lines
4.2 KiB
TypeScript
// @ts-nocheck
|
|
|
|
import videojs from 'video.js'
|
|
import 'videojs-contrib-quality-levels'
|
|
import type { QualityLevelList, QualityLevel } from 'videojs-contrib-quality-levels'
|
|
import i18n from '@/i18n'
|
|
import { getSetting } from '@/settingsManager'
|
|
|
|
export const createQualitySelector = (player: any) => {
|
|
const qualityLevels: QualityLevelList = player.qualityLevels()
|
|
const MenuButton = videojs.getComponent('MenuButton')
|
|
const MenuItem = videojs.getComponent('MenuItem')
|
|
let formatedQualities: { name: string; index: number; id: string; height: number }[] = []
|
|
let startupQualityApplied = false
|
|
|
|
const t = i18n.global.t
|
|
const translatedAuto = t('player.auto')
|
|
const autoLabel = translatedAuto === 'player.auto' ? 'Auto' : translatedAuto
|
|
|
|
const setAutoQuality = () => {
|
|
for (const quality of qualityLevels.levels_) {
|
|
// Keep video renditions enabled and skip the audio-only pseudo-level.
|
|
quality.enabled = quality.height !== undefined
|
|
}
|
|
|
|
qualityLevels.selectedIndex_ = -1
|
|
qualityLevels.trigger({ type: 'change', selectedIndex: -1 })
|
|
|
|
player.play().catch(() => {
|
|
// Ignore autoplay restrictions; user interaction can resume.
|
|
})
|
|
}
|
|
|
|
const setQuality = (id?: string) => {
|
|
if (!id) {
|
|
setAutoQuality()
|
|
return
|
|
}
|
|
|
|
const found = formatedQualities.find((i) => i.id === id)
|
|
if (!found) {
|
|
setAutoQuality()
|
|
return
|
|
}
|
|
|
|
for (const quality of qualityLevels.levels_) {
|
|
if (quality.height === undefined) {
|
|
quality.enabled = false
|
|
continue
|
|
}
|
|
|
|
// Keep adaptive bitrate enabled up to the selected quality.
|
|
quality.enabled = quality.height <= found.height
|
|
}
|
|
|
|
qualityLevels.selectedIndex_ = found?.index
|
|
qualityLevels.trigger({ type: 'change', selectedIndex: found?.index })
|
|
|
|
player.play().catch(() => {
|
|
// Ignore autoplay restrictions; user interaction can resume.
|
|
})
|
|
}
|
|
|
|
class CustomMenuButton extends MenuButton {
|
|
options_: any
|
|
player: any
|
|
createItems() {
|
|
const player = this.player()
|
|
|
|
return this.options_.items.map((item: { name: string }) => {
|
|
const qualitySelectorButton = new MenuItem(player, { label: item.name })
|
|
|
|
qualitySelectorButton.handleClick = (data) => {
|
|
const qualityClicked = data.currentTarget.innerText
|
|
if (qualityClicked === autoLabel) {
|
|
setAutoQuality()
|
|
return
|
|
}
|
|
|
|
const id = formatedQualities.find((i) => i.name === qualityClicked)?.id
|
|
|
|
if (id) {
|
|
setQuality(id)
|
|
}
|
|
}
|
|
|
|
return qualitySelectorButton
|
|
})
|
|
}
|
|
|
|
buildCSSClass() {
|
|
return `vjs-icon-cog ${super.buildCSSClass()}`
|
|
}
|
|
}
|
|
videojs.registerComponent('CustomMenuButton', CustomMenuButton)
|
|
const formattedLevels = []
|
|
|
|
const updateLevels = () => {
|
|
player.controlBar.removeChild('CustomMenuButton')
|
|
player.controlBar.addChild('CustomMenuButton', {
|
|
title: t('player.quality'),
|
|
items: [{ name: autoLabel }, ...formatedQualities]
|
|
})
|
|
|
|
if (!startupQualityApplied) {
|
|
startupQualityApplied = true
|
|
const startingQuality = String(getSetting('defaultQuality'))
|
|
const targetHeight = Number(startingQuality.replace(/[^0-9]/g, ''))
|
|
|
|
if (!Number.isFinite(targetHeight) || targetHeight <= 0) {
|
|
setAutoQuality()
|
|
return
|
|
}
|
|
|
|
// Prefer exact/default match; otherwise keep auto mode so ABR can adapt.
|
|
const id = formatedQualities.find((i) => i.height === targetHeight)?.id
|
|
setQuality(id)
|
|
}
|
|
}
|
|
|
|
qualityLevels.on('addqualitylevel', () => {
|
|
formatedQualities = qualityLevels.levels_.map((quality: QualityLevel, index: number) => {
|
|
let qualityFramerate = ''
|
|
|
|
if (quality.frameRate > 30) {
|
|
qualityFramerate = quality.frameRate
|
|
}
|
|
|
|
return {
|
|
name: quality.height?.toString() + 'p' + qualityFramerate,
|
|
index,
|
|
id: quality.id,
|
|
height: quality.height || 0
|
|
}
|
|
})
|
|
|
|
// remove audio only
|
|
const audioOnlyQuality = formatedQualities[formatedQualities.length - 1]
|
|
if (audioOnlyQuality.name === 'undefinedp') {
|
|
formatedQualities.splice(formatedQualities.length - 1, 1)
|
|
}
|
|
|
|
formattedLevels.push(formatedQualities)
|
|
updateLevels()
|
|
})
|
|
}
|