UNPKG

@aidenlx/player

Version:

Headless web components that make integrating media on the a web a breeze.

494 lines (493 loc) 19.6 kB
import "../../chunks/chunk.LNH2V2XS.js"; import { appendTriggerEvent, debounce, DisposalBin, eventListener, isNil, keysOf, RequestQueue, setAttribute, throttle, vdsEvent } from "@vidstack/foundation"; import { mediaStoreContext } from "../store"; import { MediaIdleController } from "./MediaIdleController"; class MediaController { constructor(_host) { this._host = _host; this._disconnectDisposal = new DisposalBin(); this._mediaProviderConnectedQueue = new RequestQueue(); this._mediaProviderDisconnectedDisposal = new DisposalBin(); this._logController = false ? new LogController(this._host) : void 0; this._logger = false ? new LogDispatcher(this._host) : void 0; this._handleMediaProviderConnect = eventListener(this._host, "vds-media-provider-connect", (event) => { if (event.detail.connected) return; const { element, onDisconnect } = event.detail; event.detail.connected = true; if (this.mediaProvider === element) return; this._handleMediaProviderDisconnect(); this._mediaProvider = element; this._flushMediaProviderConnectedQueue(); onDisconnect(this._handleMediaProviderDisconnect.bind(this)); }); this._mediaStoreProvider = mediaStoreContext.provide(this._host); this._mediaIdleController = new MediaIdleController(this._host, this._store); this._handleIdleChange = eventListener(this._host, "vds-idle-change", (event) => { this._store.idle.set(event.detail); this._satisfyMediaRequest("idle", event); }); this._pendingMediaRequests = { play: [], pause: [], volume: [], fullscreen: [], seeked: [], seeking: [], idle: [] }; this._handleCanLoad = eventListener(this._host, "vds-can-load", () => { this._store.canLoad.set(true); }); this._handleMuteRequest = eventListener(this._host, "vds-mute-request", this._createMediaRequestHandler("muted", (event) => { if (this._mediaProvider?.muted === true) return; this._pendingMediaRequests.volume.push(event); this.mediaProvider.muted = true; })); this._handleUnmuteRequest = eventListener(this._host, "vds-unmute-request", this._createMediaRequestHandler("muted", (event) => { if (this._mediaProvider?.muted === false) return; this._pendingMediaRequests.volume.push(event); this.mediaProvider.muted = false; })); this._handlePlayRequest = eventListener(this._host, "vds-play-request", this._createMediaRequestHandler("paused", (event) => { if (this._mediaProvider?.paused === false) return; this._pendingMediaRequests.play.push(event); this.mediaProvider.paused = false; })); this._handlePauseRequest = eventListener(this._host, "vds-pause-request", this._createMediaRequestHandler("paused", (event) => { if (this._mediaProvider?.paused === true) return; this._pendingMediaRequests.pause.push(event); this.mediaProvider.paused = true; })); this._isSeekingRequestPending = false; this._handleSeekingRequest = eventListener(this._host, "vds-seeking-request", this._createMediaRequestHandler("seeking", (event) => { this._stopWaiting(); this._pendingMediaRequests.seeking.push(event); this._isSeekingRequestPending = true; this._store.seeking.set(true); })); this._handleSeekRequest = eventListener(this._host, "vds-seek-request", this._createMediaRequestHandler("seeking", (event) => { if (this._mediaProvider?.ended) { this._isReplay = true; } this._pendingMediaRequests.seeked.push(event); this._isSeekingRequestPending = false; let time = event.detail; if (this.mediaProvider.duration - event.detail < 0.25) { time = this.mediaProvider.duration; } this.mediaProvider.currentTime = time; })); this._handleVolumeChangeRequest = eventListener(this._host, "vds-volume-change-request", this._createMediaRequestHandler("volume", (event) => { if (this._mediaProvider?.volume === event.detail) return; this._pendingMediaRequests.volume.push(event); this.mediaProvider.volume = event.detail; })); this._handleEnterFullscreenRequest = eventListener(this._host, "vds-enter-fullscreen-request", this._createMediaRequestHandler("fullscreen", async (event) => { if (this.mediaProvider?.fullscreen) return; this._pendingMediaRequests.fullscreen.push(event); await this._host.requestFullscreen(); })); this._handleExitFullscreenRequest = eventListener(this._host, "vds-exit-fullscreen-request", this._createMediaRequestHandler("fullscreen", async (event) => { if (!this.mediaProvider?.fullscreen) return; this._pendingMediaRequests.fullscreen.push(event); await this._host.exitFullscreen?.(); })); this._handleResumeIdlingRequest = eventListener(this._host, "vds-resume-idling-request", (event) => { if (!this._mediaRequestEventGateway(event)) return; this._pendingMediaRequests.idle.push(event); this._mediaIdleController.paused = false; }); this._handlePauseIdlingRequest = eventListener(this._host, "vds-pause-idling-request", (event) => { if (!this._mediaRequestEventGateway(event)) return; this._pendingMediaRequests.idle.push(event); this._mediaIdleController.paused = true; }); this._handleShowPosterRequest = eventListener(this._host, "vds-show-poster-request", this._createMediaRequestHandler("poster", () => { this._mediaProvider.__canLoadPoster = true; })); this._handleHidePosterRequest = eventListener(this._host, "vds-hide-poster-request", this._createMediaRequestHandler("poster", () => { this._mediaProvider.__canLoadPoster = false; })); this._handleLoopRequest = eventListener(this._host, "vds-loop-request", this._createMediaRequestHandler("loop", () => { window.requestAnimationFrame(async () => { try { this._isLooping = true; this._isReplay = true; await this._mediaProvider.play(); } catch (e) { this._isReplay = false; this._isLooping = false; } }); })); this._handleFullscreenChange = eventListener(this._host, "vds-fullscreen-change", (event) => { this._store.fullscreen.set(event.detail); this._satisfyMediaRequest("fullscreen", event); }); this._handleFullscreenError = eventListener(this._host, "vds-fullscreen-error", (event) => { this._satisfyMediaRequest("fullscreen", event); }); this._isReplay = false; this._isLooping = false; this._firingWaiting = false; this._mediaEvents = []; this._handleLoadStart = eventListener(this._host, "vds-load-start", (event) => { this._mediaEvents.push(event); appendTriggerEvent(event, this._findLastMediaEvent("vds-src-change")); this._store.currentSrc.set(event.detail.src); this._store.currentPoster.set(event.detail.poster); this._store.mediaType.set(event.detail.mediaType); this._store.viewType.set(event.detail.viewType); }); this._handleLoadedData = eventListener(this._host, "vds-loaded-data", (event) => { this._mediaEvents.push(event); appendTriggerEvent(event, this._findLastMediaEvent("vds-load-start")); }); this._handleLoadedMetadata = eventListener(this._host, "vds-loaded-metadata", (event) => { this._mediaEvents.push(event); appendTriggerEvent(event, this._findLastMediaEvent("vds-load-start")); }); this._handleCanPlay = eventListener(this._host, "vds-can-play", (event) => { this._mediaEvents.push(event); if (event.triggerEvent?.type !== "loadedmetadata") { appendTriggerEvent(event, this._findLastMediaEvent("vds-loaded-metadata")); } this._store.canPlay.set(true); this._store.duration.set(event.detail.duration); }); this._handleCanPlayThrough = eventListener(this._host, "vds-can-play-through", (event) => { appendTriggerEvent(event, this._findLastMediaEvent("vds-can-play")); }); this._handleAutoplay = eventListener(this._host, "vds-autoplay", (event) => { this._mediaEvents.push(event); appendTriggerEvent(event, this._findLastMediaEvent("vds-play")); appendTriggerEvent(event, this._findLastMediaEvent("vds-can-play")); this._store.autoplayError.set(void 0); }); this._handleAutoplayFail = eventListener(this._host, "vds-autoplay-fail", (event) => { appendTriggerEvent(event, this._findLastMediaEvent("vds-play-fail")); appendTriggerEvent(event, this._findLastMediaEvent("vds-can-play")); this._store.autoplayError.set(event.detail); this._clearMediaStateTracking(); }); this._handlePlay = eventListener(this._host, "vds-play", (event) => { if (this._isLooping || !this._mediaProvider?.paused) { event.stopImmediatePropagation(); return; } this._mediaEvents.push(event); appendTriggerEvent(event, this._findLastMediaEvent("vds-waiting")); this._satisfyMediaRequest("play", event); this._store.paused.set(false); this._store.autoplayError.set(void 0); this._setProviderAttr("paused", false); if (this._mediaProvider?.ended || this._isReplay) { this._isReplay = false; this._store.ended.set(false); const replayEvent = vdsEvent("vds-replay", { triggerEvent: event }); this._mediaProvider?.dispatchEvent(replayEvent); } }); this._handlePlayFail = eventListener(this._host, "vds-play-fail", (event) => { this._mediaEvents.push(event); this._stopWaiting(); appendTriggerEvent(event, this._findLastMediaEvent("vds-play")); this._store.paused.set(true); this._store.playing.set(false); this._satisfyMediaRequest("play", event); this._clearMediaStateTracking(); }); this._handlePlaying = eventListener(this._host, "vds-playing", (event) => { this._mediaEvents.push(event); const playEvent = this._findLastMediaEvent("vds-play"); if (playEvent) { appendTriggerEvent(event, this._findLastMediaEvent("vds-waiting")); appendTriggerEvent(event, playEvent); } else { appendTriggerEvent(event, this._findLastMediaEvent("vds-seeked")); } this._stopWaiting(); this._clearMediaStateTracking(); this._store.paused.set(false); this._store.playing.set(true); this._store.seeking.set(false); this._store.ended.set(false); if (this._isLooping) { event.stopImmediatePropagation(); this._isLooping = false; return; } if (!this._mediaProvider?.started) { this._store.started.set(true); this._mediaProvider?.dispatchEvent(vdsEvent("vds-started", { triggerEvent: event })); } }); this._handlePause = eventListener(this._host, "vds-pause", (event) => { if (this._isLooping) { event.stopImmediatePropagation(); return; } appendTriggerEvent(event, this._findLastMediaEvent("vds-seeked")); this._satisfyMediaRequest("pause", event); this._store.paused.set(true); this._store.playing.set(false); this._store.seeking.set(false); this._setProviderAttr("paused", true); this._stopWaiting(); this._clearMediaStateTracking(); }); this._handleTimeUpdate = eventListener(this._host, "vds-time-update", (event) => { this._store.currentTime.set(event.detail); this._store.waiting.set(false); }); this._handleVolumeChange = eventListener(this._host, "vds-volume-change", (event) => { this._store.volume.set(event.detail.volume); this._store.muted.set(event.detail.muted); this._satisfyMediaRequest("volume", event); this._setProviderAttr("muted", event.detail.muted); }); this._handleSeeking = eventListener(this._host, "vds-seeking", throttle((event) => { this._mediaEvents.push(event); this._store.seeking.set(true); this._store.currentTime.set(event.detail); this._satisfyMediaRequest("seeking", event); }, 150, { leading: true })); this._handleSeeked = eventListener(this._host, "vds-seeked", (event) => { if (this._isSeekingRequestPending) { this._store.seeking.set(true); event.stopImmediatePropagation(); } else if (this._mediaProvider?.seeking) { this._mediaEvents.push(event); appendTriggerEvent(event, this._findLastMediaEvent("vds-waiting")); appendTriggerEvent(event, this._findLastMediaEvent("vds-seeking")); if (this._mediaProvider.paused) { this._stopWaiting(); } this._store.seeking.set(false); if (event.detail !== this._mediaProvider.duration) { this._store.ended.set(false); } this._store.currentTime.set(event.detail); this._satisfyMediaRequest("seeked", event); } }); this._fireWaiting = debounce(() => { if (!this._originalWaitingEvent) return; this._firingWaiting = true; const event = vdsEvent("vds-waiting", this._originalWaitingEvent); this._mediaEvents.push(event); this._store.waiting.set(true); this._store.playing.set(false); this._mediaProvider?.dispatchEvent(event); this._originalWaitingEvent = void 0; this._firingWaiting = false; }, 300); this._handleWaiting = eventListener(this._host, "vds-waiting", (event) => { if (this._firingWaiting) return; event.stopImmediatePropagation(); this._originalWaitingEvent = event; this._fireWaiting(); }); this._handleEnded = eventListener(this._host, "vds-ended", (event) => { if (this._isLooping) { event.stopImmediatePropagation(); return; } this._stopWaiting(); this._store.paused.set(true); this._store.playing.set(false); this._store.seeking.set(false); this._store.ended.set(true); this._clearMediaStateTracking(); }); this._handleAutoplayChange = eventListener(this._host, "vds-autoplay-change", (event) => { this._store.autoplay.set(event.detail); }); this._handleError = eventListener(this._host, "vds-error", (event) => { this._store.error.set(event.detail); }); this._handleFullscreenSupportChange = eventListener(this._host, "vds-fullscreen-support-change", (event) => { this._store.canFullscreen.set(event.detail); }); this._handlePosterChange = eventListener(this._host, "vds-poster-change", (event) => { this._store.currentPoster.set(event.detail); this._setProviderAttr("poster", event.detail); }); this._handleLoopChange = eventListener(this._host, "vds-loop-change", (event) => { this._store.loop.set(event.detail); }); this._handlePlaysinlineChange = eventListener(this._host, "vds-playsinline-change", (event) => { this._store.playsinline.set(event.detail); }); this._handleControlsChange = eventListener(this._host, "vds-controls-change", (event) => { this._store.controls.set(event.detail); }); this._handleMediaTypeChange = eventListener(this._host, "vds-media-type-change", (event) => { this._store.mediaType.set(event.detail); }); this._handleDurationChange = eventListener(this._host, "vds-duration-change", (event) => { this._store.duration.set(event.detail); }); this._handleProgress = eventListener(this._host, "vds-progress", (event) => { const { buffered, seekable } = event.detail; const bufferedAmount = buffered.length === 0 ? 0 : buffered.end(buffered.length - 1); const seekableAmount = seekable.length === 0 ? 0 : seekable.end(seekable.length - 1); this._store.buffered.set(buffered); this._store.bufferedAmount.set(bufferedAmount); this._store.seekable.set(seekable); this._store.seekableAmount.set(seekableAmount); }); this._handleSrcChange = eventListener(this._host, "vds-src-change", (event) => { this._clearMediaStateTracking(); this._mediaEvents.push(event); this._store.currentSrc.set(event.detail); this._setProviderAttr("src", event.detail); const dontReset = /* @__PURE__ */ new Set([ "currentSrc", "autoplay", "canFullscreen", "controls", "canLoad", "loop", "muted", "playsinline", "viewType", "volume" ]); const store = this._store; keysOf(store).forEach((prop) => { if (!dontReset.has(prop)) { store[prop].set(store[prop].initialValue); } }); }); _host.addController({ hostDisconnected: () => { this._clearMediaStateTracking(); this._clearPendingMediaRequests(); this._mediaProviderConnectedQueue.destroy(); this._mediaProviderDisconnectedDisposal.empty(); this._disconnectDisposal.empty(); } }); } get logLevel() { return this._logController?.logLevel ?? "silent"; } set logLevel(level) { if (false) { this._logController.logLevel = level; } } get mediaProvider() { return this._mediaProvider; } _handleMediaProviderDisconnect() { if (isNil(this.mediaProvider)) return; this._mediaProviderConnectedQueue.destroy(); this._mediaProviderDisconnectedDisposal.empty(); this._mediaProvider = void 0; this._store.canLoad.set(false); } _flushMediaProviderConnectedQueue() { this._mediaProviderConnectedQueue.start(); this._mediaProviderDisconnectedDisposal.add(() => { this._mediaProviderConnectedQueue.stop(); }); } _setProviderAttr(name, value) { this._mediaProviderConnectedQueue.queue(`attr:${name}`, () => { if (this.mediaProvider) { setAttribute(this._mediaProvider, name, value); } }); } get store() { return this._mediaStoreProvider.value; } get _store() { return this._mediaStoreProvider.value; } get idleDelay() { return this._mediaIdleController.delay; } set idleDelay(delay) { this._mediaIdleController.delay = delay; } _clearPendingMediaRequests() { keysOf(this._pendingMediaRequests).forEach((key) => { this._pendingMediaRequests[key] = []; }); } _satisfyMediaRequest(type, event) { const requestEvent = this._pendingMediaRequests[type].shift(); if (requestEvent) { event.requestEvent = requestEvent; appendTriggerEvent(event, requestEvent); } } _mediaRequestEventGateway(event) { event.stopPropagation(); if (false) { this._logger?.infoGroup(`\u{1F4EC} received \`${event.type}\``).labelledLog("Request", event).dispatch(); } return true; } _createMediaRequestHandler(queueKey, callback) { return async (event) => { if (!this._mediaRequestEventGateway(event)) return; if (this._mediaProvider) { await callback(event); return; } this._mediaProviderConnectedQueue.queue(queueKey, async () => { await callback(event); }); }; } _clearMediaStateTracking() { this._isReplay = false; this._isLooping = false; this._firingWaiting = false; this._originalWaitingEvent = void 0; this._mediaEvents = []; } _findLastMediaEvent(eventType) { return this._mediaEvents[this._mediaEvents.map((e) => e.type).lastIndexOf(eventType)]; } _stopWaiting() { this._fireWaiting.cancel(); this._store.waiting.set(false); } } export { MediaController };