Files
ArtPlayer/packages/artplayer-tool-thumbnail/src/lifecycle.ts
T

84 lines
2.1 KiB
TypeScript

import type ArtplayerToolThumbnail from './index'
import type { Cleanup, LifecycleState } from './types'
const states = new WeakMap<ArtplayerToolThumbnail, LifecycleState>()
export function stateFor(tool: ArtplayerToolThumbnail): LifecycleState {
if (!states.has(tool)) {
states.set(tool, {
closed: false,
epoch: 0,
inputEpoch: 0,
job: null,
video: null,
sourceUrl: null,
thumbnailUrl: null,
sourceUrls: new Set(),
thumbnailUrls: new Set(),
sourceListeners: [],
loading: false,
})
}
return states.get(tool)!
}
export function cancellation(reason: string) {
const error = new Error(`Thumbnail task cancelled: ${reason}`)
error.name = 'AbortError'
return error
}
export function revoke(urls: Set<string>, url: string | undefined) {
if (!url)
return
urls.delete(url)
URL.revokeObjectURL(url)
}
export function cleanupAll(callbacks: Cleanup[]) {
let failure: unknown
for (const callback of callbacks) {
try {
callback()
}
catch (error) {
failure ||= error
}
}
if (failure)
throw failure
}
export function closeState(tool: ArtplayerToolThumbnail, reason = 'destroyed') {
const state = stateFor(tool)
if (state.closed)
return false
state.closed = true
state.epoch++
state.job?.cancel(reason)
return true
}
export function releaseMedia(tool: ArtplayerToolThumbnail) {
const state = stateFor(tool)
const sources = new Set([...state.sourceUrls, tool.videoUrl])
const thumbnails = new Set([...state.thumbnailUrls, tool.thumbnailUrl])
const media: Cleanup[] = []
for (const video of new Set([state.video, tool.video])) {
if (video) {
media.push(
() => video.pause?.(),
() => video.removeAttribute?.('src'),
() => video.load?.(),
() => video.parentNode?.removeChild(video),
)
}
}
cleanupAll([
...state.sourceListeners.splice(0),
...media,
...[...sources].map(url => () => revoke(state.sourceUrls, url)),
...[...thumbnails].map(url => () => revoke(state.thumbnailUrls, url)),
])
}