UNPKG

@ktt45678/vidstack

Version:

UI component library for building high-quality, accessible video and audio experiences on the web.

278 lines (274 loc) 7.74 kB
import { createScope, signal, effect, isString, deferredPromise, isUndefined, isObject, isNumber, isBoolean } from '../chunks/vidstack-C6myozhB.js'; import { TimeRange } from '../chunks/vidstack-Dy-iOvF5.js'; import { preconnect } from '../chunks/vidstack-CVbXna2m.js'; import { EmbedProvider } from '../chunks/vidstack-B8NrcVDt.js'; import { resolveYouTubeVideoId } from '../chunks/vidstack-Zc3I7oOd.js'; import '../chunks/vidstack-CTW_LGt6.js'; const YouTubePlayerState = { Om: -1, pg: 0, qg: 1, gj: 2, hj: 3, ij: 5 }; class YouTubeProvider extends EmbedProvider { constructor(iframe, _ctx) { super(iframe); this.b = _ctx; this.$$PROVIDER_TYPE = "YOUTUBE"; this.scope = createScope(); this.ha = signal(""); this.za = -1; this.K = null; this.md = -1; this.vc = false; this.Rn = /* @__PURE__ */ new Map(); this.language = "en"; this.color = "red"; this.cookies = false; } get c() { return this.b.delegate.c; } get currentSrc() { return this.K; } get type() { return "youtube"; } get videoId() { return this.ha(); } preconnect() { preconnect(this.Nb()); } setup() { super.setup(); effect(this.we.bind(this)); this.c("provider-setup", this); } destroy() { this.z(); const message = "provider destroyed"; for (const promises of this.Rn.values()) { for (const { reject } of promises) reject(message); } this.Rn.clear(); } async play() { return this.t("playVideo"); } Hn(message) { this.Sn("playVideo")?.reject(message); } async pause() { return this.t("pauseVideo"); } In(message) { this.Sn("pauseVideo")?.reject(message); } setMuted(muted) { if (muted) this.t("mute"); else this.t("unMute"); } setCurrentTime(time) { this.vc = this.b.$state.paused(); this.t("seekTo", time); this.c("seeking", time); } setVolume(volume) { this.t("setVolume", volume * 100); } setPlaybackRate(rate) { this.t("setPlaybackRate", rate); } async loadSource(src) { if (!isString(src.src)) { this.K = null; this.ha.set(""); return; } const videoId = resolveYouTubeVideoId(src.src); this.ha.set(videoId ?? ""); this.K = src; } Nb() { return !this.cookies ? "https://www.youtube-nocookie.com" : "https://www.youtube.com"; } we() { this.z(); const videoId = this.ha(); if (!videoId) { this.sc.set(""); return; } this.sc.set(`${this.Nb()}/embed/${videoId}`); this.c("load-start"); } mg() { const { keyDisabled } = this.b.$props, { muted, playsInline, nativeControls } = this.b.$state, showControls = nativeControls(); return { autoplay: 0, cc_lang_pref: this.language, cc_load_policy: showControls ? 1 : void 0, color: this.color, controls: showControls ? 1 : 0, disablekb: !showControls || keyDisabled() ? 1 : 0, enablejsapi: 1, fs: 1, hl: this.language, iv_load_policy: showControls ? 1 : 3, mute: muted() ? 1 : 0, playsinline: playsInline() ? 1 : 0 }; } t(command, arg) { let promise = deferredPromise(), promises = this.Rn.get(command); if (!promises) this.Rn.set(command, promises = []); promises.push(promise); this.se({ event: "command", func: command, args: arg ? [arg] : void 0 }); return promise.promise; } gd() { window.setTimeout(() => this.se({ event: "listening" }), 100); } kd(trigger) { this.c("loaded-metadata"); this.c("loaded-data"); this.b.delegate.Ga(void 0, trigger); } ib(trigger) { this.Sn("pauseVideo")?.resolve(); this.c("pause", void 0, trigger); } mc(time, trigger) { const { duration, realCurrentTime } = this.b.$state, hasEnded = this.za === YouTubePlayerState.pg, boundTime = hasEnded ? duration() : time; this.c("time-change", boundTime, trigger); if (!hasEnded && Math.abs(boundTime - realCurrentTime()) > 1) { this.c("seeking", boundTime, trigger); } } nb(buffered, seekable, trigger) { const detail = { buffered: new TimeRange(0, buffered), seekable }; this.c("progress", detail, trigger); const { seeking, realCurrentTime } = this.b.$state; if (seeking() && buffered > realCurrentTime()) { this.ob(trigger); } } ob(trigger) { const { paused, realCurrentTime } = this.b.$state; window.clearTimeout(this.md); this.md = window.setTimeout( () => { this.c("seeked", realCurrentTime(), trigger); this.md = -1; }, paused() ? 100 : 0 ); this.vc = false; } lc(trigger) { const { seeking } = this.b.$state; if (seeking()) this.ob(trigger); this.c("pause", void 0, trigger); this.c("end", void 0, trigger); } ie(state, trigger) { const { started, paused, seeking } = this.b.$state, isPlaying = state === YouTubePlayerState.qg, isBuffering = state === YouTubePlayerState.hj, isPendingPlay = !isUndefined(this.Sn("playVideo")), isPlay = (paused() || isPendingPlay) && (isBuffering || isPlaying); if (isBuffering) this.c("waiting", void 0, trigger); if (seeking() && isPlaying) { this.ob(trigger); } if (!started() && isPlay && this.vc) { this.Hn("invalid internal play operation"); if (isPlaying) { this.pause(); this.vc = false; } return; } if (isPlay) { this.Sn("playVideo")?.resolve(); this.c("play", void 0, trigger); } switch (state) { case YouTubePlayerState.Om: this.Hn("provider rejected"); this.In("provider rejected"); this.c("pause", void 0, trigger); break; case YouTubePlayerState.ij: this.kd(trigger); break; case YouTubePlayerState.qg: this.c("playing", void 0, trigger); break; case YouTubePlayerState.gj: this.ib(trigger); break; case YouTubePlayerState.pg: this.lc(trigger); break; } this.za = state; } te({ info }, event) { if (!info) return; const { title, intrinsicDuration, playbackRate } = this.b.$state; if (isObject(info.videoData) && info.videoData.title !== title()) { this.c("title-change", info.videoData.title, event); } if (isNumber(info.duration) && info.duration !== intrinsicDuration()) { if (isNumber(info.videoLoadedFraction)) { const buffered = info.progressState?.loaded ?? info.videoLoadedFraction * info.duration, seekable = new TimeRange(0, info.duration); this.nb(buffered, seekable, event); } this.c("duration-change", info.duration, event); } if (isNumber(info.playbackRate) && info.playbackRate !== playbackRate()) { this.c("rate-change", info.playbackRate, event); } if (info.progressState) { const { current, seekableStart, seekableEnd, loaded, duration: _duration } = info.progressState; this.mc(current, event); this.nb(loaded, new TimeRange(seekableStart, seekableEnd), event); if (_duration !== intrinsicDuration()) { this.c("duration-change", _duration, event); } } if (isNumber(info.volume) && isBoolean(info.muted)) { const detail = { muted: info.muted, volume: info.volume / 100 }; this.c("volume-change", detail, event); } if (isNumber(info.playerState) && info.playerState !== this.za) { this.ie(info.playerState, event); } } z() { this.za = -1; this.md = -1; this.vc = false; } Sn(command) { return this.Rn.get(command)?.shift(); } } export { YouTubeProvider };