mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-29 02:51:20 -08:00
Enhance property handling in artplayerProxyMediabunny
- Updated the way properties from the shim instance and its prototype are retrieved. - Replaced the for-in loop with a Set to ensure all properties, including inherited ones, are considered. - Excluded the 'constructor' property from being added to the canvas. refactor: remove unused utility functions and streamline event handling - Deleted `videoEvents.js`, `clamp.js`, and `sleep.js` as they were not utilized. - Introduced a new `EventTarget` class to manage event listeners more efficiently. - Enhanced `MediaBunnyEngine`, `AudioEngine`, `VideoEngine`, and `VideoShim` to utilize the new event system. - Updated type definitions in `artplayer-proxy-mediabunny.d.ts` to include new configuration options. - Added comprehensive architecture documentation to clarify the structure and flow of the MediaBunny engine. - Refactored the codebase for improved readability and maintainability, including better naming conventions and JSDoc comments.
This commit is contained in:
1 parent
59e9a32ecb
commit
be44c1f8dd
25 files changed
+2299
-10309
No files matched your search
@@ -0,0 +1,205 @@
|
||||
/**
|
||||
* Main MediaBunny Engine
|
||||
* Coordinates audio and video playback
|
||||
*/
|
||||
import AudioEngine from './AudioEngine.js'
|
||||
import VideoEngine from './VideoEngine.js'
|
||||
|
||||
export default class MediaBunnyEngine {
|
||||
constructor({ canvas, ctx, events, option = {} }) {
|
||||
this.events = events
|
||||
this.option = option
|
||||
|
||||
// Create audio and video engines
|
||||
this.audio = new AudioEngine(events)
|
||||
this.video = new VideoEngine({
|
||||
canvas,
|
||||
ctx,
|
||||
events,
|
||||
timeupdateInterval: option.timeupdateInterval ?? 250,
|
||||
avSyncTolerance: option.avSyncTolerance ?? 0.12,
|
||||
dropLateFrames: option.dropLateFrames ?? false,
|
||||
poster: option.poster ?? '',
|
||||
preflightRange: option.preflightRange ?? false,
|
||||
})
|
||||
|
||||
// Playback state
|
||||
this.paused = true
|
||||
this.ended = false
|
||||
this.readyState = 0
|
||||
this.networkState = 0
|
||||
this.error = null
|
||||
this.seeking = false
|
||||
this.loadSeq = 0
|
||||
|
||||
// Listen to ended event
|
||||
events.addEventListener?.('ended', () => {
|
||||
this.ended = true
|
||||
this.paused = true
|
||||
})
|
||||
}
|
||||
|
||||
async load(src) {
|
||||
const id = ++this.loadSeq
|
||||
|
||||
this.pause()
|
||||
this.ended = false
|
||||
this.error = null
|
||||
this.networkState = 2 // NETWORK_LOADING
|
||||
this.readyState = 0 // HAVE_NOTHING
|
||||
|
||||
setTimeout(() => this.events.emit('waiting'), 0)
|
||||
setTimeout(() => this.events.emit('loadstart'), 0)
|
||||
|
||||
const loadTimeout = Number.isFinite(this.option.loadTimeout)
|
||||
? this.option.loadTimeout
|
||||
: 12000
|
||||
|
||||
try {
|
||||
await Promise.race([
|
||||
this.performLoad(src, id),
|
||||
loadTimeout > 0 ? this.createTimeout(loadTimeout) : Promise.resolve(),
|
||||
])
|
||||
} catch (err) {
|
||||
if (id !== this.loadSeq) return
|
||||
|
||||
this.loadSeq++
|
||||
this.error = { code: 4, message: err.message }
|
||||
this.networkState = 3 // NETWORK_NO_SOURCE
|
||||
this.events.emit('error')
|
||||
}
|
||||
}
|
||||
|
||||
async performLoad(src, id) {
|
||||
let videoMetadataLoaded = false
|
||||
let audioMetadataLoaded = false
|
||||
|
||||
const checkMetadata = () => {
|
||||
if (videoMetadataLoaded && audioMetadataLoaded) {
|
||||
this.readyState = 1 // HAVE_METADATA
|
||||
this.events.emit('loadedmetadata')
|
||||
this.events.emit('durationchange')
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all([
|
||||
this.video.load(src, () => {
|
||||
if (id !== this.loadSeq) return
|
||||
videoMetadataLoaded = true
|
||||
checkMetadata()
|
||||
}),
|
||||
this.audio.load(src, () => {
|
||||
if (id !== this.loadSeq) return
|
||||
audioMetadataLoaded = true
|
||||
checkMetadata()
|
||||
}),
|
||||
])
|
||||
|
||||
if (id !== this.loadSeq) return
|
||||
|
||||
this.readyState = 4 // HAVE_ENOUGH_DATA
|
||||
this.networkState = 1 // NETWORK_IDLE
|
||||
this.events.emit('loadeddata')
|
||||
this.events.emit('canplay')
|
||||
this.events.emit('canplaythrough')
|
||||
} catch (err) {
|
||||
if (id !== this.loadSeq) return
|
||||
|
||||
this.error = { code: 4, message: err.message }
|
||||
this.networkState = 3
|
||||
this.events.emit('error')
|
||||
console.error('MediaBunny load error:', err)
|
||||
}
|
||||
}
|
||||
|
||||
createTimeout(ms) {
|
||||
return new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error('Load timeout')), ms)
|
||||
})
|
||||
}
|
||||
|
||||
async play() {
|
||||
if (!this.paused) return
|
||||
|
||||
if (this.ended) {
|
||||
this.ended = false
|
||||
await this.seek(0)
|
||||
}
|
||||
|
||||
this.paused = false
|
||||
|
||||
await this.audio.play()
|
||||
this.video.start(this.audio)
|
||||
|
||||
this.events.emit('play')
|
||||
this.events.emit('playing')
|
||||
}
|
||||
|
||||
pause() {
|
||||
if (this.paused) return
|
||||
|
||||
this.paused = true
|
||||
|
||||
this.audio.pause()
|
||||
this.video.stop()
|
||||
|
||||
this.events.emit('pause')
|
||||
}
|
||||
|
||||
async seek(time) {
|
||||
const shouldResume = !this.paused
|
||||
|
||||
this.ended = false
|
||||
this.seeking = true
|
||||
|
||||
this.events.emit('seeking')
|
||||
this.events.emit('waiting')
|
||||
|
||||
this.pause()
|
||||
|
||||
await Promise.all([
|
||||
this.audio.seek(time),
|
||||
this.video.seek(time),
|
||||
])
|
||||
|
||||
this.seeking = false
|
||||
this.events.emit('seeked')
|
||||
|
||||
if (shouldResume && !this.ended) {
|
||||
await this.play()
|
||||
}
|
||||
}
|
||||
|
||||
setVolume(volume, muted) {
|
||||
this.audio.setVolume(volume, muted)
|
||||
}
|
||||
|
||||
setPlaybackRate(rate) {
|
||||
this.audio.setPlaybackRate(rate)
|
||||
this.video.setPlaybackRate(rate)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.pause()
|
||||
this.audio.destroy()
|
||||
this.video.destroy()
|
||||
}
|
||||
|
||||
// Getters
|
||||
get currentTime() {
|
||||
return this.audio.currentTime
|
||||
}
|
||||
|
||||
get duration() {
|
||||
return this.audio.duration || this.video.duration
|
||||
}
|
||||
|
||||
get videoWidth() {
|
||||
return this.video.width
|
||||
}
|
||||
|
||||
get videoHeight() {
|
||||
return this.video.height
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user