@aidenlx/player
Version:
Headless web components that make integrating media on the a web a breeze.
494 lines (493 loc) • 19.6 kB
JavaScript
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
};