mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-22 21:31:20 -08:00
refactor(thumbnail): [PKG-TOOL-THUMB-04] preserve approved published defaults
This commit is contained in:
1 parent
490e157717
commit
b5f8b5e28e
37 files changed
+11172
-136
No files matched your search
@@ -1,13 +1,15 @@
|
||||
import type ArtplayerToolThumbnail from './index'
|
||||
import type { Cleanup, ExtractionJob, LifecycleState } from './types'
|
||||
import { cancellation, cleanupAll, stateFor } from './lifecycle'
|
||||
import { thumbnailPolicy } from './policy'
|
||||
import { replaceThumbnail } from './source'
|
||||
import { clamp } from './utils'
|
||||
|
||||
function prepare(tool: ArtplayerToolThumbnail, job: ExtractionJob) {
|
||||
const { video } = job
|
||||
const { width, number, begin, end } = tool.option
|
||||
const height = (video.videoHeight / video.videoWidth) * width
|
||||
const policy = thumbnailPolicy(tool.option)
|
||||
const height = policy.aspectHeight ? (video.videoHeight / video.videoWidth) * width : tool.option.height
|
||||
const guard = (condition: unknown, message: string) => {
|
||||
try {
|
||||
tool.errorHandle(condition, message)
|
||||
@@ -32,7 +34,7 @@ function prepare(tool: ArtplayerToolThumbnail, job: ExtractionJob) {
|
||||
const canvas = tool.creatCanvas()
|
||||
const context = canvas.getContext('2d')!
|
||||
tool.emit('canvas', canvas)
|
||||
return { width, height, number, points, canvas, context }
|
||||
return { width, height, number, points, canvas, context, delay: policy.delay }
|
||||
}
|
||||
|
||||
function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) {
|
||||
@@ -112,15 +114,19 @@ function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) {
|
||||
if (!live())
|
||||
return
|
||||
if (index === plan.points.length) {
|
||||
complete()
|
||||
if (plan.delay !== null)
|
||||
timer = setTimeout(complete, Number(plan.delay) * 2)
|
||||
else
|
||||
complete()
|
||||
return
|
||||
}
|
||||
const point = plan.points[index]!
|
||||
let drawing = false
|
||||
let encoded = false
|
||||
let delayElapsed = plan.delay === null
|
||||
const previous = video.oncanplay
|
||||
const draw = () => {
|
||||
if (!live() || drawing || video.seeking || (video.readyState !== undefined && video.readyState < 2))
|
||||
if (!live() || !delayElapsed || drawing || video.seeking || (video.readyState !== undefined && video.readyState < 2))
|
||||
return
|
||||
drawing = true
|
||||
try {
|
||||
@@ -160,8 +166,17 @@ function createJob(tool: ArtplayerToolThumbnail, state: LifecycleState) {
|
||||
video.oncanplay = draw
|
||||
video.addEventListener('seeked', draw)
|
||||
video.currentTime = point.time
|
||||
if (video.readyState >= 2 && !video.seeking)
|
||||
if (plan.delay !== null) {
|
||||
timer = setTimeout(() => {
|
||||
delayElapsed = true
|
||||
draw()
|
||||
}, plan.delay)
|
||||
if (!live())
|
||||
clearTimeout(timer)
|
||||
}
|
||||
else if (video.readyState >= 2 && !video.seeking) {
|
||||
Promise.resolve().then(draw)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
fail(error)
|
||||
|
||||
@@ -3,6 +3,7 @@ import Emitter from './emitter'
|
||||
import { startExtraction } from './extraction'
|
||||
import { connectInput, releaseInput, setupInput } from './input'
|
||||
import { cleanupAll, closeState, releaseMedia, stateFor } from './lifecycle'
|
||||
import { thumbnailPolicy } from './policy'
|
||||
import { createSheet, downloadSheet, screenshotPoints } from './sheet'
|
||||
import { loadSource } from './source'
|
||||
|
||||
@@ -46,6 +47,7 @@ export default class ArtplayerToolThumbnail extends Emitter<ThumbnailEvents> {
|
||||
|
||||
static get DEFAULTS() {
|
||||
return {
|
||||
delay: 300,
|
||||
number: 60,
|
||||
width: 160,
|
||||
height: 90,
|
||||
@@ -94,9 +96,11 @@ export default class ArtplayerToolThumbnail extends Emitter<ThumbnailEvents> {
|
||||
inputChange(event: Event) {
|
||||
if (stateFor(this).closed)
|
||||
return
|
||||
const { resetInput } = thumbnailPolicy(this.option)
|
||||
const file = this.option.fileInput.files![0]
|
||||
this.loadVideo(file)
|
||||
;(event.target as HTMLInputElement).value = ''
|
||||
if (resetInput)
|
||||
(event.target as HTMLInputElement).value = ''
|
||||
}
|
||||
|
||||
loadVideo(file?: File | null) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type ArtplayerToolThumbnail from './index'
|
||||
import type { InputOptions } from './types'
|
||||
import { cancellation, stateFor } from './lifecycle'
|
||||
import { clamp } from './utils'
|
||||
import { normalizePolicy } from './policy'
|
||||
|
||||
type Registration = readonly [name: string, callback: EventListener]
|
||||
interface InputRecord {
|
||||
@@ -62,11 +62,7 @@ export function setupInput(tool: ArtplayerToolThumbnail, patch: InputOptions) {
|
||||
const option = Object.assign({}, tool.option, patch)
|
||||
const target = option.fileInput!
|
||||
tool.errorHandle(target instanceof Element, 'The \'fileInput\' is not a Element')
|
||||
for (const name of ['number', 'width', 'column', 'begin', 'end'])
|
||||
tool.errorHandle(typeof option[name] === 'number', `The '${name}' is not a number`)
|
||||
option.number = clamp(option.number, 10, 1000)
|
||||
option.width = clamp(option.width, 10, 1000)
|
||||
option.column = clamp(option.column, 1, 1000)
|
||||
normalizePolicy(option, (condition, message) => tool.errorHandle(condition, message))
|
||||
if (!current())
|
||||
throw cancellation('input setup superseded')
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import type { RuntimeOptions } from './types'
|
||||
import { clamp } from './utils'
|
||||
|
||||
export function thumbnailPolicy(option: RuntimeOptions) {
|
||||
const workspace = option.compatibility === 'workspace-4.4'
|
||||
return { delay: workspace ? null : option.delay, aspectHeight: workspace, resetInput: workspace }
|
||||
}
|
||||
|
||||
export function normalizePolicy(option: RuntimeOptions, validate: (condition: unknown, message: string) => void) {
|
||||
validate(option.compatibility === undefined || option.compatibility === 'published-3.5' || option.compatibility === 'workspace-4.4', 'The compatibility option must be published-3.5 or workspace-4.4')
|
||||
const workspace = thumbnailPolicy(option).aspectHeight
|
||||
const fields = workspace ? ['number', 'width', 'column', 'begin', 'end'] : ['delay', 'number', 'width', 'height', 'column', 'begin', 'end']
|
||||
for (const name of fields)
|
||||
validate(typeof option[name] === 'number', `The '${name}' is not a number`)
|
||||
option.number = clamp(option.number, 10, 1000)
|
||||
option.width = clamp(option.width, 10, 1000)
|
||||
option.column = clamp(option.column, 1, 1000)
|
||||
if (!workspace) {
|
||||
option.delay = clamp(Number(option.delay), 10, 1000)
|
||||
option.height = clamp(option.height, 10, 1000)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import type ArtplayerToolThumbnail from './index'
|
||||
import type { Cleanup } from './types'
|
||||
import { cleanupAll, revoke, stateFor } from './lifecycle'
|
||||
import { thumbnailPolicy } from './policy'
|
||||
|
||||
export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) {
|
||||
const state = stateFor(tool)
|
||||
@@ -8,6 +9,7 @@ export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) {
|
||||
return
|
||||
const initialEpoch = state.epoch
|
||||
const video = tool.video
|
||||
const { delay } = thumbnailPolicy(tool.option)
|
||||
const support = video.canPlayType(file.type)
|
||||
tool.errorHandle(support === 'maybe' || support === 'probably', `Playback of this file format is not supported: ${file.type}`)
|
||||
if (state.closed || state.epoch !== initialEpoch)
|
||||
@@ -28,11 +30,15 @@ export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) {
|
||||
const listeners: Cleanup[] = []
|
||||
state.sourceListeners = listeners
|
||||
let pendingCleanup = () => {}
|
||||
let notificationTimer: ReturnType<typeof setTimeout> | undefined
|
||||
const cancelNotification = () => clearTimeout(notificationTimer)
|
||||
listeners.push(cancelNotification)
|
||||
let reported = false
|
||||
const error = () => {
|
||||
if (!current() || reported || (video.currentSrc && video.currentSrc !== url))
|
||||
return
|
||||
reported = true
|
||||
cancelNotification()
|
||||
state.loading = false
|
||||
const failure = new Error(`Unable to load video: media error ${video.error?.code || 0}`)
|
||||
if (state.job)
|
||||
@@ -79,8 +85,20 @@ export function loadSource(tool: ArtplayerToolThumbnail, file?: File | null) {
|
||||
if (previous !== url)
|
||||
revoke(state.sourceUrls, previous)
|
||||
}
|
||||
if (current())
|
||||
tool.emit('video', video)
|
||||
if (current() && !reported) {
|
||||
if (delay !== null) {
|
||||
notificationTimer = setTimeout(() => {
|
||||
cancelNotification()
|
||||
if (current() && !reported)
|
||||
tool.emit('video', video)
|
||||
}, delay)
|
||||
if (!current() || reported)
|
||||
cancelNotification()
|
||||
}
|
||||
else {
|
||||
tool.emit('video', video)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
if (current()) {
|
||||
|
||||
@@ -9,11 +9,15 @@ export interface SheetOptions {
|
||||
|
||||
export interface InputOptions extends Partial<SheetOptions> {
|
||||
fileInput?: Element
|
||||
delay?: number
|
||||
compatibility?: 'published-3.5' | 'workspace-4.4'
|
||||
[name: string]: unknown
|
||||
}
|
||||
|
||||
export interface RuntimeOptions extends SheetOptions {
|
||||
fileInput: HTMLInputElement
|
||||
delay?: number
|
||||
compatibility?: 'published-3.5' | 'workspace-4.4'
|
||||
[name: string]: unknown
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user