feat: extract quality switching and mode selection
This commit is contained in:
64
static/player/mode.ts
Normal file
64
static/player/mode.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { state } from './state'
|
||||||
|
import { displayTimeFromAbsolute, absoluteTimeFromDisplay } from './timeline'
|
||||||
|
import { showControls } from './controls'
|
||||||
|
import { ModeSource } from './types'
|
||||||
|
|
||||||
|
const streamUrlForMode = (mode: string, quality?: string): string => {
|
||||||
|
const src = state.modeSources[mode]
|
||||||
|
if (!src?.token) return ''
|
||||||
|
let url = `${state.streamURL}?mode=${encodeURIComponent(mode)}&token=${encodeURIComponent(src.token)}`
|
||||||
|
if (quality && quality !== 'best') url += `&quality=${encodeURIComponent(quality)}`
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadVideo = (url: string): void => {
|
||||||
|
if (!url) return
|
||||||
|
const wasPlaying = !state.video.paused
|
||||||
|
const prevTime = displayTimeFromAbsolute(state.video.currentTime)
|
||||||
|
state.video.src = url
|
||||||
|
state.video.load()
|
||||||
|
state.pendingSeekTime = prevTime
|
||||||
|
if (wasPlaying) state.video.play().catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const switchMode = (mode: string): void => {
|
||||||
|
if (!state.availableModes.includes(mode) || mode === state.currentMode) return
|
||||||
|
state.currentMode = mode
|
||||||
|
localStorage.setItem('player-audio-mode', mode)
|
||||||
|
loadVideo(streamUrlForMode(mode, state.container.querySelector('[data-quality-select]')?.value))
|
||||||
|
import('./subtitles').then(m => m.updateSubtitleOptions())
|
||||||
|
import('./quality').then(m => m.updateQualityOptions())
|
||||||
|
import('./mode').then(m => m.updateModeButtons())
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateModeButtons = (): void => {
|
||||||
|
const dub = state.container.querySelector('[data-mode-dub]') as HTMLButtonElement | null
|
||||||
|
const sub = state.container.querySelector('[data-mode-sub]') as HTMLButtonElement | null
|
||||||
|
const m = state.currentMode
|
||||||
|
|
||||||
|
dub?.classList.toggle('text-accent', m === 'dub')
|
||||||
|
dub?.classList.toggle('text-white', m !== 'dub')
|
||||||
|
dub?.classList.toggle('opacity-50', !state.availableModes.includes('dub'))
|
||||||
|
dub?.classList.toggle('cursor-not-allowed', !state.availableModes.includes('dub'))
|
||||||
|
dub && (dub.disabled = !state.availableModes.includes('dub'))
|
||||||
|
|
||||||
|
sub?.classList.toggle('text-accent', m === 'sub')
|
||||||
|
sub?.classList.toggle('text-white', m !== 'sub')
|
||||||
|
sub?.classList.toggle('opacity-50', !state.availableModes.includes('sub'))
|
||||||
|
sub?.classList.toggle('cursor-not-allowed', !state.availableModes.includes('sub'))
|
||||||
|
sub && (sub.disabled = !state.availableModes.includes('sub'))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupMode = (): void => {
|
||||||
|
const dub = state.container.querySelector('[data-mode-dub]') as HTMLButtonElement | null
|
||||||
|
const sub = state.container.querySelector('[data-mode-sub]') as HTMLButtonElement | null
|
||||||
|
|
||||||
|
dub?.addEventListener('click', () => { if (state.availableModes.includes('dub')) { switchMode('dub'); showControls() } })
|
||||||
|
sub?.addEventListener('click', () => { if (state.availableModes.includes('sub')) { switchMode('sub'); showControls() } })
|
||||||
|
|
||||||
|
const autoplayBtn = document.querySelector('[data-autoplay]') as HTMLInputElement | null
|
||||||
|
autoplayBtn?.addEventListener('change', (e) => {
|
||||||
|
localStorage.setItem('mal:autoplay-enabled', (e.target as HTMLInputElement).checked ? 'true' : 'false')
|
||||||
|
showControls()
|
||||||
|
})
|
||||||
|
}
|
||||||
59
static/player/quality.ts
Normal file
59
static/player/quality.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { state } from './state'
|
||||||
|
import { displayTimeFromAbsolute, absoluteTimeFromDisplay } from './timeline'
|
||||||
|
|
||||||
|
const streamUrlForMode = (mode: string, quality?: string): string => {
|
||||||
|
const src = state.modeSources[mode]
|
||||||
|
if (!src?.token) return ''
|
||||||
|
let url = `${state.streamURL}?mode=${encodeURIComponent(mode)}&token=${encodeURIComponent(src.token)}`
|
||||||
|
if (quality && quality !== 'best') url += `&quality=${encodeURIComponent(quality)}`
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadVideo = (url: string): void => {
|
||||||
|
if (!url) return
|
||||||
|
const wasPlaying = !state.video.paused
|
||||||
|
const prevTime = displayTimeFromAbsolute(state.video.currentTime)
|
||||||
|
state.video.src = url
|
||||||
|
state.video.load()
|
||||||
|
state.pendingSeekTime = prevTime
|
||||||
|
if (wasPlaying) state.video.play().catch(() => {})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const switchQuality = (quality: string): void => {
|
||||||
|
const url = streamUrlForMode(state.currentMode, quality)
|
||||||
|
if (!url) return
|
||||||
|
localStorage.setItem('mal:preferred-quality', quality)
|
||||||
|
loadVideo(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateQualityOptions = (): void => {
|
||||||
|
const select = state.container.querySelector('[data-quality-select]') as HTMLSelectElement | null
|
||||||
|
if (!select) return
|
||||||
|
const qualities = state.modeSources[state.currentMode]?.qualities ?? []
|
||||||
|
select.innerHTML = ''
|
||||||
|
|
||||||
|
const best = document.createElement('option')
|
||||||
|
best.value = 'best'
|
||||||
|
best.textContent = 'Auto / Best'
|
||||||
|
select.appendChild(best)
|
||||||
|
|
||||||
|
qualities.forEach(q => {
|
||||||
|
const opt = document.createElement('option')
|
||||||
|
opt.value = q
|
||||||
|
opt.textContent = q
|
||||||
|
select.appendChild(opt)
|
||||||
|
})
|
||||||
|
|
||||||
|
const preferred = localStorage.getItem('mal:preferred-quality') || 'best'
|
||||||
|
select.value = qualities.includes(preferred) ? preferred : 'best'
|
||||||
|
|
||||||
|
const wrapper = select.parentElement
|
||||||
|
wrapper?.classList.toggle('hidden', qualities.length === 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setupQuality = (): void => {
|
||||||
|
const select = state.container.querySelector('[data-quality-select]') as HTMLSelectElement | null
|
||||||
|
select?.addEventListener('change', (e) => {
|
||||||
|
switchQuality((e.target as HTMLSelectElement).value)
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user